eve 0.52.4 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/src/cli/commands/init-agent-workspace.d.ts +2 -0
- package/dist/src/cli/commands/init-agent-workspace.js +1 -1
- package/dist/src/cli/commands/init-install.d.ts +8 -0
- package/dist/src/cli/commands/init-install.js +1 -0
- package/dist/src/cli/commands/init.d.ts +3 -4
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/dev/tui/message-queue.js +1 -1
- package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
- package/dist/src/cli/run.js +2 -2
- package/dist/src/cli/telemetry/index.d.ts +3 -0
- package/dist/src/cli/telemetry/index.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +17 -16
- package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +23 -2
- package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -4
- package/dist/src/compiled/@ai-sdk/google/index.d.ts +145 -33
- package/dist/src/compiled/@ai-sdk/google/index.js +10 -7
- package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +176 -7
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/openai/index.d.ts +71 -40
- package/dist/src/compiled/@ai-sdk/openai/index.js +8 -8
- package/dist/src/compiled/@ai-sdk/otel/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/provider/index.d.ts +225 -3
- package/dist/src/compiled/@ai-sdk/provider-utils/index.d.ts +220 -151
- package/dist/src/compiled/@workflow/core/create-hook.d.ts +3 -0
- package/dist/src/compiled/@workflow/core/index.js +2 -2
- package/dist/src/compiled/@workflow/core/private.js +1 -1
- package/dist/src/compiled/@workflow/core/runtime/resume-hook.d.ts +59 -7
- package/dist/src/compiled/@workflow/core/runtime/run.d.ts +42 -0
- package/dist/src/compiled/@workflow/core/runtime/start.d.ts +31 -1
- package/dist/src/compiled/@workflow/core/runtime.d.ts +2 -2
- package/dist/src/compiled/@workflow/core/runtime.js +2 -2
- package/dist/src/compiled/@workflow/core/serialization.d.ts +22 -0
- package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
- package/dist/src/compiled/@workflow/core/workflow.js +1 -1
- package/dist/src/compiled/@workflow/errors/index.d.ts +37 -3
- package/dist/src/compiled/@workflow/errors/index.js +1 -1
- package/dist/src/compiled/@workflow/world/attributes-validation.d.ts +68 -0
- package/dist/src/compiled/@workflow/world/index.d.ts +1 -1
- package/dist/src/compiled/@workflow/world/interfaces.d.ts +21 -0
- package/dist/src/compiled/@workflow/world-local/index.js +5 -5
- package/dist/src/compiled/@workflow/world-vercel/index.js +3 -3
- package/dist/src/compiled/_chunks/workflow/attribute-changes-C6H-fRVP.js +20 -0
- package/dist/src/compiled/_chunks/workflow/dist-DDeH-1ER.js +40 -0
- package/dist/src/compiled/_chunks/workflow/dist-DhncQSl7.js +74 -0
- package/dist/src/compiled/_chunks/workflow/{dist-BIO9Inoc.js → dist-xTushGCp.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/frames-DsEqSDes.js +3 -0
- package/dist/src/compiled/_chunks/workflow/{private-aXEeo0fU.js → private-CFJkQA7R.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/resume-hook-BRDLAb67.js +1 -0
- package/dist/src/compiled/_chunks/workflow/run-BM2OFphk.js +1 -0
- package/dist/src/compiled/_chunks/workflow/sleep-6cG0F99-.js +1 -0
- package/dist/src/compiled/_chunks/workflow/{ws-transport-M7uk8HV8.js → ws-transport-DuK82LLz.js} +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +7 -6
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-agent-config.js +1 -1
- package/dist/src/compiler/normalize-manifest-helpers.js +1 -1
- package/dist/src/context/dynamic-model-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +11 -2
- package/dist/src/context/keys.js +1 -1
- package/dist/src/evals/mock-model.js +1 -1
- package/dist/src/evals/runner/run-evals.d.ts +1 -1
- package/dist/src/evals/runner/run-evals.js +1 -1
- package/dist/src/execution/activity-cohort.d.ts +20 -0
- package/dist/src/execution/activity-cohort.js +1 -0
- package/dist/src/execution/authorization-callback-match.d.ts +1 -0
- package/dist/src/execution/authorization-callback-match.js +1 -1
- package/dist/src/execution/cancelled-model-call-batch.d.ts +15 -0
- package/dist/src/execution/cancelled-model-call-batch.js +1 -0
- package/dist/src/execution/coordination-dispatch-shared.js +1 -1
- package/dist/src/execution/model-call-batching.d.ts +11 -0
- package/dist/src/execution/model-call-batching.js +1 -0
- package/dist/src/execution/parked-delivery-wait.js +1 -1
- package/dist/src/execution/session-activity-projection.d.ts +2 -0
- package/dist/src/execution/session-activity-projection.js +1 -1
- package/dist/src/execution/skills/instructions.js +1 -1
- package/dist/src/execution/tools/sleep-workflow.js +1 -1
- package/dist/src/execution/tools/subagent/invoke-agent.d.ts +5 -5
- package/dist/src/execution/tools/subagent/invoke-agent.js +1 -1
- package/dist/src/execution/tools/subagent/start.js +1 -1
- package/dist/src/execution/tools/workflow/body.js +1 -1
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/wire/session-inbox-resume.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/approval-delivery-coordinator.js +1 -1
- package/dist/src/harness/authorization.d.ts +1 -0
- package/dist/src/harness/authorization.js +1 -1
- package/dist/src/harness/compaction.js +1 -1
- package/dist/src/harness/current-messages.d.ts +8 -2
- package/dist/src/harness/current-messages.js +1 -1
- package/dist/src/harness/hitl/approval-input-requests.js +1 -1
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/pending-input-batches.d.ts +4 -0
- package/dist/src/harness/pending-input-batches.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/harness/turn-client-context.d.ts +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/core.js +1 -1
- package/dist/src/internal/authored-package-boundary.js +1 -1
- package/dist/src/internal/mock-model-identity.d.ts +2 -0
- package/dist/src/internal/mock-model-identity.js +1 -0
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
- package/dist/src/internal/workflow-bundle/authored-workflow-directives.js +1 -1
- package/dist/src/public/tools/index.d.ts +1 -0
- package/dist/src/public/tools/index.js +1 -1
- package/dist/src/runtime/resolve-agent.js +1 -1
- package/dist/src/self-modification/agent.d.ts +2 -2
- package/dist/src/self-modification/agent.js +1 -1
- package/dist/src/self-modification/config.d.ts +15 -0
- package/dist/src/self-modification/config.js +1 -1
- package/dist/src/self-modification/extension/extension.d.ts +3 -0
- package/dist/src/self-modification/extension/extension.js +1 -1
- package/dist/src/self-modification/setup.d.ts +3 -0
- package/dist/src/self-modification/setup.js +25 -4
- package/dist/src/setup/integrations/self-modification/setup.js +1 -1
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +2 -2
- package/dist/src/setup/scaffold/update/web-options.js +1 -1
- package/dist/src/shared/agent-definition.d.ts +11 -2
- package/dist/src/tasks/delivery-context.d.ts +2 -2
- package/dist/src/tasks/delivery-context.js +2 -18
- package/dist/src/tasks/delivery-policy.js +1 -1
- package/dist/src/tools/durable-callbacks.d.ts +18 -1
- package/dist/src/tools/durable-callbacks.js +1 -1
- package/dist/src/tools/workflow-definition.d.ts +38 -4
- package/docs/agent-config.md +44 -8
- package/docs/channels/overview.mdx +2 -0
- package/docs/channels/slack.mdx +2 -0
- package/docs/concepts/execution-model-and-durability.mdx +20 -2
- package/docs/evals/running.mdx +3 -1
- package/docs/guides/dev-tui.md +11 -11
- package/docs/guides/dynamic-capabilities.md +36 -1
- package/docs/guides/instrumentation.md +3 -2
- package/docs/instructions.mdx +1 -1
- package/docs/reference/cli.md +1 -1
- package/docs/reference/telemetry.md +1 -1
- package/docs/reference/typescript-api.md +36 -35
- package/docs/subagents/index.mdx +1 -1
- package/docs/tools/overview.mdx +4 -2
- package/docs/tools/workflows.mdx +15 -10
- package/package.json +17 -17
- package/dist/src/compiled/_chunks/workflow/attribute-changes-DpruylJZ.js +0 -20
- package/dist/src/compiled/_chunks/workflow/dist-ChzQSWQA.js +0 -65
- package/dist/src/compiled/_chunks/workflow/dist-veFIAdWV.js +0 -40
- package/dist/src/compiled/_chunks/workflow/frames-DKxlMf3X.js +0 -3
- package/dist/src/compiled/_chunks/workflow/resume-hook-DWDi_wCN.js +0 -1
- package/dist/src/compiled/_chunks/workflow/run-vsAdI41O.js +0 -1
- package/dist/src/compiled/_chunks/workflow/sleep-BKxoNDRv.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveSelfModificationConfig}from"./config.js";import{hasGitHubCredential}from"./credentials.js";import{resolveSelfModificationMode}from"./mode.js";import{defineAgent,defineDynamic}from"#public/index.js";const DEFAULT_SELF_MODIFICATION_MODEL=`anthropic/claude-sonnet-5`;function defineSelfModificationAgent(e={}){let t=e.model??`anthropic/claude-sonnet-5`,n=resolveSelfModificationConfig(e.config),resolve=()=>{let
|
|
1
|
+
import{resolveSelfModificationConfig}from"./config.js";import{hasGitHubCredential}from"./credentials.js";import{resolveSelfModificationMode}from"./mode.js";import{defineAgent,defineDynamic}from"#public/index.js";const DEFAULT_SELF_MODIFICATION_MODEL=`anthropic/claude-sonnet-5`;function defineSelfModificationAgent(e={}){let t=e.model??`anthropic/claude-sonnet-5`,n=resolveSelfModificationConfig(e.config),resolve=async(e,r)=>{let i=resolveSelfModificationMode(n);if(i===`local`)return defineAgent({description:`Delegate here when the developer asks to change this eve agent or its authored source. Describe the requested behavior change without guessing source paths or passing runtime skill paths under $HOME/.agents or /workspace/skills; the subagent discovers authored files under its /source mount. Treat requests for persistent changes to future behavior or capabilities as source-modification requests, even when the developer does not mention files or source code. Infer persistence from the request and conversation rather than waiting for phrases such as “modify your source.” For example, asking the agent to stop always doing something, add a capability, or change future responses calls for inspecting and editing the authored source instead of providing a one-turn workaround. Also delegate questions about which integrations, channels, connections, or capabilities are available to add: the subagent searches the eve registry and reports exact item addresses instead of guessing them. Delegate requests to investigate, diagnose, or optimize the agent's behavior from local traces to this subagent: it can inspect the invoking session's trace and make persistent source changes when warranted. Resolve short follow-ups such as “yes” or “do it” against the preceding conversation. Source edits do not affect the caller’s current turn. After this subagent reports changes, do not invoke edited tools or attempt runtime verification until a new user turn. If whether the requested change should persist is genuinely ambiguous, ask one concise clarifying question.`,model:t});if(i!==`deployed`||n.deployed===void 0||n.deployed.credentials.kind===`pat`&&!hasGitHubCredential())return null;try{if(!await n.deployed.authorize({channel:r.channel,principal:r.session.auth.current}))return null}catch{return null}return defineAgent({description:`Delegate here when the user asks to change this eve agent or its authored source. Describe the requested behavior change without guessing source paths or passing runtime skill paths under $HOME/.agents or /workspace/skills; the subagent discovers authored files in an isolated checkout under its /source mount. Treat requests for persistent changes to future behavior or capabilities as source-modification requests, even when the user does not mention files or source code. Infer persistence from the request and conversation rather than waiting for phrases such as “modify your source.” For example, asking the agent to stop always doing something, add a capability, or change future responses calls for inspecting and editing the authored source instead of providing a one-turn workaround. Also delegate requests to add integrations, channels, connections, or capabilities: the subagent searches the official eve registry and can install source changes into the draft proposal. Setup may require non-secret answers or an external action, and secret binding remains separate. Resolve short follow-ups such as “yes” or “do it” against the preceding conversation. Source edits do not affect the caller’s current turn. The subagent can publish only a draft pull request, and changes become effective only after review, merge, and redeployment. If whether the requested change should persist is genuinely ambiguous, ask one concise clarifying question.`,model:t})};return defineDynamic({events:{"session.started":resolve,"turn.started":resolve}})}var agent_default=defineSelfModificationAgent();export{DEFAULT_SELF_MODIFICATION_MODEL,agent_default as default,defineSelfModificationAgent};
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import type { SessionAuthContext } from "#channel/types.js";
|
|
2
|
+
/** Principal and channel that requested a deployed source modification. */
|
|
3
|
+
export interface SelfModificationAuthorizationContext {
|
|
4
|
+
readonly channel: {
|
|
5
|
+
/** Channel adapter family, such as `"slack"` or `"http"`. */
|
|
6
|
+
readonly kind?: string;
|
|
7
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
readonly principal: SessionAuthContext | null;
|
|
10
|
+
}
|
|
11
|
+
/** Decides whether a principal may use deployed self-modification. */
|
|
12
|
+
export type SelfModificationAuthorization = (context: SelfModificationAuthorizationContext) => boolean | Promise<boolean>;
|
|
1
13
|
export interface SelfModificationConfig {
|
|
2
14
|
readonly local?: {
|
|
3
15
|
readonly enabled?: boolean;
|
|
@@ -14,6 +26,8 @@ export interface SelfModificationConfig {
|
|
|
14
26
|
readonly target: {
|
|
15
27
|
readonly branch: string;
|
|
16
28
|
};
|
|
29
|
+
/** Fail-closed policy for principals that may create draft proposals. */
|
|
30
|
+
readonly authorize: SelfModificationAuthorization;
|
|
17
31
|
readonly credentials?: {
|
|
18
32
|
/** Ephemeral, repository-scoped GitHub App credentials from Vercel Connect. */
|
|
19
33
|
readonly vercelConnect?: {
|
|
@@ -32,6 +46,7 @@ export interface ResolvedSelfModificationConfig {
|
|
|
32
46
|
readonly deployed?: ResolvedDeployedSelfModificationConfig;
|
|
33
47
|
}
|
|
34
48
|
export interface ResolvedDeployedSelfModificationConfig {
|
|
49
|
+
readonly authorize: SelfModificationAuthorization;
|
|
35
50
|
readonly credentials: ResolvedGitHubCredentials;
|
|
36
51
|
readonly directory: string;
|
|
37
52
|
readonly repository: GitHubRepository;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assertGitRef,assertRepositoryPart}from"./identifiers.js";function defineSelfModificationConfig(e={}){return resolveSelfModificationConfig(e),e}function resolveSelfModificationConfig(e={}){if(!isRecord(e))throw Error(`Self-modification configuration must be an object.`);let t=e.local;if(t!==void 0&&!isRecord(t))throw Error(`Self-modification local must be an object.`);let n=t?.enabled??!0;if(typeof n!=`boolean`)throw Error(`Self-modification local.enabled must be a boolean.`);let r=e.deployed;if(r===void 0)return{localEnabled:n};if(!isRecord(r))throw Error(`Self-modification deployed must be an object.`);let{source:i,target:a,credentials:
|
|
1
|
+
import{assertGitRef,assertRepositoryPart}from"./identifiers.js";function defineSelfModificationConfig(e={}){return resolveSelfModificationConfig(e),e}function resolveSelfModificationConfig(e={}){if(!isRecord(e))throw Error(`Self-modification configuration must be an object.`);let t=e.local;if(t!==void 0&&!isRecord(t))throw Error(`Self-modification local must be an object.`);let n=t?.enabled??!0;if(typeof n!=`boolean`)throw Error(`Self-modification local.enabled must be a boolean.`);let r=e.deployed;if(r===void 0)return{localEnabled:n};if(!isRecord(r))throw Error(`Self-modification deployed must be an object.`);let{source:i,target:a,authorize:o,credentials:s}=r;if(i===void 0||a===void 0||o===void 0)throw Error(`Self-modification deployed requires source, target, and authorization configuration.`);if(!isRecord(i))throw Error(`Self-modification deployed.source must be an object.`);if(!isRecord(i.git))throw Error(`Self-modification deployed.source.git must be an object.`);if(!isRecord(a))throw Error(`Self-modification deployed.target must be an object.`);if(typeof o!=`function`)throw Error(`Self-modification deployed.authorize must be a function.`);let{git:c}=i;if(typeof c.repository!=`string`||typeof c.directory!=`string`)throw Error(`Self-modification deployed.source.git.repository and deployed.source.git.directory must be strings.`);if(typeof a.branch!=`string`)throw Error(`Self-modification deployed.target.branch must be a string.`);return{deployed:{authorize:o,credentials:parseCredentials(s),directory:parseDirectory(c.directory),repository:parseGitHubRepository(c.repository),targetBranch:parseBranch(a.branch)},localEnabled:n}}function parseCredentials(e){if(!isRecord(e))throw Error(`Self-modification deployed.credentials must explicitly configure Vercel Connect or the self-hosted PAT exception.`);if(e.vercelConnect!==void 0==(e.pat!==void 0))throw Error(`Self-modification deployed.credentials must configure exactly one of vercelConnect or pat.`);if(e.pat!==void 0){if(e.pat!==!0)throw Error(`Self-modification deployed.credentials.pat must be true.`);return{kind:`pat`}}if(!isRecord(e.vercelConnect))throw Error(`Self-modification deployed.credentials.vercelConnect must be an object.`);let t=e.vercelConnect.connector;if(typeof t!=`string`||t.trim().length===0)throw Error(`Self-modification deployed.credentials.vercelConnect.connector must be a string.`);return{connector:t.trim(),kind:`vercel-connect`}}function parseGitHubRepository(e){let t=/^github\.com\/([^/]+)\/([^/]+)$/u.exec(e);if(t?.[1]===void 0||t[2]===void 0)throw Error(`Self-modification deployed.source.git.repository must use github.com/owner/repo form.`);return assertRepositoryPart(t[1],`repository owner`),assertRepositoryPart(t[2],`repository name`),{owner:t[1],repo:t[2]}}function parseDirectory(e){if(e!==`.`&&(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||e.split(`/`).some(e=>e===``||e===`.`||e===`..`)))throw Error(`Self-modification deployed.source.git.directory must be a safe repository-relative path.`);return e}function parseBranch(e){if(assertGitRef(e,`target branch`),e.startsWith(`refs/`))throw Error(`Self-modification deployed.target.branch must be a branch name, not a full Git ref.`);return e}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{defineSelfModificationConfig,resolveSelfModificationConfig};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import type { SelfModificationAuthorization } from "../config.js";
|
|
2
3
|
export declare const selfModificationConfigSchema: z.ZodObject<{
|
|
3
4
|
local: z.ZodOptional<z.ZodObject<{
|
|
4
5
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -19,6 +20,7 @@ export declare const selfModificationConfigSchema: z.ZodObject<{
|
|
|
19
20
|
target: z.ZodObject<{
|
|
20
21
|
branch: z.ZodString;
|
|
21
22
|
}, z.core.$strip>;
|
|
23
|
+
authorize: z.ZodCustom<SelfModificationAuthorization, SelfModificationAuthorization>;
|
|
22
24
|
}, z.core.$strip>>;
|
|
23
25
|
}, z.core.$strip>;
|
|
24
26
|
/** Extension mount configured with the same policy as the agent and sandbox. */
|
|
@@ -42,6 +44,7 @@ declare const _default: import("eve/extension").ExtensionHandle<z.ZodObject<{
|
|
|
42
44
|
target: z.ZodObject<{
|
|
43
45
|
branch: z.ZodString;
|
|
44
46
|
}, z.core.$strip>;
|
|
47
|
+
authorize: z.ZodCustom<SelfModificationAuthorization, SelfModificationAuthorization>;
|
|
45
48
|
}, z.core.$strip>>;
|
|
46
49
|
}, z.core.$strip>>;
|
|
47
50
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{boolean,literal,object,string}from"../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/v4/classic/schemas.js";import"../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/index.js";import{defineExtension}from"eve/extension";const selfModificationConfigSchema=object({local:object({enabled:boolean().optional()}).optional(),deployed:object({credentials:object({pat:literal(!0).optional(),vercelConnect:object({connector:string()}).optional()}).optional(),source:object({git:object({directory:string(),repository:string()})}),target:object({branch:string()})}).optional()});var extension_default=defineExtension({config:selfModificationConfigSchema});export{extension_default as default,selfModificationConfigSchema};
|
|
1
|
+
import{boolean,custom,literal,object,string}from"../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/v4/classic/schemas.js";import"../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/index.js";import{defineExtension}from"eve/extension";const selfModificationConfigSchema=object({local:object({enabled:boolean().optional()}).optional(),deployed:object({credentials:object({pat:literal(!0).optional(),vercelConnect:object({connector:string()}).optional()}).optional(),source:object({git:object({directory:string(),repository:string()})}),target:object({branch:string()}),authorize:custom(e=>typeof e==`function`)}).optional()});var extension_default=defineExtension({config:selfModificationConfigSchema});export{extension_default as default,selfModificationConfigSchema};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { relative } from "node:path";
|
|
2
2
|
export interface SelfModificationSetupValues {
|
|
3
3
|
readonly branch: string;
|
|
4
|
+
readonly channelNames: readonly string[];
|
|
4
5
|
readonly connector: string;
|
|
5
6
|
readonly directory: string;
|
|
6
7
|
readonly repository: string;
|
|
8
|
+
readonly vercelBackend: boolean;
|
|
7
9
|
}
|
|
8
10
|
export interface DetectedGitRepository {
|
|
9
11
|
readonly branch?: string;
|
|
@@ -14,6 +16,7 @@ export interface DetectedGitRepository {
|
|
|
14
16
|
}
|
|
15
17
|
export interface SelfModificationSetupOperations {
|
|
16
18
|
attachConnector(connector: string): Promise<void>;
|
|
19
|
+
detectChannelNames(): Promise<readonly string[]>;
|
|
17
20
|
detectGitRepository(): Promise<DetectedGitRepository>;
|
|
18
21
|
findOrCreateConnector(name: string): Promise<string>;
|
|
19
22
|
readConfig(): Promise<string | undefined>;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import{SELF_MODIFICATION_CONFIG_PATH}from"./git-workspace.js";import{createHash}from"node:crypto";import{readFile,writeFile}from"node:fs/promises";import{join,relative}from"node:path";import{execFile}from"node:child_process";import{promisify}from"node:util";const runFile=promisify(execFile);function connectorName(e,t){return`selfmod-${e}-${t}`.toLowerCase().replaceAll(/[^a-z0-9-]/gu,`-`)}function renderSelfModificationConfig(e){if(e===void 0)return`import { defineSelfModificationConfig } from "eve/self-modification/config";
|
|
1
|
+
import{SELF_MODIFICATION_CONFIG_PATH}from"./git-workspace.js";import{createHash}from"node:crypto";import{readFile,writeFile}from"node:fs/promises";import{join,relative}from"node:path";import{discoverAgent}from"#discover/discover-agent.js";import{execFile}from"node:child_process";import{promisify}from"node:util";import{stripLogicalPathExtension}from"#discover/filesystem.js";const runFile=promisify(execFile);function connectorName(e,t){return`selfmod-${e}-${t}`.toLowerCase().replaceAll(/[^a-z0-9-]/gu,`-`)}function renderSelfModificationConfig(e){if(e===void 0)return`import { defineSelfModificationConfig } from "eve/self-modification/config";
|
|
2
2
|
|
|
3
3
|
export default defineSelfModificationConfig({});
|
|
4
|
-
`;let t
|
|
4
|
+
`;let t=[...new Set(e.channelNames)].filter(e=>e!==`eve`).sort(),n=(e.vercelBackend?t:[]).map(e=>` case ${JSON.stringify(`channel:${e}`)}:
|
|
5
|
+
// Authorize trusted principals for this channel before returning true.
|
|
6
|
+
return false;`).join(`
|
|
7
|
+
`),r=`${e.vercelBackend?` case "http": {
|
|
8
|
+
const projectId = process.env.VERCEL_PROJECT_ID;
|
|
9
|
+
return (
|
|
10
|
+
projectId !== undefined &&
|
|
11
|
+
projectId.length > 0 &&
|
|
12
|
+
principal?.authenticator === "oidc" &&
|
|
13
|
+
(principal.issuer === "https://oidc.vercel.com" ||
|
|
14
|
+
principal.issuer?.startsWith("https://oidc.vercel.com/") === true) &&
|
|
15
|
+
principal.attributes.project_id === projectId
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
`:``}${n}`,i=`import { defineSelfModificationConfig } from "eve/self-modification/config";
|
|
5
19
|
|
|
6
20
|
export default defineSelfModificationConfig({
|
|
7
21
|
deployed: {
|
|
@@ -15,8 +29,15 @@ export default defineSelfModificationConfig({
|
|
|
15
29
|
credentials: {
|
|
16
30
|
vercelConnect: { connector: ${JSON.stringify(e.connector)} },
|
|
17
31
|
},
|
|
32
|
+
authorize: ({ channel, principal }) => {
|
|
33
|
+
switch (channel.kind) {
|
|
34
|
+
${r} default:
|
|
35
|
+
// Add another branch when you add a trusted channel.
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
18
39
|
},
|
|
19
40
|
});
|
|
20
|
-
`;return`// eve-self-modification: generated-v1 digest:${createHash(`sha256`).update(
|
|
41
|
+
`;return`// eve-self-modification: generated-v1 digest:${createHash(`sha256`).update(i).digest(`hex`)}\n${i}`}function classifySelfModificationConfig(e){if(e===void 0)return`missing`;if(e===renderSelfModificationConfig())return`local`;let[t,...n]=e.split(`
|
|
21
42
|
`);return/^\/\/ eve-self-modification: generated-v1 digest:([a-f0-9]{64})$/u.exec(t??``)?.[1]===createHash(`sha256`).update(n.join(`
|
|
22
|
-
`)).digest(`hex`)?`generated`:`authored`}function parseGitHubRemote(e){let t=e.trim().match(/^(?:https?:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+)\/([^/]+?)(?:\.git)?$/u);return t?.[1]===void 0||t[2]===void 0?void 0:{owner:t[1],repo:t[2]}}function defaultSelfModificationSetupOperations(e){let t=join(e,SELF_MODIFICATION_CONFIG_PATH);return{async detectGitRepository(){let t=await gitOutput(e,[`config`,`--get`,`remote.origin.url`]),n=t===void 0?void 0:parseGitHubRemote(t),r=await gitOutput(e,[`symbolic-ref`,`--quiet`,`--short`,`refs/remotes/origin/HEAD`]),i=await gitOutput(e,[`rev-parse`,`--show-toplevel`]);return{...n,branch:r?.replace(/^origin\//u,``),directory:i===void 0?void 0:repositoryRelativeDirectory(i,e),remoteKind:t===void 0?`missing`:n===void 0?`other`:`github`}},async readConfig(){try{return await readFile(t,`utf8`)}catch(e){if(e.code===`ENOENT`)return;throw e}},writeConfig:e=>writeFile(t,e,`utf8`),async findOrCreateConnector(t){let n=await vercel(e,[`connect`,`list`,`-F`,`json`]);if(!n.ok)throw Error(`Vercel Connect requires an authenticated Vercel CLI linked to this project.`);let r=parseConnectors(n.stdout),i=`github/${t}`,a=r.find(e=>e.uid===i);if(a!==void 0){if(a.type!==`github`)throw Error(`The existing connector ${i} is not a GitHub connector.`);return a.uid}let o=await vercel(e,[`connect`,`create`,`github`,`--name`,t,`-F`,`json`]),s=o.ok?parseCreatedConnector(o.stdout):void 0;if(s===void 0||!s.startsWith(`github/`))throw Error(`Could not create a GitHub Vercel Connect connector.`);return s},async attachConnector(t){let n=await readLinkedProject(e);if(n===void 0)throw Error("Vercel Connect requires this directory to be linked to a Vercel project. Run `vercel link` and retry.");if(!(await vercel(e,[`connect`,`attach`,t,`--project`,n.projectId,`--environment`,`production`,`--yes`])).ok)throw Error(`Could not attach ${t} to the linked Vercel project for Production.`)}}}function parseCreatedConnector(e){try{let t=JSON.parse(e);return typeof t.uid==`string`?t.uid:void 0}catch{return}}function parseConnectors(e){try{let t=JSON.parse(e);return(Array.isArray(t.connectors)?t.connectors:[t]).flatMap(e=>typeof e==`object`&&e&&typeof e.uid==`string`&&typeof e.type==`string`?[{uid:e.uid,type:e.type}]:[])}catch{return[]}}async function vercel(e,t){try{return{ok:!0,stdout:(await runFile(`vercel`,t,{cwd:e})).stdout}}catch{return{ok:!1,stdout:``}}}async function readLinkedProject(e){try{let t=JSON.parse(await readFile(join(e,`.vercel`,`project.json`),`utf8`));return typeof t.projectId==`string`?{projectId:t.projectId}:void 0}catch{return}}function repositoryRelativeDirectory(e,t,n=relative){return n(e,t).replaceAll(`\\`,`/`)||`.`}function repositoryPartError(e){return/^[A-Za-z0-9_.-]+$/u.test(e)?void 0:`Enter a valid GitHub owner or repository name.`}function directoryError(e){return e===`.`||e.length>0&&!e.startsWith(`/`)&&!e.includes(`\\`)&&e.split(`/`).every(e=>e!==``&&e!==`.`&&e!==`..`)?void 0:`Enter a safe repository-relative directory or ".".`}function gitRefError(e){return/^[A-Za-z0-9][A-Za-z0-9._/-]*$/u.test(e)&&!e.endsWith(`.`)&&!e.endsWith(`/`)&&!e.includes(`..`)&&!e.includes(`//`)&&!e.includes(`@{`)?void 0:`Enter a valid branch name.`}async function gitOutput(e,t){try{return(await runFile(`git`,t,{cwd:e})).stdout.trim()||void 0}catch{return}}export{classifySelfModificationConfig,connectorName,defaultSelfModificationSetupOperations,directoryError,gitRefError,parseGitHubRemote,renderSelfModificationConfig,repositoryPartError,repositoryRelativeDirectory};
|
|
43
|
+
`)).digest(`hex`)?`generated`:`authored`}function parseGitHubRemote(e){let t=e.trim().match(/^(?:https?:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+)\/([^/]+?)(?:\.git)?$/u);return t?.[1]===void 0||t[2]===void 0?void 0:{owner:t[1],repo:t[2]}}function defaultSelfModificationSetupOperations(e){let t=join(e,SELF_MODIFICATION_CONFIG_PATH);return{async detectChannelNames(){return(await discoverAgent({appRoot:e,agentRoot:join(e,`agent`)})).manifest.channels.map(e=>stripLogicalPathExtension(e.logicalPath).replace(/^channels\//,``))},async detectGitRepository(){let t=await gitOutput(e,[`config`,`--get`,`remote.origin.url`]),n=t===void 0?void 0:parseGitHubRemote(t),r=await gitOutput(e,[`symbolic-ref`,`--quiet`,`--short`,`refs/remotes/origin/HEAD`]),i=await gitOutput(e,[`rev-parse`,`--show-toplevel`]);return{...n,branch:r?.replace(/^origin\//u,``),directory:i===void 0?void 0:repositoryRelativeDirectory(i,e),remoteKind:t===void 0?`missing`:n===void 0?`other`:`github`}},async readConfig(){try{return await readFile(t,`utf8`)}catch(e){if(e.code===`ENOENT`)return;throw e}},writeConfig:e=>writeFile(t,e,`utf8`),async findOrCreateConnector(t){let n=await vercel(e,[`connect`,`list`,`-F`,`json`]);if(!n.ok)throw Error(`Vercel Connect requires an authenticated Vercel CLI linked to this project.`);let r=parseConnectors(n.stdout),i=`github/${t}`,a=r.find(e=>e.uid===i);if(a!==void 0){if(a.type!==`github`)throw Error(`The existing connector ${i} is not a GitHub connector.`);return a.uid}let o=await vercel(e,[`connect`,`create`,`github`,`--name`,t,`-F`,`json`]),s=o.ok?parseCreatedConnector(o.stdout):void 0;if(s===void 0||!s.startsWith(`github/`))throw Error(`Could not create a GitHub Vercel Connect connector.`);return s},async attachConnector(t){let n=await readLinkedProject(e);if(n===void 0)throw Error("Vercel Connect requires this directory to be linked to a Vercel project. Run `vercel link` and retry.");if(!(await vercel(e,[`connect`,`attach`,t,`--project`,n.projectId,`--environment`,`production`,`--yes`])).ok)throw Error(`Could not attach ${t} to the linked Vercel project for Production.`)}}}function parseCreatedConnector(e){try{let t=JSON.parse(e);return typeof t.uid==`string`?t.uid:void 0}catch{return}}function parseConnectors(e){try{let t=JSON.parse(e);return(Array.isArray(t.connectors)?t.connectors:[t]).flatMap(e=>typeof e==`object`&&e&&typeof e.uid==`string`&&typeof e.type==`string`?[{uid:e.uid,type:e.type}]:[])}catch{return[]}}async function vercel(e,t){try{return{ok:!0,stdout:(await runFile(`vercel`,t,{cwd:e})).stdout}}catch{return{ok:!1,stdout:``}}}async function readLinkedProject(e){try{let t=JSON.parse(await readFile(join(e,`.vercel`,`project.json`),`utf8`));return typeof t.projectId==`string`?{projectId:t.projectId}:void 0}catch{return}}function repositoryRelativeDirectory(e,t,n=relative){return n(e,t).replaceAll(`\\`,`/`)||`.`}function repositoryPartError(e){return/^[A-Za-z0-9_.-]+$/u.test(e)?void 0:`Enter a valid GitHub owner or repository name.`}function directoryError(e){return e===`.`||e.length>0&&!e.startsWith(`/`)&&!e.includes(`\\`)&&e.split(`/`).every(e=>e!==``&&e!==`.`&&e!==`..`)?void 0:`Enter a safe repository-relative directory or ".".`}function gitRefError(e){return/^[A-Za-z0-9][A-Za-z0-9._/-]*$/u.test(e)&&!e.endsWith(`.`)&&!e.endsWith(`/`)&&!e.includes(`..`)&&!e.includes(`//`)&&!e.includes(`@{`)?void 0:`Enter a valid branch name.`}async function gitOutput(e,t){try{return(await runFile(`git`,t,{cwd:e})).stdout.trim()||void 0}catch{return}}export{classifySelfModificationConfig,connectorName,defaultSelfModificationSetupOperations,directoryError,gitRefError,parseGitHubRemote,renderSelfModificationConfig,repositoryPartError,repositoryRelativeDirectory};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineSetupIntegration}from"../types.js";import{describeIntegrationSetupEnvironment}from"../shared/environment.js";import{confirm,text}from"#setup/ask.js";import{classifySelfModificationConfig,connectorName,defaultSelfModificationSetupOperations,directoryError,gitRefError,renderSelfModificationConfig,repositoryPartError}from"#self-modification/setup.js";import{SELF_MODIFICATION_CONFIG_PATH}from"#self-modification/git-workspace.js";function validationResult(e){return e??null}async function prepareSelfModificationSetup(e,t=defaultSelfModificationSetupOperations(e.appRoot)){let n=await t.readConfig();if(classifySelfModificationConfig(n)===`authored`)return e.presenter.note(`The existing ${SELF_MODIFICATION_CONFIG_PATH} contains authored configuration and was not overwritten.`,`Manual update required`,{tone:`warning`}),{kind:`authored`};let
|
|
1
|
+
import{defineSetupIntegration}from"../types.js";import{describeIntegrationSetupEnvironment}from"../shared/environment.js";import{confirm,text}from"#setup/ask.js";import{classifySelfModificationConfig,connectorName,defaultSelfModificationSetupOperations,directoryError,gitRefError,renderSelfModificationConfig,repositoryPartError}from"#self-modification/setup.js";import{SELF_MODIFICATION_CONFIG_PATH}from"#self-modification/git-workspace.js";function validationResult(e){return e??null}async function prepareSelfModificationSetup(e,t=defaultSelfModificationSetupOperations(e.appRoot)){let n=await t.readConfig();if(classifySelfModificationConfig(n)===`authored`)return e.presenter.note(`The existing ${SELF_MODIFICATION_CONFIG_PATH} contains authored configuration and was not overwritten.`,`Manual update required`,{tone:`warning`}),{kind:`authored`};let[r,i]=await Promise.all([t.detectGitRepository(),t.detectChannelNames()]),a=await e.asker.ask(text({key:`self-modification-repository-owner`,message:`GitHub repository owner`,detected:r.owner,required:!0,validate:e=>validationResult(repositoryPartError(e))})),o=await e.asker.ask(text({key:`self-modification-repository-name`,message:`GitHub repository name`,detected:r.repo,required:!0,validate:e=>validationResult(repositoryPartError(e))})),s=await e.asker.ask(text({key:`self-modification-repository-directory`,message:`Application directory relative to the repository root`,detected:r.directory??`.`,required:!0,validate:e=>validationResult(directoryError(e))})),c=await e.asker.ask(text({key:`self-modification-target-branch`,message:`Target branch`,detected:r.branch??`main`,required:!0,validate:e=>validationResult(gitRefError(e))})),l=connectorName(a,o),u={branch:c,channelNames:i,connector:`github/${l}`,directory:s,repository:`github.com/${a}/${o}`,vercelBackend:e.environment.vercel.kind===`available`&&e.environment.vercel.project.kind!==`unresolved`};return e.presenter.note(renderSelfModificationConfig(u),`Generated ${SELF_MODIFICATION_CONFIG_PATH}`),e.presenter.note(`Vercel Connect will issue short-lived GitHub App credentials restricted to this repository. Install the managed GitHub App and select only this repository. Review, merge, and deployment remain separate operator boundaries.`,`Security summary`),await e.asker.ask(confirm({key:`self-modification-confirm`,message:`Create or attach this GitHub connector and write this configuration?`,recommended:!1,required:!0}))?{kind:`deployed`,connectorName:l,values:u}:{kind:`local`}}async function applySelfModificationSetup(e,t,n=defaultSelfModificationSetupOperations(t.appRoot)){if(e.kind===`authored`)return{facts:[{label:`Self-modification`,value:`manual configuration update required`}]};if(e.kind===`local`)return{facts:[{label:`Self-modification`,value:`local editing`}]};let r=await n.findOrCreateConnector(e.connectorName);return await n.attachConnector(r),await n.writeConfig(renderSelfModificationConfig({...e.values,connector:r})),t.presenter.log.success(`Updated ${SELF_MODIFICATION_CONFIG_PATH}.`),t.presenter.nextSteps([`Install the managed GitHub App for the configured repository, then deploy or redeploy.`,e.values.vercelBackend?"After deployment, try self-modification by running `eve dev <deployment-url>` from this linked project. The generated policy admits its Vercel OIDC identity over HTTP; configured channels remain denied until you update `agent/subagents/self-modification/config.ts`.":"Before deployment, configure `deployed.authorize` in `agent/subagents/self-modification/config.ts` to admit a trusted principal for your deployment's channel. After deployment, use that channel to try self-modification."]),{deploymentRequired:!0,facts:[{label:`Repository`,value:e.values.repository},{label:`Application directory`,value:e.values.directory},{label:`Target branch`,value:e.values.branch},{label:`Credential`,value:r}]}}async function prepareLocalSelfModificationSetup(e,t=defaultSelfModificationSetupOperations(e.appRoot)){let n=await t.readConfig();return classifySelfModificationConfig(n)===`authored`?(e.presenter.note(`The existing ${SELF_MODIFICATION_CONFIG_PATH} contains authored configuration and was not overwritten.`,`Manual update required`,{tone:`warning`}),{kind:`authored`}):{kind:`local`}}function describeEnvironment(e){if(e.vercel.kind===`available`)return describeIntegrationSetupEnvironment(e);switch(e.vercel.reason){case`logged-out`:return`No authenticated Vercel account found. Local editing remains available; deployed proposals require Vercel Connect.`;case`cli-missing`:return`Vercel CLI not found. Local editing remains available; deployed proposals require Vercel Connect.`;case`unavailable`:return`Could not verify the Vercel account. Local editing remains available; deployed proposals require Vercel Connect.`}}const SELF_MODIFICATION_SETUP=defineSetupIntegration({kind:`self-modification`,label:`Self-modification`,hint:`Local source editing is enabled`,describeEnvironment,prepare:prepareLocalSelfModificationSetup,apply:applySelfModificationSetup}),SELF_MODIFICATION_PRODUCTION_SETUP=defineSetupIntegration({kind:`self-modification-production`,label:`Self-modification production`,hint:`Configure deployed draft pull requests`,describeEnvironment,prepare:prepareSelfModificationSetup,apply:applySelfModificationSetup});export{SELF_MODIFICATION_PRODUCTION_SETUP,SELF_MODIFICATION_SETUP,applySelfModificationSetup,prepareLocalSelfModificationSetup,prepareSelfModificationSetup};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"../update/package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{readFile}from"node:fs/promises";import{join}from"node:path";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(e){let t=e.packageManager??`pnpm`,n=join(e.projectRoot,`package.json`);if(!await pathExists(n))throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let r;try{r=JSON.parse(await readFile(n,`utf8`))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Cannot add an eve agent because "${n}" is not valid JSON. No files were changed. Fix the file, then retry eve init. ${t}`)}let i=agentTemplateFiles(e.model,e.reasoning),a=[];for(let t of Object.keys(i))await pathExists(join(e.projectRoot,t))&&a.push(t);if(a.length===0&&await pathExists(join(e.projectRoot,`agent`))&&a.push(`agent/`),a.length>0)throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it already has: ${a.join(`, `)}.`);let o=resolveEvePackageContract(e.evePackage),s=resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.
|
|
1
|
+
import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"../update/package-json.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{readFile}from"node:fs/promises";import{join}from"node:path";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(e){let t=e.packageManager??`pnpm`,n=join(e.projectRoot,`package.json`);if(!await pathExists(n))throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let r;try{r=JSON.parse(await readFile(n,`utf8`))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Cannot add an eve agent because "${n}" is not valid JSON. No files were changed. Fix the file, then retry eve init. ${t}`)}let i=agentTemplateFiles(e.model,e.reasoning),a=[];for(let t of Object.keys(i))await pathExists(join(e.projectRoot,t))&&a.push(t);if(a.length===0&&await pathExists(join(e.projectRoot,`agent`))&&a.push(`agent/`),a.length>0)throw Error(`Cannot add an eve agent to "${e.projectRoot}" because it already has: ${a.join(`, `)}.`);let o=resolveEvePackageContract(e.evePackage),s=resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.93`),c=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`1.0.0`),l=resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),u=[];for(let[t,n]of Object.entries(i)){let r=join(e.projectRoot,t);await writeTextFile(r,n),u.push(r)}let d={"@vercel/connect":c,ai:s,eve:formatEveDependencySpecifier(o.version),zod:l},f={};for(let[e,t]of Object.entries(d))hasDeclaredDependency(r,e)||(f[e]=t);let p={};Object.keys(f).length>0&&(p.dependencies=f),isPackageManagerWorkspaceMember(t,e.projectRoot)||(p.nodeEngineRequirement=o.nodeEngine);let m=await patchPackageJson(n,p),h=await patchWorkspaceRootPackageJson(t,e.projectRoot,{nodeEngineRequirement:o.nodeEngine}),g=h.nodeEngineOverride??m.nodeEngineOverride,_=await applyPackageManagerWorkspaceConfiguration({packageManager:t,projectRoot:e.projectRoot});return{filesWritten:u,dependenciesAdded:Object.keys(f).sort(),nodeEngineOverride:g,configurationFilesChanged:[...m.changed?[n]:[],...h.changed&&h.path!==void 0?[h.path]:[],..._.filesWritten]}}export{addAgentToProject};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.
|
|
1
|
+
import{pinnedNodeEngineMajor}from"../../node-engine.js";import{parseChatGptModelSelection}from"../../../shared/chatgpt-model.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{blockingCreateInPlaceEntries}from"../create-in-place.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{mkdir,readdir,stat}from"node:fs/promises";import{basename,join,resolve}from"node:path";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,DEFAULT_AI_PACKAGE_VERSION=`^7.0.93`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.5.4`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.53.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e,t){return{"agent/agent.ts":renderAgentTemplate(e,t),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderAgentTemplate(e,t){let n=parseChatGptModelSelection(e);return n===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(n)}),\n${reasoningTemplateLine(t)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,t){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(t.model)!==void 0?renderAgentTemplate(t.model,t.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(t.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -117,4 +117,4 @@ __EVE_INIT_REASONING__ modelOptions: {
|
|
|
117
117
|
},
|
|
118
118
|
},
|
|
119
119
|
});
|
|
120
|
-
`:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(e.includeRootOnlyPackageJsonFields)}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let n=await readdir(e),r=blockingCreateInPlaceEntries(n);if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let t=resolve(e.targetDirectory??process.cwd(),e.projectName),n=e.projectName===`.`,r=e.overwriteExisting??!1,i=e.byokProvider??!1,a=e.packageManager??`pnpm`,o=resolveEvePackageContract(e.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c=resolve(e.workspaceProbeDirectory??t),l=isPackageManagerWorkspaceMember(a,c);if(n)await assertCanCreateInPlace(t,r);else if(await pathExists(t))throw Error(`Cannot create project because "${t}" already exists.`);let u={appName:basename(t),model:e.model,reasoning:e.reasoning,eveVersion:o.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.
|
|
120
|
+
`:BASE_AGENT_TEMPLATE,...SHARED_TEMPLATE_FILES,"package.json":packageJsonTemplate(e.includeRootOnlyPackageJsonFields)}}async function assertCanCreateInPlace(e,t){if(!await pathExists(e))return;let n=await readdir(e),r=blockingCreateInPlaceEntries(n);if(r.length>0&&!t){let e=r.slice(0,5).join(`, `),t=r.length>5?`, and ${r.length-5} more`:``;throw Error(`Cannot create project in current directory because it is not empty. Found: ${e}${t}. Use an empty directory.`)}}async function scaffoldBaseProject(e){let t=resolve(e.targetDirectory??process.cwd(),e.projectName),n=e.projectName===`.`,r=e.overwriteExisting??!1,i=e.byokProvider??!1,a=e.packageManager??`pnpm`,o=resolveEvePackageContract(e.evePackage),s=pinnedNodeEngineMajor(o.nodeEngine),c=resolve(e.workspaceProbeDirectory??t),l=isPackageManagerWorkspaceMember(a,c);if(n)await assertCanCreateInPlace(t,r);else if(await pathExists(t))throw Error(`Cannot create project because "${t}" already exists.`);let u={appName:basename(t),model:e.model,reasoning:e.reasoning,eveVersion:o.version,aiPackageVersion:resolveVersionToken(`aiPackageVersion`,e.aiPackageVersion??`^7.0.93`),connectPackageVersion:resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`1.0.0`),zodPackageVersion:resolveVersionToken(`zodPackageVersion`,e.zodPackageVersion??`4.5.4`),typescriptPackageVersion:resolveVersionToken(`typescriptPackageVersion`,e.typescriptPackageVersion??`7.0.2`),nodeTypesVersion:s,nodeEngine:s};await mkdir(t,{recursive:!0});for(let[a,o]of Object.entries(templateFiles({byokProvider:i,includeRootOnlyPackageJsonFields:!l}))){let i=`${t}/${a}`,s=await pathExists(i);await writeTextFile(i,renderTemplate(o,u),{force:n&&r}),s&&await e.onOverwriteFile?.(i)}return await applyPackageManagerWorkspaceConfiguration({packageManager:a,projectRoot:t,workspaceProbeRoot:c,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),await patchWorkspaceRootPackageJson(a,c,{nodeEngineRequirement:o.nodeEngine,onWorkspaceRootMutation:e.onWorkspaceRootMutation}),t}async function isEveProject(e){for(let t of SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS)try{return await stat(join(e,`agent`,`agent${t}`)),!0}catch{}return!1}export{CURRENT_DIRECTORY_PROJECT_NAME,DEFAULT_AI_PACKAGE_VERSION,DEFAULT_CONNECT_PACKAGE_VERSION,DEFAULT_EVE_PACKAGE_CONTRACT,DEFAULT_ZOD_PACKAGE_VERSION,ROOT_ONLY_PACKAGE_JSON_TEMPLATE_SUFFIX,agentTemplateFiles,byokProviderEnvVar,formatEveDependencySpecifier,isEveProject,modelProviderSlug,resolveEvePackageContract,scaffoldBaseProject};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_EVE_PACKAGE_CONTRACT}from"../create/project.js";function resolveWebPackageVersions(e,t){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`^7.0.
|
|
1
|
+
import{DEFAULT_EVE_PACKAGE_CONTRACT}from"../create/project.js";function resolveWebPackageVersions(e,t){return{evePackage:e?.evePackage??DEFAULT_EVE_PACKAGE_CONTRACT,aiPackageVersion:e?.aiPackageVersion??`^7.0.93`,betterAuthPackageVersion:e?.betterAuthPackageVersion??`1.6.26`,nextPackageVersion:e?.nextPackageVersion??(t===`sign-in-with-vercel`?`16.3.0`:`16.3.4`),reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.5.4`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}export{resolveWebPackageVersions};
|
|
@@ -194,8 +194,7 @@ export interface AgentExperimentalDefinition {
|
|
|
194
194
|
*/
|
|
195
195
|
readonly tasks?: boolean;
|
|
196
196
|
/**
|
|
197
|
-
* Durable Workflow runtime configuration.
|
|
198
|
-
* the Workflow world backing sessions and runs.
|
|
197
|
+
* Durable Workflow runtime configuration.
|
|
199
198
|
*/
|
|
200
199
|
readonly workflow?: AgentWorkflowDefinition;
|
|
201
200
|
}
|
|
@@ -229,6 +228,16 @@ export type AgentWorkflowWorldDefinition = string;
|
|
|
229
228
|
* Advanced durable-runtime configuration for eve's Workflow SDK integration.
|
|
230
229
|
*/
|
|
231
230
|
export interface AgentWorkflowDefinition {
|
|
231
|
+
/**
|
|
232
|
+
* Maximum number of turn-model calls eve may run inside one durable Workflow step.
|
|
233
|
+
*
|
|
234
|
+
* Values greater than one reduce Workflow checkpoint overhead but widen the
|
|
235
|
+
* replay unit: if the Workflow step is interrupted, earlier model calls and
|
|
236
|
+
* inline tool executions in the same step may run again.
|
|
237
|
+
*
|
|
238
|
+
* @default 1
|
|
239
|
+
*/
|
|
240
|
+
readonly modelCallsPerStep?: number;
|
|
232
241
|
/**
|
|
233
242
|
* Workflow world module used for durable workflow storage, queueing, hooks,
|
|
234
243
|
* and streaming.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { SessionStateMap } from "#harness/types.js";
|
|
2
2
|
export declare const TASK_DELIVERY_CONTEXT_LABEL = "[Task state]";
|
|
3
|
-
export declare const TASK_DELIVERY_INITIATING_INSTRUCTION = "Background task reporting: launch acknowledgement\nThe
|
|
4
|
-
export declare const TASK_DELIVERY_PENDING_INSTRUCTION = "Background task control: incomplete cohort\nThis framework-authored instruction overrides any earlier instruction to report, summarize, acknowledge, or otherwise handle background results.\n\nThe accompanying [Task state] message is runtime-authored and lists tasks started by the same parent turn. At least one of those tasks is still pending, so the combined report is not ready.\n\nAction:\n- You may call tools only if the newly delivered task result requires immediate action.\n- Otherwise, take no action.\n\nDelivery:\n- Do not report completed tasks or partial results.\n- Do not provide progress, status, an acknowledgement, or a waiting message.\n- After any necessary tool calls, your entire final text response must be exactly <eve-empty-delivery/> and no other text.\n\nIncorrect: \"Two of three tasks have completed.\"\nIncorrect: \"Still waiting for the remaining task.\"\nCorrect: <eve-empty-delivery/>";
|
|
3
|
+
export declare const TASK_DELIVERY_INITIATING_INSTRUCTION = "Background task reporting: launch acknowledgement\nThe latest [Task state] message is runtime-authored and lists background tasks accepted so far from the current turn. They continue independently after this turn.\n\nContinue carrying out the user's request, including starting any remaining background work. When no further tool calls are needed in this turn, send one brief user-facing acknowledgement that the background work has started. Do not wait for results or report results that are not available yet. End the turn after the acknowledgement.";
|
|
5
4
|
export declare const TASK_DELIVERY_SETTLED_INSTRUCTION = "Background task reporting\nThis turn was triggered by background task activity. The accompanying [Task state] message is runtime-authored and lists tasks started by the same parent turn, all settled, with every available terminal output. Do not reply with <eve-empty-delivery/>. Send one user-facing response that combines their useful results.";
|
|
6
5
|
/** Returns model context and cohort phase for tasks started by the same parent turn as this delivery. */
|
|
7
6
|
export declare function resolveTaskDeliveryContext(input: {
|
|
@@ -10,6 +9,7 @@ export declare function resolveTaskDeliveryContext(input: {
|
|
|
10
9
|
}): {
|
|
11
10
|
readonly context: string;
|
|
12
11
|
readonly phase: "pending" | "settled";
|
|
12
|
+
readonly rootTurnId: string;
|
|
13
13
|
} | undefined;
|
|
14
14
|
/** Returns model context for durable tasks launched by the active parent turn. */
|
|
15
15
|
export declare function resolveInitiatingTaskContext(input: {
|
|
@@ -1,20 +1,4 @@
|
|
|
1
1
|
import{getSessionTaskIndex}from"#tasks/session-index.js";import{EMPTY_DELIVERY_SENTINEL}from"#shared/empty-delivery.js";const TASK_DELIVERY_CONTEXT_LABEL=`[Task state]`,TASK_DELIVERY_INITIATING_INSTRUCTION=`Background task reporting: launch acknowledgement
|
|
2
|
-
The
|
|
2
|
+
The latest ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists background tasks accepted so far from the current turn. They continue independently after this turn.
|
|
3
3
|
|
|
4
|
-
Continue carrying out the user's request, including starting any remaining background work. When no further tool calls are needed in this turn, send one brief user-facing acknowledgement that the background work has started. Do not wait for results or report results that are not available yet. End the turn after the acknowledgement.`,
|
|
5
|
-
This framework-authored instruction overrides any earlier instruction to report, summarize, acknowledge, or otherwise handle background results.
|
|
6
|
-
|
|
7
|
-
The accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists tasks started by the same parent turn. At least one of those tasks is still pending, so the combined report is not ready.
|
|
8
|
-
|
|
9
|
-
Action:
|
|
10
|
-
- You may call tools only if the newly delivered task result requires immediate action.
|
|
11
|
-
- Otherwise, take no action.
|
|
12
|
-
|
|
13
|
-
Delivery:
|
|
14
|
-
- Do not report completed tasks or partial results.
|
|
15
|
-
- Do not provide progress, status, an acknowledgement, or a waiting message.
|
|
16
|
-
- After any necessary tool calls, your entire final text response must be exactly ${EMPTY_DELIVERY_SENTINEL} and no other text.
|
|
17
|
-
|
|
18
|
-
Incorrect: "Two of three tasks have completed."
|
|
19
|
-
Incorrect: "Still waiting for the remaining task."
|
|
20
|
-
Correct: ${EMPTY_DELIVERY_SENTINEL}`,TASK_DELIVERY_SETTLED_INSTRUCTION=`Background task reporting\nThis turn was triggered by background task activity. The accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists tasks started by the same parent turn, all settled, with every available terminal output. Do not reply with ${EMPTY_DELIVERY_SENTINEL}. Send one user-facing response that combines their useful results.`;function resolveTaskDeliveryContext(e){let t=getSessionTaskIndex(e.state),n=t.find(t=>e.taskDeliveryId.startsWith(`${t.taskId}:`));return n===void 0?void 0:projectTaskCohort(t.filter(e=>e.createdByTurnId===n.createdByTurnId))}function resolveInitiatingTaskContext(e){let t=getSessionTaskIndex(e.state).filter(t=>t.createdByTurnId===e.turnId);if(t.some(e=>e.executor!==void 0&&e.terminalView===void 0))return{...projectTaskCohort(t),phase:`initiating`}}function projectTaskCohort(e){let t=e.every(e=>e.terminalView!==void 0),n=e.map(e=>({name:e.metadata.name,output:t?e.terminalView?.lastOutput:void 0,status:e.terminalView?.status??`pending`,taskId:e.taskId}));return{context:`${TASK_DELIVERY_CONTEXT_LABEL}\n${JSON.stringify({tasks:n})}`,phase:t?`settled`:`pending`}}export{TASK_DELIVERY_CONTEXT_LABEL,TASK_DELIVERY_INITIATING_INSTRUCTION,TASK_DELIVERY_PENDING_INSTRUCTION,TASK_DELIVERY_SETTLED_INSTRUCTION,resolveInitiatingTaskContext,resolveTaskDeliveryContext};
|
|
4
|
+
Continue carrying out the user's request, including starting any remaining background work. When no further tool calls are needed in this turn, send one brief user-facing acknowledgement that the background work has started. Do not wait for results or report results that are not available yet. End the turn after the acknowledgement.`,TASK_DELIVERY_SETTLED_INSTRUCTION=`Background task reporting\nThis turn was triggered by background task activity. The accompanying ${TASK_DELIVERY_CONTEXT_LABEL} message is runtime-authored and lists tasks started by the same parent turn, all settled, with every available terminal output. Do not reply with ${EMPTY_DELIVERY_SENTINEL}. Send one user-facing response that combines their useful results.`;function resolveTaskDeliveryContext(e){let t=getSessionTaskIndex(e.state),n=t.find(t=>e.taskDeliveryId.startsWith(`${t.taskId}:`));return n===void 0?void 0:{...projectTaskCohort(t.filter(e=>e.createdByTurnId===n.createdByTurnId)),rootTurnId:n.createdByTurnId}}function resolveInitiatingTaskContext(e){let t=getSessionTaskIndex(e.state).filter(t=>t.createdByTurnId===e.turnId);if(t.some(e=>e.executor!==void 0&&e.terminalView===void 0))return{...projectTaskCohort(t),phase:`initiating`}}function projectTaskCohort(e){let t=e.every(e=>e.terminalView!==void 0),n=e.map(e=>({name:e.metadata.name,output:t?e.terminalView?.lastOutput:void 0,status:e.terminalView?.status??`pending`,taskId:e.taskId}));return{context:`${TASK_DELIVERY_CONTEXT_LABEL}\n${JSON.stringify({tasks:n})}`,phase:t?`settled`:`pending`}}export{TASK_DELIVERY_CONTEXT_LABEL,TASK_DELIVERY_INITIATING_INSTRUCTION,TASK_DELIVERY_SETTLED_INSTRUCTION,resolveInitiatingTaskContext,resolveTaskDeliveryContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{TASK_DELIVERY_INITIATING_INSTRUCTION,
|
|
1
|
+
import{TASK_DELIVERY_INITIATING_INSTRUCTION,TASK_DELIVERY_SETTLED_INSTRUCTION}from"#tasks/delivery-context.js";import{CONDITIONAL_DELIVERY_INSTRUCTION}from"#shared/empty-delivery.js";const POLICIES={conditional:{allowsEmptyDelivery:!0,instruction:CONDITIONAL_DELIVERY_INSTRUCTION},initiating:{allowsEmptyDelivery:!1,instruction:TASK_DELIVERY_INITIATING_INSTRUCTION},normal:{allowsEmptyDelivery:!1},pending:{allowsEmptyDelivery:!0},settled:{allowsEmptyDelivery:!1,instruction:TASK_DELIVERY_SETTLED_INSTRUCTION}};function resolveDeliveryPolicy(e){return e.hasOutputSchema||e.isChild?POLICIES.normal:e.taskDeliveryPhase===`pending`?POLICIES.pending:e.taskDeliveryPhase===`settled`?POLICIES.settled:e.isFirstTurn&&e.hasScheduleProvenance?POLICIES.conditional:e.taskDeliveryPhase===`initiating`?POLICIES.initiating:POLICIES.normal}export{resolveDeliveryPolicy};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Approval } from "#approval/definition.js";
|
|
2
|
-
import type { JsonObject } from "#shared/json.js";
|
|
2
|
+
import type { JsonObject, JsonPrimitive } from "#shared/json.js";
|
|
3
3
|
export type DurableDynamicCallbackPhase = "labelComplete" | "labelDelta" | "labelStart" | "approvalKey" | "approvalRequest" | "approvalResponse" | "execute" | "toModelOutput";
|
|
4
4
|
export type DurableDynamicCallbackFn = (closure: JsonObject, ...args: never[]) => unknown;
|
|
5
5
|
/**
|
|
@@ -71,6 +71,22 @@ export declare function hasUnregisteredDurableDynamicCallbacks(metadata: readonl
|
|
|
71
71
|
resolverSlug: string;
|
|
72
72
|
entryKey: string;
|
|
73
73
|
}[], scope: Pick<DynamicToolCallbackOwner, "sessionId" | "scope">): boolean;
|
|
74
|
+
type DurableCallbackJsonValue<T> = T extends (...args: never[]) => unknown ? never : T extends JsonPrimitive ? T : T extends readonly (infer TItem)[] ? readonly DurableCallbackJsonValue<TItem>[] : T extends object ? DurableCallbackJsonObject<T> : never;
|
|
75
|
+
type DurableCallbackJsonObject<T extends object> = Extract<keyof T, symbol> extends never ? {
|
|
76
|
+
readonly [TKey in keyof T]: DurableCallbackJsonValue<Exclude<T[TKey], undefined>> | Extract<T[TKey], undefined>;
|
|
77
|
+
} : never;
|
|
78
|
+
/**
|
|
79
|
+
* Defines a replayable callback for tools created outside eve's authored-source
|
|
80
|
+
* transform, such as tools returned by a provider package.
|
|
81
|
+
*
|
|
82
|
+
* Put every per-tool value in `closure`. eve snapshots that JSON-serializable
|
|
83
|
+
* object when the dynamic tool resolves and passes it as the first argument to
|
|
84
|
+
* the current callback implementation during both live execution and replay.
|
|
85
|
+
*/
|
|
86
|
+
export declare function defineDurableCallback<TClosure extends object, TArgs extends unknown[], TResult>(input: {
|
|
87
|
+
readonly callback: (closure: TClosure, ...args: TArgs) => TResult;
|
|
88
|
+
readonly closure: TClosure & DurableCallbackJsonObject<TClosure>;
|
|
89
|
+
}): (...args: TArgs) => TResult;
|
|
74
90
|
/** Marks a live callback with the descriptor needed to register it at resolve time. */
|
|
75
91
|
export declare function stampDurableDynamicCallback<TCallback extends (...args: never[]) => unknown>(callback: TCallback, descriptor: StampedDurableDynamicCallback): TCallback;
|
|
76
92
|
export declare function readDurableDynamicCallback(callback: unknown): StampedDurableDynamicCallback | undefined;
|
|
@@ -88,3 +104,4 @@ export declare function collectDurableDynamicToolCallbacks(input: {
|
|
|
88
104
|
readonly toModelOutput?: (...args: never[]) => unknown;
|
|
89
105
|
}): LiveDurableDynamicToolCallbacks;
|
|
90
106
|
export declare function readDurableDynamicToolCallbacks(definition: object): LiveDurableDynamicToolCallbacks | undefined;
|
|
107
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveApprovalPolicy}from"#approval/definition.js";const STAMPED_CALLBACK=Symbol.for(`eve:durable-dynamic-callback`),DURABLE_DYNAMIC_TOOL_CALLBACKS=Symbol.for(`eve:durable-dynamic-tool-callbacks`),REGISTRY=Symbol.for(`eve:scoped-dynamic-tool-callbacks`);function getRegistry(){let e=globalThis;return e[REGISTRY]??=new Map}function getSessionBindings(e){let t=getRegistry(),n=t.get(e);return n!==void 0&&(t.delete(e),t.set(e,n)),n}function callbackKey(e,t){return JSON.stringify([e.scope,e.resolverSlug,e.entryKey,e.name,t])}function registerDurableDynamicCallback(e){let t=getRegistry(),n=getSessionBindings(e.owner.sessionId);n===void 0&&(n=new Map,t.set(e.owner.sessionId,n),t.size>1024&&t.delete(t.keys().next().value)),n.set(callbackKey(e.owner,e.phase),e.callback)}function lookupDurableDynamicCallback(e,t){return getSessionBindings(e.sessionId)?.get(callbackKey(e,t))}function clearDurableDynamicCallbacks(e,t){if(t===void 0){getRegistry().delete(e);return}let n=getSessionBindings(e);if(n===void 0)return;let r=JSON.stringify([t.scope,t.resolverSlug]).slice(0,-1)+`,`;for(let e of n.keys())e.startsWith(r)&&n.delete(e)}function callDurableDynamicCallback(e,t,...n){return e(t,...n)}function hasUnregisteredDurableDynamicCallbacks(e,t){return e.some(e=>durableCallbackPhases(e.callbacks).some(n=>lookupDurableDynamicCallback({...e,...t},n)===void 0))}function durableCallbackPhases(e){return[[`execute`,e.execute],[`labelComplete`,e.label?.complete],[`labelDelta`,e.label?.delta],[`labelStart`,e.label?.start],[`approvalKey`,e.approvalKey],[`approvalRequest`,e.approvalRequest],[`approvalResponse`,e.approvalResponse],[`toModelOutput`,e.toModelOutput]].flatMap(([e,t])=>t===void 0?[]:[e])}function stampDurableDynamicCallback(e,t){return Object.defineProperty(e,STAMPED_CALLBACK,{configurable:!0,value:t}),e}function readDurableDynamicCallback(e){if(typeof e==`function`)return Reflect.get(e,STAMPED_CALLBACK)}function stampDurableDynamicToolCallbacks(e,t){Object.defineProperty(e,DURABLE_DYNAMIC_TOOL_CALLBACKS,{configurable:!0,value:t})}function collectDurableDynamicToolCallbacks(e){let t=readDurableDynamicCallback(e.label?.complete),n=readDurableDynamicCallback(e.label?.delta),r=readDurableDynamicCallback(e.label?.start),i=e.approval===void 0?void 0:readDurableDynamicCallback(resolveApprovalPolicy(e.approval)),a=e.approval===void 0||typeof e.approval==`function`?void 0:readDurableDynamicCallback(e.approval.response),o=readDurableDynamicCallback(e.approvalKey),s=readDurableDynamicCallback(e.execute),c=readDurableDynamicCallback(e.toModelOutput),l={};return s!==void 0&&(l.execute=s),(t!==void 0||n!==void 0||r!==void 0)&&(l.label={complete:t,delta:n,start:r}),o!==void 0&&(l.approvalKey=o),i!==void 0&&(l.approvalRequest=i),a!==void 0&&(l.approvalResponse=a),c!==void 0&&(l.toModelOutput=c),l}function readDurableDynamicToolCallbacks(e){return e[DURABLE_DYNAMIC_TOOL_CALLBACKS]}export{DURABLE_DYNAMIC_TOOL_CALLBACKS,callDurableDynamicCallback,clearDurableDynamicCallbacks,collectDurableDynamicToolCallbacks,hasUnregisteredDurableDynamicCallbacks,lookupDurableDynamicCallback,readDurableDynamicCallback,readDurableDynamicToolCallbacks,registerDurableDynamicCallback,stampDurableDynamicCallback,stampDurableDynamicToolCallbacks};
|
|
1
|
+
import{resolveApprovalPolicy}from"#approval/definition.js";const STAMPED_CALLBACK=Symbol.for(`eve:durable-dynamic-callback`),DURABLE_DYNAMIC_TOOL_CALLBACKS=Symbol.for(`eve:durable-dynamic-tool-callbacks`),REGISTRY=Symbol.for(`eve:scoped-dynamic-tool-callbacks`);function getRegistry(){let e=globalThis;return e[REGISTRY]??=new Map}function getSessionBindings(e){let t=getRegistry(),n=t.get(e);return n!==void 0&&(t.delete(e),t.set(e,n)),n}function callbackKey(e,t){return JSON.stringify([e.scope,e.resolverSlug,e.entryKey,e.name,t])}function registerDurableDynamicCallback(e){let t=getRegistry(),n=getSessionBindings(e.owner.sessionId);n===void 0&&(n=new Map,t.set(e.owner.sessionId,n),t.size>1024&&t.delete(t.keys().next().value)),n.set(callbackKey(e.owner,e.phase),e.callback)}function lookupDurableDynamicCallback(e,t){return getSessionBindings(e.sessionId)?.get(callbackKey(e,t))}function clearDurableDynamicCallbacks(e,t){if(t===void 0){getRegistry().delete(e);return}let n=getSessionBindings(e);if(n===void 0)return;let r=JSON.stringify([t.scope,t.resolverSlug]).slice(0,-1)+`,`;for(let e of n.keys())e.startsWith(r)&&n.delete(e)}function callDurableDynamicCallback(e,t,...n){return e(t,...n)}function hasUnregisteredDurableDynamicCallbacks(e,t){return e.some(e=>durableCallbackPhases(e.callbacks).some(n=>lookupDurableDynamicCallback({...e,...t},n)===void 0))}function durableCallbackPhases(e){return[[`execute`,e.execute],[`labelComplete`,e.label?.complete],[`labelDelta`,e.label?.delta],[`labelStart`,e.label?.start],[`approvalKey`,e.approvalKey],[`approvalRequest`,e.approvalRequest],[`approvalResponse`,e.approvalResponse],[`toModelOutput`,e.toModelOutput]].flatMap(([e,t])=>t===void 0?[]:[e])}function defineDurableCallback(e){return stampDurableDynamicCallback((...t)=>e.callback(e.closure,...t),{callback:(t,...n)=>Reflect.apply(e.callback,void 0,[t,...n]),closure:e.closure})}function stampDurableDynamicCallback(e,t){return Object.defineProperty(e,STAMPED_CALLBACK,{configurable:!0,value:t}),e}function readDurableDynamicCallback(e){if(typeof e==`function`)return Reflect.get(e,STAMPED_CALLBACK)}function stampDurableDynamicToolCallbacks(e,t){Object.defineProperty(e,DURABLE_DYNAMIC_TOOL_CALLBACKS,{configurable:!0,value:t})}function collectDurableDynamicToolCallbacks(e){let t=readDurableDynamicCallback(e.label?.complete),n=readDurableDynamicCallback(e.label?.delta),r=readDurableDynamicCallback(e.label?.start),i=e.approval===void 0?void 0:readDurableDynamicCallback(resolveApprovalPolicy(e.approval)),a=e.approval===void 0||typeof e.approval==`function`?void 0:readDurableDynamicCallback(e.approval.response),o=readDurableDynamicCallback(e.approvalKey),s=readDurableDynamicCallback(e.execute),c=readDurableDynamicCallback(e.toModelOutput),l={};return s!==void 0&&(l.execute=s),(t!==void 0||n!==void 0||r!==void 0)&&(l.label={complete:t,delta:n,start:r}),o!==void 0&&(l.approvalKey=o),i!==void 0&&(l.approvalRequest=i),a!==void 0&&(l.approvalResponse=a),c!==void 0&&(l.toModelOutput=c),l}function readDurableDynamicToolCallbacks(e){return e[DURABLE_DYNAMIC_TOOL_CALLBACKS]}export{DURABLE_DYNAMIC_TOOL_CALLBACKS,callDurableDynamicCallback,clearDurableDynamicCallbacks,collectDurableDynamicToolCallbacks,defineDurableCallback,hasUnregisteredDurableDynamicCallbacks,lookupDurableDynamicCallback,readDurableDynamicCallback,readDurableDynamicToolCallbacks,registerDurableDynamicCallback,stampDurableDynamicCallback,stampDurableDynamicToolCallbacks};
|
|
@@ -6,18 +6,52 @@ import type { TaskExec } from "#tools/task.js";
|
|
|
6
6
|
import type { ToolModelOutput } from "#tools/model-output.js";
|
|
7
7
|
export interface AgentInput {
|
|
8
8
|
readonly agentId?: string;
|
|
9
|
-
readonly key: string;
|
|
10
9
|
readonly message: string;
|
|
11
10
|
readonly outputSchema?: JsonObject;
|
|
12
|
-
|
|
11
|
+
}
|
|
12
|
+
type JsonSchemaProperties = Readonly<Record<string, JsonObject>>;
|
|
13
|
+
type JsonSchemaRequiredKeys<TProperties extends JsonSchemaProperties, TRequired> = TRequired extends readonly string[] ? Extract<TRequired[number], keyof TProperties> : never;
|
|
14
|
+
type Simplify<TValue> = {
|
|
15
|
+
[TKey in keyof TValue]: TValue[TKey];
|
|
16
|
+
};
|
|
17
|
+
type JsonSchemaObjectOutput<TProperties extends JsonSchemaProperties, TRequired> = Simplify<{
|
|
18
|
+
-readonly [TKey in JsonSchemaRequiredKeys<TProperties, TRequired>]-?: JsonSchemaOutput<TProperties[TKey]>;
|
|
19
|
+
} & {
|
|
20
|
+
-readonly [TKey in Exclude<keyof TProperties, JsonSchemaRequiredKeys<TProperties, TRequired>>]?: JsonSchemaOutput<TProperties[TKey]>;
|
|
21
|
+
}>;
|
|
22
|
+
type JsonSchemaOutput<TSchema> = TSchema extends {
|
|
23
|
+
readonly const: infer TValue;
|
|
24
|
+
} ? Extract<TValue, JsonValue> : TSchema extends {
|
|
25
|
+
readonly enum: readonly (infer TValue)[];
|
|
26
|
+
} ? Extract<TValue, JsonValue> : TSchema extends {
|
|
27
|
+
readonly type: "object";
|
|
28
|
+
readonly properties: infer TProperties extends JsonSchemaProperties;
|
|
29
|
+
readonly required?: infer TRequired;
|
|
30
|
+
} ? JsonSchemaObjectOutput<TProperties, TRequired> : TSchema extends {
|
|
31
|
+
readonly type: "array";
|
|
32
|
+
readonly items: infer TItems extends JsonObject;
|
|
33
|
+
} ? JsonSchemaOutput<TItems>[] : TSchema extends {
|
|
34
|
+
readonly type: "string";
|
|
35
|
+
} ? string : TSchema extends {
|
|
36
|
+
readonly type: "integer" | "number";
|
|
37
|
+
} ? number : TSchema extends {
|
|
38
|
+
readonly type: "boolean";
|
|
39
|
+
} ? boolean : TSchema extends {
|
|
40
|
+
readonly type: "null";
|
|
41
|
+
} ? null : JsonValue;
|
|
42
|
+
interface WorkflowAgent {
|
|
43
|
+
<const TOutputSchema extends JsonObject>(target: string, input: AgentInput & {
|
|
44
|
+
readonly outputSchema: TOutputSchema;
|
|
45
|
+
}): Promise<JsonSchemaOutput<TOutputSchema>>;
|
|
46
|
+
(target: string, input: AgentInput): Promise<JsonValue>;
|
|
13
47
|
}
|
|
14
48
|
/**
|
|
15
49
|
* Context supplied to a workflow tool. Pass it directly to a step helper for
|
|
16
50
|
* getToken/requireAuth; those capabilities throw in the workflow body itself.
|
|
17
51
|
*/
|
|
18
52
|
export type WorkflowToolContext = Pick<ToolContext, "abortSignal" | "callId" | "session" | "toolName" | "getToken" | "requireAuth"> & {
|
|
19
|
-
/** Invoke a visible subagent
|
|
20
|
-
agent
|
|
53
|
+
/** Invoke a visible subagent by its model-visible name. */
|
|
54
|
+
agent: WorkflowAgent;
|
|
21
55
|
/** Ask the human on the session's channel; awaiting the answer suspends the run. */
|
|
22
56
|
ask(request: ToolInputRequest): PromiseLike<ToolInputResponse>;
|
|
23
57
|
};
|
package/docs/agent-config.md
CHANGED
|
@@ -247,18 +247,54 @@ putting the connection string or credentials in the env vars it reads. If the
|
|
|
247
247
|
installed package must stay external in hosted output, list it in
|
|
248
248
|
`build.externalDependencies`.
|
|
249
249
|
|
|
250
|
+
## Workflow checkpoint batching
|
|
251
|
+
|
|
252
|
+
By default, eve commits a durable Workflow step after every model call and its
|
|
253
|
+
inline tool calls. You can experimentally let one Workflow step run several
|
|
254
|
+
sequential model calls:
|
|
255
|
+
|
|
256
|
+
```ts title="agent/agent.ts"
|
|
257
|
+
import { defineAgent } from "eve";
|
|
258
|
+
|
|
259
|
+
export default defineAgent({
|
|
260
|
+
model: "anthropic/claude-opus-4.8",
|
|
261
|
+
experimental: {
|
|
262
|
+
workflow: {
|
|
263
|
+
modelCallsPerStep: 4,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`experimental.workflow.modelCallsPerStep` is a positive-integer ceiling. It defaults to
|
|
270
|
+
`1` and applies independently to each root agent or declared subagent. A higher
|
|
271
|
+
value can reduce Workflow checkpoint overhead in sequential tool loops, but it
|
|
272
|
+
widens the replay unit: if the Workflow step is interrupted, its earlier model
|
|
273
|
+
calls and inline tool executions can run again. That can repeat provider costs,
|
|
274
|
+
events, and side effects. Use stable idempotency keys for non-idempotent tools.
|
|
275
|
+
|
|
276
|
+
eve ends a batch before it waits for input, authorization, or blocking
|
|
277
|
+
coordination, and before it acknowledges a background task. A batch can also
|
|
278
|
+
end below the configured ceiling when the turn completes. Steering cancels the
|
|
279
|
+
active model-and-tool cycle, commits earlier completed cycles in the batch, and
|
|
280
|
+
starts the replacement turn from that state. It does not roll the session back
|
|
281
|
+
to the beginning of the batch. This option is experimental and may change or
|
|
282
|
+
disappear in any release. See [Execution model and
|
|
283
|
+
durability](./concepts/execution-model-and-durability#resuming-after-a-crash)
|
|
284
|
+
for the retry behavior.
|
|
285
|
+
|
|
250
286
|
## Other defineAgent fields
|
|
251
287
|
|
|
252
288
|
`defineAgent` takes a few more fields, all optional. For the exported types, see the [TypeScript API Reference](./reference/typescript-api).
|
|
253
289
|
|
|
254
|
-
| Field | Type | Default | Description
|
|
255
|
-
| -------------- | --------------------------------------- | ---------------- |
|
|
256
|
-
| `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls.
|
|
257
|
-
| `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call.
|
|
258
|
-
| `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. Sessions complete after 30 days by default; usage-limit defaults and inheritance are described above. Set a limit to `false` to disable it.
|
|
259
|
-
| `experimental` | `
|
|
260
|
-
| `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for function-like invocations such as a subagent turn, schedule, or remote job. Ordinary interactive turns ignore it unless the client supplies a per-message schema.
|
|
261
|
-
| `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.
|
|
290
|
+
| Field | Type | Default | Description |
|
|
291
|
+
| -------------- | --------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
292
|
+
| `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
|
|
293
|
+
| `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
|
|
294
|
+
| `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. Sessions complete after 30 days by default; usage-limit defaults and inheritance are described above. Set a limit to `false` to disable it. |
|
|
295
|
+
| `experimental` | `AgentExperimentalDefinition` | unset | Unstable opt-ins. `workflow.world` selects the Workflow world package on the root agent; `workflow.modelCallsPerStep` batches sequential model calls into a wider replay unit. |
|
|
296
|
+
| `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for function-like invocations such as a subagent turn, schedule, or remote job. Ordinary interactive turns ignore it unless the client supplies a per-message schema. |
|
|
297
|
+
| `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. |
|
|
262
298
|
|
|
263
299
|
`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.
|
|
264
300
|
|
|
@@ -28,6 +28,8 @@ export default defineChannel({
|
|
|
28
28
|
});
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Adjacent queued messages can combine into one turn only when their full auth contexts match, including identity, issuer, authenticator, and authorization attributes. Their attachments and context stay in payload order. A different auth context ends the batch; eve does not regroup messages across intervening senders. Anonymous deliveries stay separate. Turns still share the session's conversation history.
|
|
32
|
+
|
|
31
33
|
`from(address).send(...)`, fixed `Session.send(...)`, cross-channel sends, and Chat SDK bridge sends also accept a per-send `turnPolicy` override. Pure `inputResponses` deliveries answer their pending request without steering. Explicit `cancel()` remains the stop-without-replacement operation.
|
|
32
34
|
|
|
33
35
|
Channel admission still runs first. Ignored mentions, rejected signatures, duplicates, and any other dropped platform events never affect the active turn.
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -294,6 +294,8 @@ export default slackChannel({
|
|
|
294
294
|
});
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
+
With `"queue"`, adjacent messages with matching full auth contexts can combine into one turn, keeping their attachments and context in order. Messages from different users, or from the same user with different authorization attributes, stay separate. Anonymous deliveries also stay separate. Batching follows session-inbox order, and turns still share the thread's session history.
|
|
298
|
+
|
|
297
299
|
Message hooks (`onMessage`, `onAppMention`, and `onDirectMessage`) also receive thread-bound session operations directly on `ctx`. `ctx.cancel({ turnId? })` remains useful for a Stop button: it cancels without sending replacement input. `"accepted"` means the live thread session durably queued that request; a parked session consumes it as a no-op, and `"no_active_turn"` means the thread has no active session owner.
|
|
298
300
|
|
|
299
301
|
Message and interaction hooks expose every current-owner operation directly on `ctx`:
|
|
@@ -11,10 +11,16 @@ Work nests in three levels:
|
|
|
11
11
|
|
|
12
12
|
- **session**: the whole durable conversation or task. It's long-lived and can span many requests over days or weeks without losing context.
|
|
13
13
|
- **turn**: one user message and all the work it triggers (model calls, tool calls, reasoning) until the agent produces its response.
|
|
14
|
-
- **step**: a durable checkpoint inside a turn
|
|
14
|
+
- **step**: a durable checkpoint inside a turn. By default, it contains one model call and the inline tool calls that follow it.
|
|
15
15
|
|
|
16
16
|
Every turn runs as a durable workflow, built on the open-source [Workflow SDK](https://workflow-sdk.dev/) (Vercel Workflow when you deploy on Vercel). eve checkpoints progress and serializes durable state at each step boundary. Your code runs inside a managed step, so tools, the sandbox, and subagents feel synchronous even though the session underneath them is durable.
|
|
17
17
|
|
|
18
|
+
The experimental
|
|
19
|
+
[`workflow.modelCallsPerStep`](../agent-config#workflow-checkpoint-batching)
|
|
20
|
+
agent setting can group several sequential model-and-tool cycles into one
|
|
21
|
+
Workflow step. This reduces checkpoint overhead but makes the entire group one
|
|
22
|
+
replay unit.
|
|
23
|
+
|
|
18
24
|
The Workflow SDK is not inherently tied to Vercel. In local development and in a self-deployed `eve start` process, eve uses the SDK's local world by default; that world persists workflow runs on disk under `.eve/.workflow-data` and dispatches through the same Nitro-hosted workflow routes. On Vercel, the same workflow code runs against Vercel Workflow instead, which adds platform features such as latest production deployment routing and dashboard run metadata.
|
|
19
25
|
|
|
20
26
|
When a Vercel production deployment changes, the next model turn in an existing session uses that deployment's current system-role instructions, model, and tools. Static user-role instructions stay pinned in the durable conversation history and are not seeded again. Identity-based channels such as Telegram private chats and Twilio phone-number conversations therefore adopt agent behavior updates without duplicating or rewriting their existing context.
|
|
@@ -58,7 +64,19 @@ Provider keys, tool secrets, and MCP, OpenAPI, and connection credentials stay i
|
|
|
58
64
|
|
|
59
65
|
Crash the process, hit a timeout, or redeploy mid-turn, and the run picks up from the last completed step rather than replaying the whole turn. Completed steps never re-run; eve replays the recorded result. A step interrupted mid-execution re-runs, so make non-idempotent side effects like charges or emails idempotent, or gate them with approval. Whatever that step already wrote to the session stream stays there, and the re-run emits its events again under new ids, so a stream consumer sees both attempts — see [the event envelope](./sessions-runs-and-streaming#the-event-envelope).
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
With `experimental.workflow.modelCallsPerStep` greater than `1`, an
|
|
68
|
+
interrupted Workflow step can also repeat earlier model calls and inline tool
|
|
69
|
+
executions from the same batch. Approval, input, blocking coordination, and
|
|
70
|
+
background-task boundaries still force a checkpoint before eve waits or
|
|
71
|
+
acknowledges the work.
|
|
72
|
+
|
|
73
|
+
Steering does not replay or discard the whole batch. eve aborts the active
|
|
74
|
+
model-and-tool cycle, commits earlier completed cycles in the batch, and starts
|
|
75
|
+
the replacement turn from that state in the next Workflow step.
|
|
76
|
+
|
|
77
|
+
Durability itself needs no configuration. eve owns the workflow lifecycle, and
|
|
78
|
+
sessions are durable by default; checkpoint batching is an explicit
|
|
79
|
+
experimental opt-in.
|
|
62
80
|
|
|
63
81
|
For ordinary tools, eve manages the workflow around your executor. Use [`defineWorkflowTool`](../tools/workflows) when your own tool body needs durable waits, such as a timer, webhook, or human answer. Two surfaces give your own code session data: tools read the current session's metadata (id, turn, auth, parent lineage) via `ctx.session`, and [`defineState`](./state) reads or writes session-scoped durable state. See [State](./state) for the read/write model.
|
|
64
82
|
|