eve 0.10.0 → 0.11.1
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 +17 -0
- package/README.md +14 -0
- package/dist/src/cli/banner.d.ts +6 -3
- package/dist/src/cli/banner.js +1 -1
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/dev/tui/agent-header.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/evals/runner/run-evals.d.ts +1 -1
- package/dist/src/execution/sandbox/bindings/docker.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.d.ts +2 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-errors.d.ts +1 -0
- package/dist/src/execution/sandbox/bindings/vercel-errors.js +1 -2
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +0 -5
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/harness/input-requests.d.ts +20 -1
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/protocol/message.d.ts +11 -2
- package/dist/src/protocol/message.js +2 -2
- package/dist/src/public/next/index.d.ts +1 -7
- package/dist/src/public/next/index.js +1 -1
- package/dist/src/public/next/vercel-output-config.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
- package/dist/src/setup/scaffold/create/web-template.js +27 -4
- package/dist/src/setup/scaffold/update/channels.js +1 -1
- package/docs/README.md +19 -5
- package/docs/agent-config.md +16 -19
- package/docs/channels/custom.mdx +24 -18
- package/docs/channels/discord.mdx +25 -13
- package/docs/channels/eve.mdx +24 -6
- package/docs/channels/github.mdx +41 -10
- package/docs/channels/linear.mdx +29 -36
- package/docs/channels/overview.mdx +16 -4
- package/docs/channels/slack.mdx +59 -32
- package/docs/channels/teams.mdx +19 -7
- package/docs/channels/telegram.mdx +23 -9
- package/docs/channels/twilio.mdx +28 -6
- package/docs/concepts/context-control.md +24 -41
- package/docs/concepts/default-harness.md +19 -9
- package/docs/concepts/execution-model-and-durability.md +14 -14
- package/docs/concepts/security-model.md +32 -8
- package/docs/concepts/sessions-runs-and-streaming.md +11 -9
- package/docs/connections.mdx +49 -29
- package/docs/evals/assertions.mdx +30 -29
- package/docs/evals/cases.mdx +10 -10
- package/docs/evals/judge.mdx +14 -14
- package/docs/evals/overview.mdx +15 -17
- package/docs/evals/reporters.mdx +19 -7
- package/docs/evals/running.mdx +2 -2
- package/docs/evals/targets.mdx +15 -4
- package/docs/getting-started.mdx +72 -31
- package/docs/guides/auth-and-route-protection.md +18 -12
- package/docs/guides/client/continuations.mdx +5 -13
- package/docs/guides/client/messages.mdx +2 -2
- package/docs/guides/client/output-schema.mdx +2 -2
- package/docs/guides/client/overview.mdx +4 -4
- package/docs/guides/client/streaming.mdx +6 -5
- package/docs/guides/deployment.md +22 -20
- package/docs/guides/dev-tui.md +85 -13
- package/docs/guides/dynamic-capabilities.md +24 -8
- package/docs/guides/dynamic-workflows.md +33 -12
- package/docs/guides/frontend/nextjs.mdx +28 -14
- package/docs/guides/frontend/nuxt.mdx +21 -4
- package/docs/guides/frontend/overview.mdx +11 -11
- package/docs/guides/frontend/sveltekit.mdx +20 -3
- package/docs/guides/frontend/use-eve-agent-svelte.mdx +106 -14
- package/docs/guides/frontend/use-eve-agent-vue.mdx +108 -15
- package/docs/guides/hooks.md +14 -60
- package/docs/guides/instrumentation.md +24 -18
- package/docs/guides/remote-agents.md +19 -15
- package/docs/guides/session-context.md +12 -12
- package/docs/guides/state.md +25 -9
- package/docs/instructions.mdx +18 -10
- package/docs/{introduction.md → introduction.mdx} +14 -12
- package/docs/reference/cli.md +99 -53
- package/docs/reference/meta.json +1 -1
- package/docs/reference/project-layout.md +39 -32
- package/docs/reference/typescript-api.md +1 -1
- package/docs/responsible-use.md +14 -0
- package/docs/sandbox.mdx +25 -19
- package/docs/schedules.mdx +16 -14
- package/docs/skills.mdx +8 -6
- package/docs/subagents.mdx +39 -13
- package/docs/tools.mdx +13 -9
- package/docs/tutorial/connect-a-warehouse.mdx +16 -7
- package/docs/tutorial/first-agent.mdx +21 -11
- package/docs/tutorial/guard-the-spend.mdx +11 -9
- package/docs/tutorial/how-it-runs.mdx +10 -8
- package/docs/tutorial/query-sample-data.mdx +8 -8
- package/docs/tutorial/remember-definitions.mdx +5 -5
- package/docs/tutorial/run-analysis.mdx +13 -11
- package/docs/tutorial/ship-it.mdx +70 -14
- package/docs/tutorial/team-playbooks.mdx +33 -10
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ export declare const EVE_STREAM_FORMAT_HEADER = "x-eve-stream-format";
|
|
|
8
8
|
export declare const EVE_STREAM_VERSION_HEADER = "x-eve-stream-version";
|
|
9
9
|
export declare const EVE_MESSAGE_STREAM_CONTENT_TYPE = "application/x-ndjson; charset=utf-8";
|
|
10
10
|
export declare const EVE_MESSAGE_STREAM_FORMAT = "ndjson";
|
|
11
|
-
export declare const EVE_MESSAGE_STREAM_VERSION = "
|
|
11
|
+
export declare const EVE_MESSAGE_STREAM_VERSION = "16";
|
|
12
12
|
/**
|
|
13
13
|
* Eve-owned finish reason for one completed assistant step.
|
|
14
14
|
*
|
|
@@ -28,8 +28,12 @@ export interface HandleMessageStreamEventMeta {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Normalized completion status for one emitted runtime action result.
|
|
31
|
+
*
|
|
32
|
+
* `rejected` marks a tool call the user (or a policy) denied at a HITL
|
|
33
|
+
* approval gate: it never executed, so it is neither a success nor a
|
|
34
|
+
* runtime failure.
|
|
31
35
|
*/
|
|
32
|
-
export type ActionResultStatus = "completed" | "failed";
|
|
36
|
+
export type ActionResultStatus = "completed" | "failed" | "rejected";
|
|
33
37
|
/**
|
|
34
38
|
* Stable failure payload projected onto `action.result`.
|
|
35
39
|
*
|
|
@@ -573,8 +577,13 @@ export declare function createInputRequestedEvent(input: {
|
|
|
573
577
|
}): InputRequestedStreamEvent;
|
|
574
578
|
/**
|
|
575
579
|
* Creates the `action.result` event for one runtime action result.
|
|
580
|
+
*
|
|
581
|
+
* Pass `rejected: true` for a tool call denied at a HITL approval gate. The
|
|
582
|
+
* call never executed, so the outcome is forced to `rejected` rather than
|
|
583
|
+
* derived from the synthesized denial output.
|
|
576
584
|
*/
|
|
577
585
|
export declare function createActionResultEvent(input: {
|
|
586
|
+
readonly rejected?: boolean;
|
|
578
587
|
readonly result: RuntimeActionResult;
|
|
579
588
|
readonly sequence: number;
|
|
580
589
|
readonly stepIndex: number;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const EVE_SESSION_ID_HEADER=`x-eve-session-id`,EVE_STREAM_FORMAT_HEADER=`x-eve-stream-format`,EVE_STREAM_VERSION_HEADER=`x-eve-stream-version`,EVE_MESSAGE_STREAM_CONTENT_TYPE=`application/x-ndjson; charset=utf-8`,EVE_MESSAGE_STREAM_FORMAT=`ndjson`,EVE_MESSAGE_STREAM_VERSION=`
|
|
2
|
-
`)}function createActionsRequestedEvent(e){return{data:{actions:e.actions,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`actions.requested`}}function createAuthorizationRequiredEvent(e){let t={description:e.description,name:e.name,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.authorization!==void 0&&(t.authorization=e.authorization),e.webhookUrl!==void 0&&(t.webhookUrl=e.webhookUrl),{data:t,type:`authorization.required`}}function createAuthorizationCompletedEvent(e){let t={name:e.name,outcome:e.outcome,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.authorization!==void 0&&(t.authorization=e.authorization),e.reason!==void 0&&(t.reason=e.reason),{data:t,type:`authorization.completed`}}function createInputRequestedEvent(e){return{data:{requests:e.requests,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`input.requested`}}function createActionResultEvent(e){let t=normalizeActionResultOutcome(e.result);return{data:{error:t.error,result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,status:t.status,turnId:e.turnId},type:`action.result`}}function createSubagentCalledEvent(e){return{data:{callId:e.callId,childSessionId:e.childSessionId,sessionId:e.sessionId,sequence:e.sequence,name:e.name,remote:e.remote,toolName:e.toolName,turnId:e.turnId,workflowId:e.workflowId},type:`subagent.called`}}function createMessageAppendedEvent(e){return{data:{messageDelta:e.messageDelta,messageSoFar:e.messageSoFar,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.appended`}}function createReasoningAppendedEvent(e){return{data:{reasoningDelta:e.reasoningDelta,reasoningSoFar:e.reasoningSoFar,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.appended`}}function createMessageCompletedEvent(e){return{data:{finishReason:e.finishReason??`stop`,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.completed`}}function createReasoningCompletedEvent(e){return{data:{reasoning:e.reasoning,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.completed`}}function createResultCompletedEvent(e){return{data:{result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`result.completed`}}function createStepStartedEvent(e){return{data:{sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.started`}}function createStepCompletedEvent(e){let t={finishReason:e.finishReason,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.usage!==void 0&&(t.usage=e.usage),{data:t,type:`step.completed`}}function createStepFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.failed`}}function createTurnCompletedEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.completed`}}function createTurnFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,turnId:e.turnId},type:`turn.failed`}}function createCompactionRequestedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,turnId:e.turnId,usageInputTokens:e.usageInputTokens??null},type:`compaction.requested`}}function createCompactionCompletedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,turnId:e.turnId},type:`compaction.completed`}}function createSessionWaitingEvent(){return{data:{wait:`next-user-message`},type:`session.waiting`}}function createSessionFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sessionId:e.sessionId},type:`session.failed`}}function createSessionCompletedEvent(){return{type:`session.completed`}}function timestampHandleMessageStreamEvent(e,t=new Date().toISOString()){return{...e,meta:{at:t}}}function encodeMessageStreamEvent(e){return textEncoder.encode(`${JSON.stringify(e)}\n`)}function normalizeActionResultOutcome(e){if(e.isError===!0)return{error:buildActionResultError(e),status:`failed`};let t=readActionResultOutputError(e.output);return t===void 0?{status:`completed`}:{error:t,status:`failed`}}function buildActionResultError(e){let t=readActionResultOutputError(e.output);return t===void 0?{code:`ACTION_RESULT_FAILED`,message:formatActionResultOutput(e.output)}:t}function readActionResultOutputError(e){let t=parseActionResultOutputRecord(e);if(t===void 0)return;let n=typeof t.code==`string`&&t.code.length>0?t.code:void 0,r=typeof t.message==`string`&&t.message.length>0?t.message:void 0;if(!(n===void 0||r===void 0))return{code:n,message:r}}function parseActionResultOutputRecord(e){if(typeof e==`object`&&e)return e;if(typeof e!=`string`)return;let t=e.trim();if(t.length!==0)try{let e=JSON.parse(t);if(typeof e==`object`&&e)return e}catch{return}}function formatActionResultOutput(e){if(typeof e==`string`)return e;let t=JSON.stringify(e);return typeof t==`string`&&t.length>0?t:`Action failed.`}export{EVE_MESSAGE_STREAM_CONTENT_TYPE,EVE_MESSAGE_STREAM_FORMAT,EVE_MESSAGE_STREAM_VERSION,EVE_SESSION_ID_HEADER,EVE_STREAM_FORMAT_HEADER,EVE_STREAM_VERSION_HEADER,createActionResultEvent,createActionsRequestedEvent,createAuthorizationCompletedEvent,createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createMessageAppendedEvent,createMessageCompletedEvent,createMessageReceivedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent,createResultCompletedEvent,createSessionCompletedEvent,createSessionFailedEvent,createSessionStartedEvent,createSessionWaitingEvent,createStepCompletedEvent,createStepFailedEvent,createStepStartedEvent,createSubagentCalledEvent,createTurnCompletedEvent,createTurnFailedEvent,createTurnStartedEvent,encodeMessageStreamEvent,isCurrentTurnBoundaryEvent,isTurnFailureEvent,timestampHandleMessageStreamEvent};
|
|
1
|
+
const EVE_SESSION_ID_HEADER=`x-eve-session-id`,EVE_STREAM_FORMAT_HEADER=`x-eve-stream-format`,EVE_STREAM_VERSION_HEADER=`x-eve-stream-version`,EVE_MESSAGE_STREAM_CONTENT_TYPE=`application/x-ndjson; charset=utf-8`,EVE_MESSAGE_STREAM_FORMAT=`ndjson`,EVE_MESSAGE_STREAM_VERSION=`16`,textEncoder=new TextEncoder;function isCurrentTurnBoundaryEvent(e){return e.type===`session.completed`||e.type===`session.failed`||e.type===`session.waiting`}function isTurnFailureEvent(e){return e.type===`session.failed`||e.type===`step.failed`||e.type===`turn.failed`}function createSessionStartedEvent(e){let t={};return e?.invocation!==void 0&&(t.invocation=e.invocation),e?.runtime!==void 0&&(t.runtime=e.runtime),{data:t,type:`session.started`}}function createTurnStartedEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.started`}}function createMessageReceivedEvent(e){return{data:{message:summarizeUserContent(e.message),sequence:e.sequence,turnId:e.turnId},type:`message.received`}}function summarizeUserContent(e){if(typeof e==`string`)return e;let t=[];for(let n of e)if(n.type===`text`)t.push(n.text);else if(n.type===`file`){let e=n.filename??n.mediaType;t.push(`[file: ${e} (${n.mediaType})]`)}else n.type===`image`&&t.push(`[image: ${n.mediaType??`image`}]`);return t.join(`
|
|
2
|
+
`)}function createActionsRequestedEvent(e){return{data:{actions:e.actions,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`actions.requested`}}function createAuthorizationRequiredEvent(e){let t={description:e.description,name:e.name,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.authorization!==void 0&&(t.authorization=e.authorization),e.webhookUrl!==void 0&&(t.webhookUrl=e.webhookUrl),{data:t,type:`authorization.required`}}function createAuthorizationCompletedEvent(e){let t={name:e.name,outcome:e.outcome,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.authorization!==void 0&&(t.authorization=e.authorization),e.reason!==void 0&&(t.reason=e.reason),{data:t,type:`authorization.completed`}}function createInputRequestedEvent(e){return{data:{requests:e.requests,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`input.requested`}}function createActionResultEvent(e){let t=e.rejected===!0?{error:buildActionResultError(e.result),status:`rejected`}:normalizeActionResultOutcome(e.result);return{data:{error:t.error,result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,status:t.status,turnId:e.turnId},type:`action.result`}}function createSubagentCalledEvent(e){return{data:{callId:e.callId,childSessionId:e.childSessionId,sessionId:e.sessionId,sequence:e.sequence,name:e.name,remote:e.remote,toolName:e.toolName,turnId:e.turnId,workflowId:e.workflowId},type:`subagent.called`}}function createMessageAppendedEvent(e){return{data:{messageDelta:e.messageDelta,messageSoFar:e.messageSoFar,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.appended`}}function createReasoningAppendedEvent(e){return{data:{reasoningDelta:e.reasoningDelta,reasoningSoFar:e.reasoningSoFar,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.appended`}}function createMessageCompletedEvent(e){return{data:{finishReason:e.finishReason??`stop`,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`message.completed`}}function createReasoningCompletedEvent(e){return{data:{reasoning:e.reasoning,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`reasoning.completed`}}function createResultCompletedEvent(e){return{data:{result:e.result,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`result.completed`}}function createStepStartedEvent(e){return{data:{sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.started`}}function createStepCompletedEvent(e){let t={finishReason:e.finishReason,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId};return e.usage!==void 0&&(t.usage=e.usage),{data:t,type:`step.completed`}}function createStepFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,stepIndex:e.stepIndex,turnId:e.turnId},type:`step.failed`}}function createTurnCompletedEvent(e){return{data:{sequence:e.sequence,turnId:e.turnId},type:`turn.completed`}}function createTurnFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sequence:e.sequence,turnId:e.turnId},type:`turn.failed`}}function createCompactionRequestedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,turnId:e.turnId,usageInputTokens:e.usageInputTokens??null},type:`compaction.requested`}}function createCompactionCompletedEvent(e){return{data:{modelId:e.modelId,sequence:e.sequence,sessionId:e.sessionId,turnId:e.turnId},type:`compaction.completed`}}function createSessionWaitingEvent(){return{data:{wait:`next-user-message`},type:`session.waiting`}}function createSessionFailedEvent(e){return{data:{code:e.code,details:e.details,message:e.message,sessionId:e.sessionId},type:`session.failed`}}function createSessionCompletedEvent(){return{type:`session.completed`}}function timestampHandleMessageStreamEvent(e,t=new Date().toISOString()){return{...e,meta:{at:t}}}function encodeMessageStreamEvent(e){return textEncoder.encode(`${JSON.stringify(e)}\n`)}function normalizeActionResultOutcome(e){if(e.isError===!0)return{error:buildActionResultError(e),status:`failed`};let t=readActionResultOutputError(e.output);return t===void 0?{status:`completed`}:{error:t,status:`failed`}}function buildActionResultError(e){let t=readActionResultOutputError(e.output);return t===void 0?{code:`ACTION_RESULT_FAILED`,message:formatActionResultOutput(e.output)}:t}function readActionResultOutputError(e){let t=parseActionResultOutputRecord(e);if(t===void 0)return;let n=typeof t.code==`string`&&t.code.length>0?t.code:void 0,r=typeof t.message==`string`&&t.message.length>0?t.message:void 0;if(!(n===void 0||r===void 0))return{code:n,message:r}}function parseActionResultOutputRecord(e){if(typeof e==`object`&&e)return e;if(typeof e!=`string`)return;let t=e.trim();if(t.length!==0)try{let e=JSON.parse(t);if(typeof e==`object`&&e)return e}catch{return}}function formatActionResultOutput(e){if(typeof e==`string`)return e;let t=JSON.stringify(e);return typeof t==`string`&&t.length>0?t:`Action failed.`}export{EVE_MESSAGE_STREAM_CONTENT_TYPE,EVE_MESSAGE_STREAM_FORMAT,EVE_MESSAGE_STREAM_VERSION,EVE_SESSION_ID_HEADER,EVE_STREAM_FORMAT_HEADER,EVE_STREAM_VERSION_HEADER,createActionResultEvent,createActionsRequestedEvent,createAuthorizationCompletedEvent,createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createMessageAppendedEvent,createMessageCompletedEvent,createMessageReceivedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent,createResultCompletedEvent,createSessionCompletedEvent,createSessionFailedEvent,createSessionStartedEvent,createSessionWaitingEvent,createStepCompletedEvent,createStepFailedEvent,createStepStartedEvent,createSubagentCalledEvent,createTurnCompletedEvent,createTurnFailedEvent,createTurnStartedEvent,encodeMessageStreamEvent,isCurrentTurnBoundaryEvent,isTurnFailureEvent,timestampHandleMessageStreamEvent};
|
|
@@ -62,12 +62,6 @@ export interface WithEveOptions {
|
|
|
62
62
|
* framework build, without changing the Next.js service build command.
|
|
63
63
|
*/
|
|
64
64
|
readonly eveBuildCommand?: string;
|
|
65
|
-
/**
|
|
66
|
-
* Set to `false` to skip creating or updating Vercel Build Output config.
|
|
67
|
-
* When unset, `withEve` ensures `.vercel/output/config.json` contains
|
|
68
|
-
* `experimentalServices` for the Next.js app and Eve app.
|
|
69
|
-
*/
|
|
70
|
-
readonly configureVercelOutput?: boolean;
|
|
71
65
|
/**
|
|
72
66
|
* Private Vercel service prefix for the Eve deployment. Defaults to
|
|
73
67
|
* {@link EVE_NEXT_SERVICE_PREFIX} (`/_eve_internal/eve`). `withEve` normalizes
|
|
@@ -83,7 +77,7 @@ export interface WithEveOptions {
|
|
|
83
77
|
*
|
|
84
78
|
* In development, starts `eve dev --no-ui --port 0` for the Eve app and
|
|
85
79
|
* rewrites Eve protocol endpoints to that local URL. In Vercel production,
|
|
86
|
-
* rewrites to the private Eve service prefix
|
|
80
|
+
* rewrites to the resolved private Eve service prefix.
|
|
87
81
|
* Outside Vercel production, serves an existing `.output/server/index.mjs` build
|
|
88
82
|
* on a stable local port when present; otherwise set `EVE_NEXT_PRODUCTION_ORIGIN`
|
|
89
83
|
* to the origin serving the Eve service namespace.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveEveDestinationPrefix}from"./server.js";import{ensureEveVercelOutputConfig}from"./vercel-output-config.js";import{EVE_ROUTE_PREFIX}from"#protocol/routes.js";import{isAbsolute,resolve}from"node:path";const EVE_NEXT_SERVICE_PREFIX=`/_eve_internal/eve`,EVE_NEXT_PRODUCTION_PORT_ENV=`EVE_NEXT_PRODUCTION_PORT`,EVE_PROXY_REWRITE_SOURCES=[`${EVE_ROUTE_PREFIX}/:path+`];function resolveApplicationRoot(e){return e===void 0||e.length===0?process.cwd():isAbsolute(e)?e:resolve(process.cwd(),e)}function resolveDevServerTimeout(e){if(e!==void 0){if(!Number.isFinite(e)||e<=0)throw Error(`Eve Next.js development server timeout must be a positive number.`);return e}}function normalizeRoutePrefix(e){let t=(e.startsWith(`/`)?e:`/${e}`).replace(/\/+$/,``);if(t.length===0)throw Error(`Eve Next.js service prefix cannot resolve to the root route.`);return t}function joinRoutePrefix(e,t){return`${e.replace(/\/+$/,``)}/${t.replace(/^\/+/,``)}`}function normalizeOrigin(e){return new URL(e.trim()).origin}function readLocalProductionPort(){let e=process.env[EVE_NEXT_PRODUCTION_PORT_ENV];if(e===void 0||e.trim().length===0)return 4274;let t=Number.parseInt(e,10);if(String(t)!==e.trim()||t<1||t>65535)throw Error(`${EVE_NEXT_PRODUCTION_PORT_ENV} must be an integer between 1 and 65535.`);return t}function resolveProductionDestination(e){if(process.env.VERCEL)return{destinationPrefix:e};let t=process.env.EVE_NEXT_PRODUCTION_ORIGIN;if(t!==void 0&&t.trim().length>0)return{destinationPrefix:joinRoutePrefix(normalizeOrigin(t),e)};let n=`http://127.0.0.1:${String(readLocalProductionPort())}`;return{destinationPrefix:n,localServerOrigin:n}}function createEveRewriteRules(e){return EVE_PROXY_REWRITE_SOURCES.map(t=>({destination:joinRoutePrefix(e,t),source:t}))}async function resolveExistingRewrites(e){return await e?.()}function mergeRewriteRules(e,t){return e===void 0?{beforeFiles:t}:isRewriteSections(e)?{...e,beforeFiles:[...t,...e.beforeFiles??[]]}:{afterFiles:e,beforeFiles:t}}function isRewriteSections(e){return!Array.isArray(e)}async function resolveNextConfig(e,t,n){return typeof e==`function`?await e(t,n):e}function withEve(n,r={}){let i=process.cwd(),a=resolveApplicationRoot(r.eveRoot),o=resolveDevServerTimeout(r.devServerTimeoutMs),s=normalizeRoutePrefix(r.servicePrefix??`/_eve_internal/eve`)
|
|
1
|
+
import{resolveEveDestinationPrefix}from"./server.js";import{ensureEveVercelOutputConfig}from"./vercel-output-config.js";import{EVE_ROUTE_PREFIX}from"#protocol/routes.js";import{isAbsolute,resolve}from"node:path";const EVE_NEXT_SERVICE_PREFIX=`/_eve_internal/eve`,EVE_NEXT_PRODUCTION_PORT_ENV=`EVE_NEXT_PRODUCTION_PORT`,EVE_PROXY_REWRITE_SOURCES=[`${EVE_ROUTE_PREFIX}/:path+`];function resolveApplicationRoot(e){return e===void 0||e.length===0?process.cwd():isAbsolute(e)?e:resolve(process.cwd(),e)}function resolveDevServerTimeout(e){if(e!==void 0){if(!Number.isFinite(e)||e<=0)throw Error(`Eve Next.js development server timeout must be a positive number.`);return e}}function normalizeRoutePrefix(e){let t=(e.startsWith(`/`)?e:`/${e}`).replace(/\/+$/,``);if(t.length===0)throw Error(`Eve Next.js service prefix cannot resolve to the root route.`);return t}function joinRoutePrefix(e,t){return`${e.replace(/\/+$/,``)}/${t.replace(/^\/+/,``)}`}function normalizeOrigin(e){return new URL(e.trim()).origin}function readLocalProductionPort(){let e=process.env[EVE_NEXT_PRODUCTION_PORT_ENV];if(e===void 0||e.trim().length===0)return 4274;let t=Number.parseInt(e,10);if(String(t)!==e.trim()||t<1||t>65535)throw Error(`${EVE_NEXT_PRODUCTION_PORT_ENV} must be an integer between 1 and 65535.`);return t}function resolveProductionDestination(e){if(process.env.VERCEL)return{destinationPrefix:e};let t=process.env.EVE_NEXT_PRODUCTION_ORIGIN;if(t!==void 0&&t.trim().length>0)return{destinationPrefix:joinRoutePrefix(normalizeOrigin(t),e)};let n=`http://127.0.0.1:${String(readLocalProductionPort())}`;return{destinationPrefix:n,localServerOrigin:n}}function createEveRewriteRules(e){return EVE_PROXY_REWRITE_SOURCES.map(t=>({destination:joinRoutePrefix(e,t),source:t}))}async function resolveExistingRewrites(e){return await e?.()}function mergeRewriteRules(e,t){return e===void 0?{beforeFiles:t}:isRewriteSections(e)?{...e,beforeFiles:[...t,...e.beforeFiles??[]]}:{afterFiles:e,beforeFiles:t}}function isRewriteSections(e){return!Array.isArray(e)}async function resolveNextConfig(e,t,n){return typeof e==`function`?await e(t,n):e}function withEve(n,r={}){let i=process.cwd(),a=resolveApplicationRoot(r.eveRoot),o=resolveDevServerTimeout(r.devServerTimeoutMs),s=normalizeRoutePrefix(r.servicePrefix??`/_eve_internal/eve`);return async function(c,l){let u=await resolveNextConfig(n,c,l),d=u.rewrites,f=resolveProductionDestination((await ensureEveVercelOutputConfig({appRoot:a,eveBuildCommand:r.eveBuildCommand??`eve build`,nextRoot:i,servicePrefix:s})).servicePrefix);return{...u,async rewrites(){let[t,n]=await Promise.all([resolveExistingRewrites(d),resolveEveDestinationPrefix({appRoot:a,devServerTimeoutMs:o,phase:c,productionDestinationPrefix:f.destinationPrefix,productionServerOrigin:f.localServerOrigin})]);return mergeRewriteRules(t,createEveRewriteRules(n))}}}}export{EVE_NEXT_SERVICE_PREFIX,withEve};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname,join,relative}from"node:path";import{mkdir,readFile,writeFile}from"node:fs/promises";import{findClosestLinkedVercelDirectory,findClosestVercelOutputDirectory}from"#shared/vercel-output-directory.js";const VERCEL_JSON_FILE_NAME=`vercel.json`,VERCEL_OUTPUT_CONFIG_FILE_NAME=`.vercel/output/config.json`;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasServices(e){return e!==void 0&&Object.keys(e).length>0}function resolveRelativeEntrypoint(e,t){let r=relative(e,t);return r.length===0?`.`:r.replaceAll(`\\`,`/`)}async function resolveVercelOutputConfigLocation(n){let r=await findClosestLinkedVercelDirectory(n),i=r===void 0?n:dirname(r),a=await findClosestVercelOutputDirectory(n);return a===void 0?r===void 0?{outputConfigPath:join(n,VERCEL_OUTPUT_CONFIG_FILE_NAME),projectRoot:i}:{outputConfigPath:join(r,`output`,`config.json`),projectRoot:i}:{outputConfigPath:join(a,`config.json`),projectRoot:i}}function normalizeVercelServicesConfig(e,t){if(!isRecord(e))throw Error(`${t} must contain a JSON object.`);let n=e.experimentalServices;if(n!==void 0&&!isRecord(n))throw Error(`${t} experimentalServices must be a JSON object.`);return e}async function readVercelServicesConfig(e,t){try{return normalizeVercelServicesConfig(JSON.parse(await readFile(e,`utf8`)),t)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{};throw e}}function findServiceByFramework(e,t){return Object.values(e).find(e=>e.framework===t)}function resolveServicePrefix(e){if(e!==void 0){if(typeof e.routePrefix==`string`&&e.routePrefix.trim().length>0)return e.routePrefix.trim();if(typeof e.mount==`string`&&e.mount.trim().length>0)return e.mount.trim();if(isRecord(e.mount)&&typeof e.mount.path==`string`&&e.mount.path.trim().length>0)return e.mount.path.trim()}}function resolveConfiguredServicePrefix(e){return resolveServicePrefix(findServiceByFramework(e.services,`eve`))??e.servicePrefix}function assertRootServicesAreComplete(e){let t=findServiceByFramework(e.services,`eve`),n=findServiceByFramework(e.services,`nextjs`);if(t!==void 0&&n!==void 0)return resolveServicePrefix(t)??e.servicePrefix;throw Error(`${VERCEL_JSON_FILE_NAME} already defines experimentalServices, so withEve cannot add generated services through ${VERCEL_OUTPUT_CONFIG_FILE_NAME}. Add both the Next.js and Eve services to ${VERCEL_JSON_FILE_NAME}, or remove experimentalServices from ${VERCEL_JSON_FILE_NAME}.`)}async function ensureEveVercelOutputConfig(n){let{
|
|
1
|
+
import{dirname,join,relative}from"node:path";import{mkdir,readFile,writeFile}from"node:fs/promises";import{findClosestLinkedVercelDirectory,findClosestVercelOutputDirectory}from"#shared/vercel-output-directory.js";const VERCEL_JSON_FILE_NAME=`vercel.json`,VERCEL_OUTPUT_CONFIG_FILE_NAME=`.vercel/output/config.json`;function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasServices(e){return e!==void 0&&Object.keys(e).length>0}function resolveRelativeEntrypoint(e,t){let r=relative(e,t);return r.length===0?`.`:r.replaceAll(`\\`,`/`)}async function resolveVercelOutputConfigLocation(n){let r=await findClosestLinkedVercelDirectory(n),i=r===void 0?n:dirname(r),a=await findClosestVercelOutputDirectory(n);return a===void 0?r===void 0?{canWriteGeneratedOutput:!1,outputConfigPath:join(n,VERCEL_OUTPUT_CONFIG_FILE_NAME),projectRoot:i}:{canWriteGeneratedOutput:!0,outputConfigPath:join(r,`output`,`config.json`),projectRoot:i}:{canWriteGeneratedOutput:!0,outputConfigPath:join(a,`config.json`),projectRoot:i}}function normalizeVercelServicesConfig(e,t){if(!isRecord(e))throw Error(`${t} must contain a JSON object.`);let n=e.experimentalServices;if(n!==void 0&&!isRecord(n))throw Error(`${t} experimentalServices must be a JSON object.`);return e}async function readVercelServicesConfig(e,t){try{return normalizeVercelServicesConfig(JSON.parse(await readFile(e,`utf8`)),t)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{};throw e}}function findServiceByFramework(e,t){return Object.values(e).find(e=>e.framework===t)}function resolveServicePrefix(e){if(e!==void 0){if(typeof e.routePrefix==`string`&&e.routePrefix.trim().length>0)return e.routePrefix.trim();if(typeof e.mount==`string`&&e.mount.trim().length>0)return e.mount.trim();if(isRecord(e.mount)&&typeof e.mount.path==`string`&&e.mount.path.trim().length>0)return e.mount.path.trim()}}function resolveConfiguredServicePrefix(e){return resolveServicePrefix(findServiceByFramework(e.services,`eve`))??e.servicePrefix}function assertRootServicesAreComplete(e){let t=findServiceByFramework(e.services,`eve`),n=findServiceByFramework(e.services,`nextjs`);if(t!==void 0&&n!==void 0)return resolveServicePrefix(t)??e.servicePrefix;throw Error(`${VERCEL_JSON_FILE_NAME} already defines experimentalServices, so withEve cannot add generated services through ${VERCEL_OUTPUT_CONFIG_FILE_NAME}. Add both the Next.js and Eve services to ${VERCEL_JSON_FILE_NAME}, or remove experimentalServices from ${VERCEL_JSON_FILE_NAME}.`)}async function ensureEveVercelOutputConfig(n){let{canWriteGeneratedOutput:i,outputConfigPath:o,projectRoot:s}=await resolveVercelOutputConfigLocation(n.nextRoot),c=(await readVercelServicesConfig(join(s,VERCEL_JSON_FILE_NAME),VERCEL_JSON_FILE_NAME)).experimentalServices;if(hasServices(c))return{servicePrefix:assertRootServicesAreComplete({services:c,servicePrefix:n.servicePrefix})};let l=await readVercelServicesConfig(o,VERCEL_OUTPUT_CONFIG_FILE_NAME),u=resolveRelativeEntrypoint(n.nextRoot,n.appRoot),d=l.experimentalServices??{},f=findServiceByFramework(d,`eve`),p=findServiceByFramework(d,`nextjs`),m=resolveConfiguredServicePrefix({services:d,servicePrefix:n.servicePrefix});if(!i)return{servicePrefix:m};let h={...d};p===void 0&&(h.web={entrypoint:`.`,framework:`nextjs`,mount:`/`,type:`web`}),f===void 0&&(h.eve={buildCommand:n.eveBuildCommand,entrypoint:u,framework:`eve`,mount:m,type:`web`});let g={...l,version:3,experimentalServices:h};return JSON.stringify(l)!==JSON.stringify(g)&&(await mkdir(dirname(o),{recursive:!0}),await writeFile(o,`${JSON.stringify(g,null,2)}\n`)),{servicePrefix:m}}export{ensureEveVercelOutputConfig};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.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.
|
|
1
|
+
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.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.11.1`,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_TSGO_VERSION__`,t.tsgoPackageVersion).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,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__";\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
|
+
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__";\nconst BETA_TERMS_HREF = "https://vercel.com/docs/release-phases/public-beta-agreement";\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 <a\n 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"\n href={BETA_TERMS_HREF}\n rel="noreferrer"\n target="_blank"\n >\n Public preview\n </a>\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 <a\n 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"\n href={BETA_TERMS_HREF}\n rel="noreferrer"\n target="_blank"\n >\n Public preview\n </a>\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,6 +32,7 @@ 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";
|
|
35
36
|
|
|
36
37
|
type AgentStatus = ReturnType<typeof useEveAgent>["status"];
|
|
37
38
|
|
|
@@ -57,9 +58,19 @@ export function AgentChat() {
|
|
|
57
58
|
return (
|
|
58
59
|
<main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">
|
|
59
60
|
{isEmpty ? null : (
|
|
60
|
-
<header className="flex h-14 shrink-0 items-center justify-center gap-
|
|
61
|
-
<span className="
|
|
62
|
-
|
|
61
|
+
<header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">
|
|
62
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
63
|
+
<span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>
|
|
64
|
+
<StatusDot status={agent.status} />
|
|
65
|
+
</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>
|
|
63
74
|
</header>
|
|
64
75
|
)}
|
|
65
76
|
|
|
@@ -102,7 +113,19 @@ export function AgentChat() {
|
|
|
102
113
|
: "max-w-3xl shrink-0 pb-6",
|
|
103
114
|
)}
|
|
104
115
|
>
|
|
105
|
-
{isEmpty ?
|
|
116
|
+
{isEmpty ? (
|
|
117
|
+
<div className="flex flex-col items-center gap-3 text-center">
|
|
118
|
+
<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
|
+
</div>
|
|
128
|
+
) : null}
|
|
106
129
|
<div className="w-full">{composer}</div>
|
|
107
130
|
</div>
|
|
108
131
|
</main>
|
|
@@ -4,4 +4,4 @@ import { slackChannel } from "eve/channels/slack";
|
|
|
4
4
|
export default slackChannel({
|
|
5
5
|
credentials: connectSlackCredentials(${JSON.stringify(e)}),
|
|
6
6
|
});
|
|
7
|
-
`}function renderWebAppTemplate(e,t
|
|
7
|
+
`}function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,eve:n?.eve??WEB_DEFAULT_VERCEL_SERVICES.eve}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let r of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,r);await pathExists(i)&&t.push(i)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(t){let i=join(t.projectRoot,`package.json`),a=await pathExists(join(t.projectRoot,`app/page.tsx`));if(!t.force&&await isNextJsProject(t.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[i],packageJsonUpdated:[]};let o=await patchWebPackageJson(i,resolveWebPackageVersions(t.webPackageVersions)),s=[],l=[],u=[],d=[],m=basename(resolve(t.projectRoot));if(t.configureVercelServices??!0){let e=join(t.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(e)===`written`?s.push(e):d.push(e)}let h=await getPackageManagerStrategy(t.packageManager??`pnpm`).applyProjectConfiguration(t.projectRoot);s.push(...h.filesWritten),d.push(...h.filesSkipped);for(let[e,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(t.projectRoot,e);if(e===`agent/channels/eve.ts`&&!t.force&&await pathExists(a)){d.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,m),{force:!0}),s.push(a),o&&l.push(a)}u.push(...await findCompetingNextConfigFiles(t.projectRoot));let g={kind:`web`,action:a?`overwritten`:`created`,filesWritten:s,filesSkipped:d,packageJsonUpdated:o.mutations};return l.length>0&&(g.filesOverwritten=l),u.length>0&&(g.competingNextConfigFiles=u),o.nodeEngineOverride!==void 0&&(g.nodeEngineOverride=o.nodeEngineOverride),g}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.2.2`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return i&&(l.filesOverwritten=[t]),l}async function listAuthoredChannels(e){let t=join(e,`agent/channels`),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug};
|
package/docs/README.md
CHANGED
|
@@ -10,6 +10,20 @@ Important naming note:
|
|
|
10
10
|
- The current published package name is `eve`.
|
|
11
11
|
- The CLI binary is `eve`.
|
|
12
12
|
|
|
13
|
+
## Legal and safeguards
|
|
14
|
+
|
|
15
|
+
Eve is currently a preview and subject to the Vercel beta terms; the framework, APIs, documentation, and behavior may change before general availability.
|
|
16
|
+
|
|
17
|
+
As the deployer, it is your responsibility to ensure your agent complies with applicable laws.
|
|
18
|
+
|
|
19
|
+
You are responsible for configuring approval policies, tool restrictions, connection scopes, route/session authorization, sandbox controls, telemetry exports, and other safeguards appropriate for your use case.
|
|
20
|
+
|
|
21
|
+
Before using Eve with non-public, sensitive, regulated, or production data, review which default tools, custom tools, MCP tools, shell/file/web tools, connected services, subagents, schedules, and external actions are available to the agent.
|
|
22
|
+
|
|
23
|
+
Require human approval or other safeguards for sensitive, irreversible, regulated, financial, healthcare, employment, housing, legal, safety-impacting, user-impacting, or external side-effecting actions.
|
|
24
|
+
|
|
25
|
+
Unless you configure stricter controls, Eve agents may operate with permissive settings, including tool execution without human approval where approval is omitted and sandbox network egress that is not deny-all. Do not rely on model behavior alone to prevent sensitive or irreversible actions.
|
|
26
|
+
|
|
13
27
|
Casing convention:
|
|
14
28
|
|
|
15
29
|
- Use Title Case for page `title` frontmatter and `meta.json` section titles (Fumadocs renders the page `title` as both the sidebar entry and the `<h1>`, so one casing covers both) — e.g. `Execution Model & Durability`, `Dynamic Capabilities`, `Build an Agent`.
|
|
@@ -30,14 +44,14 @@ Read in this order:
|
|
|
30
44
|
9. [Connections](./connections.mdx)
|
|
31
45
|
10. [Sandboxes](./sandbox.mdx)
|
|
32
46
|
11. [Channels](./channels/overview.mdx)
|
|
33
|
-
12. [Session Context](./
|
|
47
|
+
12. [Session Context](./reference/typescript-api.md)
|
|
34
48
|
13. [Sessions And Streaming](./concepts/sessions-runs-and-streaming.md)
|
|
35
|
-
14. [TypeScript SDK](./
|
|
49
|
+
14. [TypeScript SDK](./clients/typescript-sdk/overview.mdx)
|
|
36
50
|
15. [Subagents](./subagents.mdx)
|
|
37
51
|
16. [Schedules](./schedules.mdx)
|
|
38
52
|
17. [Evals](./evals/overview.mdx)
|
|
39
|
-
18. [Auth And Route Protection](./
|
|
40
|
-
19. [Vercel Deployment](./
|
|
53
|
+
18. [Auth And Route Protection](./develop/auth-and-route-protection.md)
|
|
54
|
+
19. [Vercel Deployment](./develop/deployment.md)
|
|
41
55
|
20. [CLI, Build, And Debugging](./reference/cli.md)
|
|
42
56
|
|
|
43
57
|
## The public mental model
|
|
@@ -90,5 +104,5 @@ That is why Eve exposes two identifiers:
|
|
|
90
104
|
|
|
91
105
|
## Good companions in this repo
|
|
92
106
|
|
|
93
|
-
- Weather-focused smoke/dev fixture: [`../../apps/fixtures/weather-
|
|
107
|
+
- Weather-focused smoke/dev fixture: [`../../apps/fixtures/weather-fixture`](../../apps/fixtures/weather-fixture)
|
|
94
108
|
- Public API source of truth: [`../../packages/eve/src/public/index.ts`](../../packages/eve/src/public/index.ts)
|
package/docs/agent-config.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "agent.ts"
|
|
3
|
-
description: "
|
|
3
|
+
description: "Set the agent's runtime config in agent.ts with defineAgent, including the model and compaction."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
An agent's `agent.ts` calls `defineAgent` (from `eve`) to set its runtime config.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Set the model
|
|
9
9
|
|
|
10
10
|
A typical config selects a model:
|
|
11
11
|
|
|
@@ -20,7 +20,7 @@ export default defineAgent({
|
|
|
20
20
|
The root `agent.ts` can be omitted when no runtime config is needed. In that case, Eve defaults
|
|
21
21
|
to `anthropic/claude-sonnet-4.6`. When `agent.ts` is present, `model` is required.
|
|
22
22
|
|
|
23
|
-
`model`
|
|
23
|
+
`model` accepts a gateway model id string, which routes through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). To call a provider directly and configure the model in code, pass a provider-authored `LanguageModel`:
|
|
24
24
|
|
|
25
25
|
```ts title="agent/agent.ts"
|
|
26
26
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
@@ -31,9 +31,11 @@ export default defineAgent({
|
|
|
31
31
|
});
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
Model use is subject to the terms, data-processing commitments, retention behavior, and available controls of the selected provider and routing path. Review the [AI Gateway model catalog](https://vercel.com/ai-gateway/models) for gateway-routed models, and review the provider's terms when you configure a direct `LanguageModel`.
|
|
35
|
+
|
|
34
36
|
## Compaction
|
|
35
37
|
|
|
36
|
-
Compaction summarizes older turns as you approach the context window. It
|
|
38
|
+
Compaction summarizes older turns as you approach the context window. It's on by default, so you only tune when it kicks in. Lower `thresholdPercent` to compact sooner:
|
|
37
39
|
|
|
38
40
|
```ts title="agent/agent.ts"
|
|
39
41
|
export default defineAgent({
|
|
@@ -46,25 +48,20 @@ export default defineAgent({
|
|
|
46
48
|
|
|
47
49
|
See [Default harness](./concepts/default-harness#compaction) for how the loop applies it.
|
|
48
50
|
|
|
49
|
-
## Other fields
|
|
50
|
-
|
|
51
|
-
`defineAgent` takes a few more fields. For every field and its type, see the [TypeScript API](./reference/typescript-api).
|
|
52
|
-
|
|
53
|
-
### `modelOptions`
|
|
54
|
-
|
|
55
|
-
Provider option overrides forwarded to the model call.
|
|
56
|
-
|
|
57
|
-
### `experimental`
|
|
58
|
-
|
|
59
|
-
Opt-in flags that can change or disappear in any release, so treat them as unstable. The main one is `codeMode`, which routes executable tools through a sandboxed code-execution wrapper.
|
|
51
|
+
## Other defineAgent fields
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
`defineAgent` takes a few more fields, all optional. For the exported types, see the [TypeScript API](./reference/typescript-api).
|
|
62
54
|
|
|
63
|
-
|
|
55
|
+
| Field | Type | Default | Description |
|
|
56
|
+
| -------------- | --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
|
|
58
|
+
| `experimental` | `{ codeMode?: boolean }` | flags unset | Opt-in flags that can change or disappear in any release. Treat them as unstable. `codeMode` routes executable tools through a sandboxed code-execution wrapper, where the model writes JavaScript that calls the tools inside the [sandbox](./sandbox). |
|
|
59
|
+
| `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
|
|
60
|
+
| `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while Eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
|
|
64
61
|
|
|
65
|
-
|
|
62
|
+
`codeMode` is experimental and may change or be removed.
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
`externalDependencies` is a packaging control only. It keeps selected packages as runtime dependencies in the hosted output; it does not authorize, configure, or review any third-party service those packages may call.
|
|
68
65
|
|
|
69
66
|
## Where adjacent settings live
|
|
70
67
|
|
package/docs/channels/custom.mdx
CHANGED
|
@@ -3,13 +3,13 @@ title: "Custom Channels"
|
|
|
3
3
|
description: "Author custom HTTP and WebSocket channels with routes, events, metadata, continuation tokens, and file uploads."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
When Eve doesn't ship a channel for your surface, you build one. Custom channels
|
|
6
|
+
When Eve doesn't ship a channel for your surface, you build one. Custom channels expose HTTP or WebSocket endpoints, parse incoming requests, start or resume sessions, observe runtime events, and own delivery back to your platform.
|
|
7
7
|
|
|
8
8
|
## File location and identity
|
|
9
9
|
|
|
10
10
|
Custom channels live in `agent/channels/` at the root agent. Local subagents do not declare channels today.
|
|
11
11
|
|
|
12
|
-
The channel file stem becomes the channel id, so `agent/channels/internal-webhook.ts` is addressed as `internal-webhook`.
|
|
12
|
+
The channel file stem becomes the channel id, so `agent/channels/internal-webhook.ts` is addressed as `internal-webhook`. Export the channel definition as the module's default export.
|
|
13
13
|
|
|
14
14
|
## Define a channel
|
|
15
15
|
|
|
@@ -32,7 +32,7 @@ export default defineChannel({
|
|
|
32
32
|
const stream = await session.getEventStream();
|
|
33
33
|
|
|
34
34
|
return new Response(stream, {
|
|
35
|
-
headers: { "content-type": "
|
|
35
|
+
headers: { "content-type": "application/x-ndjson; charset=utf-8" },
|
|
36
36
|
});
|
|
37
37
|
}),
|
|
38
38
|
],
|
|
@@ -44,14 +44,16 @@ export default defineChannel({
|
|
|
44
44
|
});
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Declare routes with the `POST()` and `GET()` helpers. Each route handler receives the raw `Request` and a helpers object:
|
|
48
48
|
|
|
49
|
-
- `send(message, { auth, continuationToken, state? })
|
|
50
|
-
- `getSession(sessionId)
|
|
51
|
-
- `
|
|
52
|
-
- `
|
|
49
|
+
- `send(message, { auth, continuationToken, state? })` starts or resumes a session. Returns a `Session`.
|
|
50
|
+
- `getSession(sessionId)` looks up an existing session. The returned `Session` exposes `getEventStream({ startIndex? })` for streaming.
|
|
51
|
+
- `receive(channel, ...)` hands inbound work to a different channel for cross-channel hand-off.
|
|
52
|
+
- `params` holds route parameters extracted from the path pattern.
|
|
53
|
+
- `waitUntil(promise)` extends the request lifetime for background work.
|
|
54
|
+
- `requestIp` is the client IP, or `null` when the host cannot provide it.
|
|
53
55
|
|
|
54
|
-
Event handlers like `"message.completed"` are declared under the `events` key. They receive `(eventData, channel, ctx)`, where `channel` carries platform handles and session continuation operations, and `ctx` is the Eve `SessionContext`.
|
|
56
|
+
Event handlers like `"message.completed"` are declared under the `events` key. They receive `(eventData, channel, ctx)`, where `eventData` is the event payload, `channel` carries platform handles and session continuation operations, and `ctx` is the Eve `SessionContext`. Every channel kind shares this signature. The one exception is `session.failed`, which receives only `(eventData, channel)` with no `ctx`.
|
|
55
57
|
|
|
56
58
|
## WebSocket routes
|
|
57
59
|
|
|
@@ -92,7 +94,7 @@ export default defineChannel({
|
|
|
92
94
|
});
|
|
93
95
|
```
|
|
94
96
|
|
|
95
|
-
The bridge server does not listen on its own port. It receives only upgrade events that matched the Eve route, and only on hosts where Nitro exposes the raw Node upgrade request, socket, and head. Treat it as a compatibility adapter for libraries with server-binding APIs, not
|
|
97
|
+
The bridge server does not listen on its own port. It receives only upgrade events that matched the Eve route, and only on hosts where Nitro exposes the raw Node upgrade request, socket, and head. Treat it as a compatibility adapter for libraries with server-binding APIs, not the primary way to build websocket channels in Eve.
|
|
96
98
|
|
|
97
99
|
## Cross-channel hand-off
|
|
98
100
|
|
|
@@ -135,7 +137,7 @@ Semantics:
|
|
|
135
137
|
|
|
136
138
|
## Channel metadata
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
A channel can project a subset of its adapter state as metadata, available to instrumentation resolvers, dynamic tool resolvers, and dynamic skill or instruction resolvers. Define a `metadata(state)` function on the channel config:
|
|
139
141
|
|
|
140
142
|
```ts
|
|
141
143
|
import { defineChannel, POST } from "eve/channels";
|
|
@@ -167,8 +169,8 @@ export default defineChannel({
|
|
|
167
169
|
}),
|
|
168
170
|
],
|
|
169
171
|
events: {
|
|
170
|
-
"turn.started"(
|
|
171
|
-
|
|
172
|
+
"turn.started"(eventData, channel) {
|
|
173
|
+
channel.state.internalCounter += 1;
|
|
172
174
|
},
|
|
173
175
|
},
|
|
174
176
|
});
|
|
@@ -190,13 +192,17 @@ slackContinuationToken("C0123ABC", "1800000000.001234"); // "C0123ABC:1800000000
|
|
|
190
192
|
twilioContinuationToken("+15551234567", "+15557654321"); // "+15551234567:+15557654321"
|
|
191
193
|
```
|
|
192
194
|
|
|
193
|
-
Custom channels write their own function that joins the identity fields. The framework
|
|
195
|
+
Custom channels write their own function that joins the identity fields. The framework derives nothing for you; the channel owns its token format.
|
|
194
196
|
|
|
195
|
-
When the identity that should address a session is not known until later, the channel can re-key the parked session by calling `
|
|
197
|
+
When the identity that should address a session is not known until later, the channel can re-key the parked session by calling `session.setContinuationToken(...)`. Pass the channel-local raw token; the runtime preserves the current channel namespace.
|
|
198
|
+
|
|
199
|
+
The `context(state, session)` config option builds the per-step `channel` argument handed to every event handler. It receives the channel's live adapter `state` and a `SessionHandle`, and returns the channel-owned context (thread handles, API clients, late-bound callbacks). The framework injects [`ChannelSessionOps`](#define-a-channel) and passes the result as the second positional argument to each handler. Closing over `session` lets the factory register callbacks that re-key the session later. State mutations made through the returned context are written back to adapter state.
|
|
196
200
|
|
|
197
201
|
```ts
|
|
198
202
|
import { defineChannel } from "eve/channels";
|
|
199
203
|
|
|
204
|
+
import { mintRef } from "./refs.js";
|
|
205
|
+
|
|
200
206
|
defineChannel<{ ref: string | null }>({
|
|
201
207
|
state: { ref: null },
|
|
202
208
|
context(state, session) {
|
|
@@ -209,8 +215,8 @@ defineChannel<{ ref: string | null }>({
|
|
|
209
215
|
};
|
|
210
216
|
},
|
|
211
217
|
events: {
|
|
212
|
-
"message.completed"(
|
|
213
|
-
if (!
|
|
218
|
+
"message.completed"(eventData, channel) {
|
|
219
|
+
if (!channel.state.ref) channel.registerAnchor(mintRef());
|
|
214
220
|
},
|
|
215
221
|
},
|
|
216
222
|
routes: [
|
|
@@ -264,7 +270,7 @@ defineChannel({
|
|
|
264
270
|
});
|
|
265
271
|
```
|
|
266
272
|
|
|
267
|
-
The `URL` object survives the queue boundary as a string and is reconstituted inside the workflow step. The staging pipeline calls `fetchFile(url)
|
|
273
|
+
The `URL` object survives the queue boundary as a string and is reconstituted inside the workflow step. The staging pipeline calls `fetchFile` with the URL serialized as a string (the URL's `href`), which is why the example matches on `url.startsWith(...)`. Return bytes to stage the file to the sandbox, or `null` to let the URL pass through to the model provider.
|
|
268
274
|
|
|
269
275
|
The framework handles staging bytes to the sandbox, enforcing upload policy, hydrating files for the model call, and reconstituting `URL` objects after queue serialization.
|
|
270
276
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Discord"
|
|
3
|
-
description: "Reach your agent from Discord HTTP Interactions
|
|
3
|
+
description: "Reach your agent from Discord HTTP Interactions, including slash commands, components, and modals."
|
|
4
4
|
type: integration
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Discord channel wires your agent into Discord's HTTP Interactions
|
|
7
|
+
The Discord channel wires your agent into Discord's HTTP Interactions, including slash and application commands, message components, and modal submissions. Discord enforces a three-second ACK deadline, so the channel verifies the Ed25519 signature headers, acknowledges the command right away, and runs the Eve work in the background. See [Channels](./overview) for the contract this builds on.
|
|
8
8
|
|
|
9
9
|
## Add the channel
|
|
10
10
|
|
|
@@ -20,11 +20,11 @@ DISCORD_APPLICATION_ID=... # edits the deferred response and sends followups
|
|
|
20
20
|
DISCORD_BOT_TOKEN=... # proactive messages + fallback + typing indicators
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
To skip env vars, pass the same values through `credentials: { applicationId, botToken, publicKey }`. The route is `POST /eve/v1/discord` by default. Paste that public URL into your Discord application's Interactions Endpoint URL.
|
|
24
24
|
|
|
25
25
|
## Register a command
|
|
26
26
|
|
|
27
|
-
Registering commands is on you, not the channel. Use Discord's API or the Developer Portal.
|
|
27
|
+
Registering commands is on you, not the channel. Use Discord's API or the Developer Portal. A string option named `message` lines up with Eve's default prompt extraction:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
curl -X PUT "https://discord.com/api/v10/applications/$DISCORD_APPLICATION_ID/commands" \
|
|
@@ -35,9 +35,11 @@ curl -X PUT "https://discord.com/api/v10/applications/$DISCORD_APPLICATION_ID/co
|
|
|
35
35
|
|
|
36
36
|
Use guild commands during development for faster propagation.
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## How the channel handles messages
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
### Dispatch
|
|
41
|
+
|
|
42
|
+
`onCommand(ctx, interaction)` decides whether to dispatch and under what `auth`. Return `{ auth }` to proceed or `null` to drop the interaction. By default, auth comes from the invoking user. Event handlers receive `(eventData, channel, ctx)`, with Discord platform handles on `channel.discord`:
|
|
41
43
|
|
|
42
44
|
```ts
|
|
43
45
|
import { discordChannel } from "eve/channels/discord";
|
|
@@ -52,21 +54,31 @@ export default discordChannel({
|
|
|
52
54
|
},
|
|
53
55
|
}),
|
|
54
56
|
events: {
|
|
55
|
-
"message.completed"(
|
|
56
|
-
if (
|
|
57
|
-
if (
|
|
57
|
+
"message.completed"(eventData, channel, ctx) {
|
|
58
|
+
if (eventData.finishReason === "tool-calls") return;
|
|
59
|
+
if (eventData.message) channel.discord.post(eventData.message);
|
|
58
60
|
},
|
|
59
61
|
},
|
|
60
62
|
});
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
### Delivery
|
|
66
|
+
|
|
67
|
+
The default `message.completed` handler edits the deferred response for the first reply and sends followups after that. If the interaction token is rejected, it falls back to a bot-authenticated channel message. Long text is split to Discord's 2000-char limit, and generated messages default to `allowed_mentions: { parse: [] }`.
|
|
68
|
+
|
|
69
|
+
Typing fires on `turn.started` and `actions.requested`, but only when a bot token is present. In custom hooks, call `channel.discord.startTyping()` yourself.
|
|
70
|
+
|
|
71
|
+
### Human-in-the-loop (HITL)
|
|
72
|
+
|
|
73
|
+
HITL renders as Discord components. Confirmations and options become buttons, `display: "select"` becomes a string select, and freeform input becomes a button that opens a modal. When the user responds, the parked session (paused awaiting input) resumes.
|
|
74
|
+
|
|
75
|
+
### Proactive sessions
|
|
64
76
|
|
|
65
|
-
|
|
77
|
+
Start a session without an inbound interaction through `receive(discord, { message, target, auth })` from a schedule `run` handler, or `args.receive(discord, ...)` from another channel. The proactive target shape is `{ channelId, conversationId?, initialMessage? }`. Either path needs `DISCORD_BOT_TOKEN`.
|
|
66
78
|
|
|
67
|
-
|
|
79
|
+
### Attachments
|
|
68
80
|
|
|
69
|
-
|
|
81
|
+
Inbound file attachments are not supported on this channel today.
|
|
70
82
|
|
|
71
83
|
## What to read next
|
|
72
84
|
|