eve 0.47.0 → 0.47.2

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.
@@ -1,2 +1,2 @@
1
- import{randomBytes}from"node:crypto";import{EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_RESUME_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_SUSPEND_ROUTE_PATH}from"#protocol/routes.js";import{isLoopbackServerUrl}from"#shared/network-address.js";import{loadDevelopmentEnvironmentFiles}from"#cli/dev/environment.js";import{resolveDiscoveryProject}from"#discover/project.js";import{eveDevArguments}from"#setup/primitives/index.js";import{detectPackageManager}from"#setup/package-manager.js";import{EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV,createDevelopmentSandboxRunId}from"#execution/sandbox/development-run.js";import{pruneLocalSandboxTemplatesInBackground,stopDevelopmentSandboxResources}from"#execution/sandbox/bindings/local.js";import{EVE_DEV_ENV_FLAG}from"#internal/application/optional-package-install.js";import{toErrorMessage}from"#shared/errors.js";import{startDevelopmentSandboxPrewarmInBackground}from"#execution/sandbox/development-prewarm.js";import{devBootPhase}from"#internal/dev-boot-progress.js";import{readDevelopmentRuntimeArtifactsRevision}from"#services/dev-client/runtime-artifacts.js";import{createDevelopmentApplicationNitro}from"#internal/nitro/host/create-application-nitro.js";import{prepareDevelopmentApplicationHost}from"#internal/nitro/host/prepare-application-host.js";import{createDevelopmentNitroArtifactsConfig}from"#internal/nitro/host/artifacts-config.js";import{buildDevelopmentHostCandidate}from"#internal/nitro/host/dev-host-candidate.js";import{removeDevelopmentHostWorkspace}from"#internal/nitro/host/dev-host-workspace.js";import{DrainedNitroDevServer}from"#internal/nitro/host/drained-nitro-dev-server.js";import{activateDevelopmentGeneration,discardDevelopmentGeneration}from"#internal/nitro/development-generation.js";import{resolveNitroCompiledArtifactsSource}from"#internal/nitro/routes/runtime-artifacts.js";import{DEFAULT_DEVELOPMENT_SERVER_HOST,normalizeDevelopmentServerClientUrl}from"#internal/nitro/host/dev-server-url.js";import{createDevelopmentAuthoredRebuildCoordinator}from"#internal/nitro/host/dev-authored-rebuild-coordinator.js";import{DevelopmentServerState}from"#internal/nitro/host/dev-server-state.js";import{handleDevRuntimeArtifactsRequest}from"#internal/nitro/routes/dev-runtime-artifacts.js";import{DEFAULT_DEVELOPMENT_SERVER_PORT,MAX_DEVELOPMENT_SERVER_PORT_ATTEMPTS}from"#internal/nitro/host/ports.js";import{createDevelopmentWorkflowWorld,installWorkflowLocalQueueEnvironment,installWorkflowTransportEnvironment}from"#internal/nitro/host/dev-workflow-world-setup.js";const MAX_ALLOWED_DEVELOPMENT_SERVER_PORT=65535,PORT_ENV=`PORT`;async function isActiveDevelopmentServerForApp(e){try{let t=await new DevelopmentServerState(await resolveDiscoveryProject(e.appRoot)).read();return t===void 0||!isLoopbackServerUrl(t)||!await isDevelopmentServerReady(t)?!1:new URL(t).origin===new URL(normalizeDevelopmentServerClientUrl(e.serverUrl)).origin}catch{return!1}}async function isDevelopmentServerReady(e){return await readDevelopmentRuntimeArtifactsRevision({serverUrl:e,timeoutMs:1e3})!==void 0}function isAddressInUseError(e){return e instanceof Error&&`code`in e&&e.code===`EADDRINUSE`}function resolveDevelopmentServerPort(e){let t=typeof e==`string`?Number(e):e??DEFAULT_DEVELOPMENT_SERVER_PORT;if(!Number.isInteger(t)||t<0||t>MAX_ALLOWED_DEVELOPMENT_SERVER_PORT)throw Error(`Invalid development server port "${String(e)}". Expected an integer between 0 and ${MAX_ALLOWED_DEVELOPMENT_SERVER_PORT}.`);return t}function readEnvironmentPort(){let e=process.env[PORT_ENV];if(e===void 0||e.trim()===``)return;let t=Number(e);if(!Number.isInteger(t)||t<0||t>MAX_ALLOWED_DEVELOPMENT_SERVER_PORT)throw Error(`Invalid ${PORT_ENV} environment variable "${e}". Expected an integer between 0 and ${MAX_ALLOWED_DEVELOPMENT_SERVER_PORT}.`);return t}async function detectDevelopmentCommandPackageManager(e){try{return(await detectPackageManager(e)).kind}catch{return`pnpm`}}async function formatDevelopmentServerConnectCommand(e,t){let n=await detectDevelopmentCommandPackageManager(e);return[n,...eveDevArguments(n),t].join(` `)}async function createDevelopmentServerAlreadyRunningError(e,t){let n=await formatDevelopmentServerConnectCommand(e,t);return Error([`A dev server is already running for this eve agent.`,`To connect to the existing instance, run: ${n}`].join(`
1
+ import{randomBytes}from"node:crypto";import{EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_RESUME_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH,EVE_DEV_RUNTIME_ARTIFACTS_SUSPEND_ROUTE_PATH}from"#protocol/routes.js";import{isLoopbackServerUrl}from"#shared/network-address.js";import{loadDevelopmentEnvironmentFiles}from"#cli/dev/environment.js";import{resolveDiscoveryProject}from"#discover/project.js";import{eveDevArguments}from"#setup/primitives/index.js";import{detectPackageManager}from"#setup/package-manager.js";import{EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV,createDevelopmentSandboxRunId}from"#execution/sandbox/development-run.js";import{pruneLocalSandboxTemplatesInBackground,stopDevelopmentSandboxResources}from"#execution/sandbox/bindings/local.js";import{EVE_DEV_ENV_FLAG}from"#internal/application/optional-package-install.js";import{devBootPhase}from"#internal/dev-boot-progress.js";import{readDevelopmentRuntimeArtifactsRevision}from"#services/dev-client/runtime-artifacts.js";import{toErrorMessage}from"#shared/errors.js";import{startDevelopmentSandboxPrewarmInBackground}from"#execution/sandbox/development-prewarm.js";import{createDevelopmentApplicationNitro}from"#internal/nitro/host/create-application-nitro.js";import{prepareDevelopmentApplicationHost}from"#internal/nitro/host/prepare-application-host.js";import{createDevelopmentNitroArtifactsConfig}from"#internal/nitro/host/artifacts-config.js";import{buildDevelopmentHostCandidate}from"#internal/nitro/host/dev-host-candidate.js";import{removeDevelopmentHostWorkspace}from"#internal/nitro/host/dev-host-workspace.js";import{DrainedNitroDevServer}from"#internal/nitro/host/drained-nitro-dev-server.js";import{activateDevelopmentGeneration,discardDevelopmentGeneration}from"#internal/nitro/development-generation.js";import{resolveNitroCompiledArtifactsSource}from"#internal/nitro/routes/runtime-artifacts.js";import{DEFAULT_DEVELOPMENT_SERVER_HOST,normalizeDevelopmentServerClientUrl}from"#internal/nitro/host/dev-server-url.js";import{createDevelopmentAuthoredRebuildCoordinator}from"#internal/nitro/host/dev-authored-rebuild-coordinator.js";import{DevelopmentServerState}from"#internal/nitro/host/dev-server-state.js";import{handleDevRuntimeArtifactsRequest}from"#internal/nitro/routes/dev-runtime-artifacts.js";import{DEFAULT_DEVELOPMENT_SERVER_PORT,MAX_DEVELOPMENT_SERVER_PORT_ATTEMPTS}from"#internal/nitro/host/ports.js";import{createDevelopmentWorkflowWorld,installWorkflowLocalQueueEnvironment,installWorkflowTransportEnvironment}from"#internal/nitro/host/dev-workflow-world-setup.js";const MAX_ALLOWED_DEVELOPMENT_SERVER_PORT=65535,PORT_ENV=`PORT`;async function isActiveDevelopmentServerForApp(e){try{let t=await new DevelopmentServerState(await resolveDiscoveryProject(e.appRoot)).read();return t===void 0||!isLoopbackServerUrl(t)||!await isDevelopmentServerReady(t)?!1:new URL(t).origin===new URL(normalizeDevelopmentServerClientUrl(e.serverUrl)).origin}catch{return!1}}async function isDevelopmentServerReady(e){return await readDevelopmentRuntimeArtifactsRevision({serverUrl:e,timeoutMs:1e3})!==void 0}function isAddressInUseError(e){return e instanceof Error&&`code`in e&&e.code===`EADDRINUSE`}function resolveDevelopmentServerPort(e){let t=typeof e==`string`?Number(e):e??DEFAULT_DEVELOPMENT_SERVER_PORT;if(!Number.isInteger(t)||t<0||t>MAX_ALLOWED_DEVELOPMENT_SERVER_PORT)throw Error(`Invalid development server port "${String(e)}". Expected an integer between 0 and ${MAX_ALLOWED_DEVELOPMENT_SERVER_PORT}.`);return t}function readEnvironmentPort(){let e=process.env[PORT_ENV];if(e===void 0||e.trim()===``)return;let t=Number(e);if(!Number.isInteger(t)||t<0||t>MAX_ALLOWED_DEVELOPMENT_SERVER_PORT)throw Error(`Invalid ${PORT_ENV} environment variable "${e}". Expected an integer between 0 and ${MAX_ALLOWED_DEVELOPMENT_SERVER_PORT}.`);return t}async function detectDevelopmentCommandPackageManager(e){try{return(await detectPackageManager(e)).kind}catch{return`pnpm`}}async function formatDevelopmentServerConnectCommand(e,t){let n=await detectDevelopmentCommandPackageManager(e);return[n,...eveDevArguments(n),t].join(` `)}async function createDevelopmentServerAlreadyRunningError(e,t){let n=await formatDevelopmentServerConnectCommand(e,t);return Error([`A dev server is already running for this eve agent.`,`To connect to the existing instance, run: ${n}`].join(`
2
2
  `))}function resolveDevelopmentServerPorts(e){let t=resolveDevelopmentServerPort(e.port);if(t===0||!e.retryOnAddressInUse)return[t];let n=[];for(let e=0;e<MAX_DEVELOPMENT_SERVER_PORT_ATTEMPTS;e+=1){let r=t+e;if(r>65535)break;n.push(r)}return n}function addDevelopmentControlHandler(e){e.devServer.setControlHandler(async a=>{let o=await e.workflowWorld?.handleRequest(a);if(o!==void 0)return o;let s=new URL(a.url);if(s.pathname===EVE_DEV_RUNTIME_ARTIFACTS_ROUTE_PATH&&a.method===`GET`)return handleDevRuntimeArtifactsRequest({appRoot:e.appRoot});let c=s.pathname===EVE_DEV_RUNTIME_ARTIFACTS_SUSPEND_ROUTE_PATH&&a.method===`POST`,l=s.pathname===EVE_DEV_RUNTIME_ARTIFACTS_RESUME_ROUTE_PATH&&a.method===`POST`,u=s.pathname===EVE_DEV_RUNTIME_ARTIFACTS_REBUILD_ROUTE_PATH&&(a.method===`GET`||a.method===`POST`);if(!c&&!l&&!u)return;let d=e.getWatcher();return d===void 0?Response.json({error:`The development server is still starting.`},{status:503}):c?(await d.suspend(),Response.json({suspended:!0})):l?(await d.resume({silent:s.searchParams.get(`silent`)===`1`}),handleDevRuntimeArtifactsRequest({appRoot:e.appRoot})):(s.searchParams.get(`force`)===`1`?await d.rebuild():await d.flush(),handleDevRuntimeArtifactsRequest({appRoot:e.appRoot}))})}async function closeDevelopmentServerResources(e){let t=[],attempt=async e=>{try{return await e(),!0}catch(e){return t.push(e),!1}},n=e.authoredSourceWatcher;n!==void 0&&await attempt(()=>n.close());let r=e.workflowWorld;r!==void 0&&await attempt(()=>r.close());let i=e.devServer,a=i===void 0||await attempt(()=>i.close()),o=e.nitro;return o!==void 0&&await attempt(()=>o.close()),await attempt(()=>stopDevelopmentSandboxResources({appRoot:e.appRoot,devRunId:e.developmentSandboxRunId,log:e=>console.warn(`[eve:dev] ${e}`)})),{errors:t,listenerClosed:a}}function createDevelopmentServerCleanupError(e){if(e.length!==0){if(e.length===1){let t=e[0];return t instanceof Error?t:Error(`Failed to close the development server: ${toErrorMessage(t)}`,{cause:t})}return AggregateError(e,`Multiple development-server resources failed to close.`)}}function createDevelopmentServerStartupCleanupError(e,t){return AggregateError([e,...t],`${toErrorMessage(e)} Cleanup also failed.`,{cause:e})}async function listenForDevelopmentServer(e){let t=resolveDevelopmentServerPorts({port:e.port,retryOnAddressInUse:e.retryOnAddressInUse}),n;for(let r of t){let t=e.devServer.listen({hostname:e.host,port:r});try{return await t.ready(),t}catch(r){if(n=r,await t.close().catch(()=>{}),!isAddressInUseError(r)||!e.retryOnAddressInUse)throw r}}throw Error(`Failed to start Nitro dev server after ${t.length} attempts. Tried ports ${t.join(`, `)}.`,{cause:n})}async function startNitroDevelopmentServer(t,n){process.env[EVE_DEV_ENV_FLAG]??=`1`;let r=await resolveDiscoveryProject(t);loadDevelopmentEnvironmentFiles(r.appRoot);let i=readEnvironmentPort(),c=n.port??i,l=n.host!==void 0||n.port!==void 0||i!==void 0,f=new DevelopmentServerState(r),m=await f.read();if(m!==void 0&&isLoopbackServerUrl(m)&&await isDevelopmentServerReady(m)){if(n.existing===`attach-if-unconfigured`&&!l)return{handle:{kind:`existing`,appRoot:r.appRoot,url:m},close:void 0};throw await createDevelopmentServerAlreadyRunningError(r.appRoot,m)}let h=process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV],v=createDevelopmentSandboxRunId();process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV]=v;let y,b,x,S,C,w,T,E=!1,D=!1;try{let t=await devBootPhase(`compiling agent`,()=>prepareDevelopmentApplicationHost(r.appRoot),n.onBootProgress);T=t;let i=resolveNitroCompiledArtifactsSource(createDevelopmentNitroArtifactsConfig({appRoot:t.appRoot,configuredWorld:t.compileResult.manifest.config.experimental?.workflow?.world}));pruneLocalSandboxTemplatesInBackground(t.appRoot);let a=await devBootPhase(`creating dev server`,()=>createDevelopmentApplicationNitro(t),n.onBootProgress);y=a,b=new DrainedNitroDevServer(a.logger);let o=b,s=randomBytes(32).toString(`base64url`);o.setClientAddressSecret(s),S=installWorkflowTransportEnvironment(r.appRoot,s),C=createDevelopmentWorkflowWorld({appRoot:r.appRoot,preparedHost:t,transportSecret:s}),addDevelopmentControlHandler({appRoot:r.appRoot,devServer:o,getWatcher:()=>w,workflowWorld:C});let l=n.host??a.options.devServer.hostname??DEFAULT_DEVELOPMENT_SERVER_HOST,u=c===void 0,d=await devBootPhase(`binding port`,()=>listenForDevelopmentServer({devServer:o,host:l,port:c,retryOnAddressInUse:u}),n.onBootProgress);if(!d.url)throw Error(`Nitro dev server did not expose a URL.`);let p=normalizeDevelopmentServerClientUrl(d.url);x=installWorkflowLocalQueueEnvironment(p),await devBootPhase(`building dev bundle`,async()=>{let e=await buildDevelopmentHostCandidate({host:t,nitro:a});y=void 0;let n=t.workspace;D=!0,await o.replaceWorker({dispose:async()=>await removeDevelopmentHostWorkspace(n),entry:e.entry,workerData:e.workerData}),await activateDevelopmentGeneration({appRoot:t.appRoot,generation:t.generation}),E=!0},n.onBootProgress),await C?.start(),startDevelopmentSandboxPrewarmInBackground({appRoot:t.appRoot,compiledArtifactsSource:i});let m=await createDevelopmentAuthoredRebuildCoordinator({devServer:o,initialHost:t});w=await devBootPhase(`starting file watcher`,async()=>{let{startAuthoredSourceWatcher:e}=await import(`#internal/nitro/host/dev-authored-source-watcher.js`);return e({coordinator:m,preparedHost:t})},n.onBootProgress),await f.write(p);let g=x;if(g===void 0)throw Error(`Workflow local queue environment was not initialized.`);let _=w,O=b,k=C,A=S,j;return{handle:{kind:`started`,appRoot:r.appRoot,url:p},close:()=>(j??=(async()=>{let e=await closeDevelopmentServerResources({appRoot:r.appRoot,authoredSourceWatcher:_,devServer:O,developmentSandboxRunId:v,nitro:void 0,workflowWorld:k});e.listenerClosed&&await f.remove().catch(()=>{});try{let t=createDevelopmentServerCleanupError(e.errors);if(t!==void 0)throw t}finally{g(),A?.(),restoreDevelopmentSandboxRunId(h)}})(),j)}}catch(e){let t=await closeDevelopmentServerResources({appRoot:r.appRoot,authoredSourceWatcher:w,devServer:b,developmentSandboxRunId:v,nitro:y,workflowWorld:C}),n=[...t.errors];throw T!==void 0&&!E&&await discardDevelopmentGeneration(T.generation).catch(e=>{n.push(e)}),T!==void 0&&!D&&await removeDevelopmentHostWorkspace(T.workspace).catch(e=>{n.push(e)}),x?.(),S?.(),t.listenerClosed&&await f.remove().catch(()=>{}),restoreDevelopmentSandboxRunId(h),n.length>0?createDevelopmentServerStartupCleanupError(e,n):e}}function createDevelopmentServer(e,t={}){let n,r;return{start(){if(n!==void 0)throw Error(`DevelopmentServer.start() was already called.`);return n=startNitroDevelopmentServer(e,t).then(({handle:e,close:t})=>(r=t,e)),n},async close(){n!==void 0&&(await n.catch(()=>void 0),await r?.())}}}function restoreDevelopmentSandboxRunId(e){if(e===void 0){delete process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV];return}process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV]=e}export{createDevelopmentServer,isActiveDevelopmentServerForApp,normalizeDevelopmentServerClientUrl};
@@ -1 +1 @@
1
- import{isLocalDevelopmentServerUrl}from"#services/dev-client/local-host.js";import{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts}from"#services/dev-client/runtime-artifacts.js";var LocalDevelopmentRuntimeArtifactRefresher=class{#e;#t;#n;constructor(t){this.#e=isLocalDevelopmentServerUrl(t.serverUrl),this.#t=t.serverUrl}clear(){this.#n=void 0}async refresh(e){shouldRefreshRuntimeArtifactsForTurn(e)&&await this.#r({...e,rebuild:!0})}async refreshIdle(e){await this.#r({...e,rebuild:!1})}async refreshAfterSourceChange(e){await this.#r({...e,force:!0,rebuild:!0})}async#r(e){if(!this.#e)return;let n=(e.rebuild?await rebuildDevelopmentRuntimeArtifacts({force:e.force,serverUrl:this.#t}):void 0)??await readDevelopmentRuntimeArtifactsRevision({serverUrl:this.#t});if(n===void 0)return;let r=this.#n;r!==void 0&&r!==n&&await e.onRuntimeArtifactsChanged?.({previousRevision:r,revision:n}),this.#n=n}};function shouldRefreshRuntimeArtifactsForTurn(e){return e.message!==void 0&&(e.inputResponses?.length??0)===0}function createDevelopmentRuntimeArtifactRefresher(e){return new LocalDevelopmentRuntimeArtifactRefresher(e)}export{createDevelopmentRuntimeArtifactRefresher};
1
+ import{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts}from"#services/dev-client/runtime-artifacts.js";import{isLocalDevelopmentServerUrl}from"#services/dev-client/local-host.js";var LocalDevelopmentRuntimeArtifactRefresher=class{#e;#t;#n;constructor(e){this.#e=isLocalDevelopmentServerUrl(e.serverUrl),this.#t=e.serverUrl}clear(){this.#n=void 0}async refresh(e){shouldRefreshRuntimeArtifactsForTurn(e)&&await this.#r({...e,rebuild:!0})}async refreshIdle(e){await this.#r({...e,rebuild:!1})}async refreshAfterSourceChange(e){await this.#r({...e,force:!0,rebuild:!0})}async#r(t){if(!this.#e)return;let n=(t.rebuild?await rebuildDevelopmentRuntimeArtifacts({force:t.force,serverUrl:this.#t}):void 0)??await readDevelopmentRuntimeArtifactsRevision({serverUrl:this.#t});if(n===void 0)return;let r=this.#n;r!==void 0&&r!==n&&await t.onRuntimeArtifactsChanged?.({previousRevision:r,revision:n}),this.#n=n}};function shouldRefreshRuntimeArtifactsForTurn(e){return e.message!==void 0&&(e.inputResponses?.length??0)===0}function createDevelopmentRuntimeArtifactRefresher(e){return new LocalDevelopmentRuntimeArtifactRefresher(e)}export{createDevelopmentRuntimeArtifactRefresher};
@@ -1 +1 @@
1
- import{select}from"../ask.js";import{z}from"#compiled/zod/index.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{AI_GATEWAY_MODELS_URL,vercelGatewayFetch}from"#internal/gateway.js";const gatewayCatalogModelSchema=z.object({id:z.string(),name:z.string(),type:z.string(),owned_by:z.string(),released:z.number().finite().optional().catch(void 0),tags:z.array(z.string()).optional().catch(void 0),pricing:z.object({service_tiers:z.record(z.string(),z.unknown()).optional().catch(void 0)}).optional().catch(void 0)}),gatewayCatalogSchema=z.object({data:z.array(z.unknown())}).transform(({data:e})=>e.flatMap(e=>{let t=gatewayCatalogModelSchema.safeParse(e);return t.success?[t.data]:[]}));function modelOption(e,t,n,r=!0){return{id:e,label:t,value:e,hint:n,featured:r||void 0}}const FEATURED_MODEL_IDS=[DEFAULT_AGENT_MODEL_ID,`anthropic/claude-opus-4.8`,`openai/gpt-5.5`],FALLBACK_MODELS=[modelOption(DEFAULT_AGENT_MODEL_ID,`GLM 5.2`,`Z.AI`),modelOption(`anthropic/claude-opus-4.8`,`Claude Opus 4.8`,`Anthropic`),modelOption(`openai/gpt-5.5`,`GPT-5.5`,`OpenAI`),modelOption(`google/gemini-3.5`,`Gemini 3.5`,`Google`,!1)],PROVIDER_BRANDS={openai:`OpenAI`,zai:`Z.AI`,xai:`xAI`,deepseek:`DeepSeek`,moonshotai:`Moonshot AI`};function providerLabel(e){return e.length===0?``:PROVIDER_BRANDS[e]??e.charAt(0).toUpperCase()+e.slice(1)}async function fetchGatewayCatalog(e){let t=new AbortController,n=setTimeout(()=>t.abort(),5e3);try{let n=await vercelGatewayFetch(AI_GATEWAY_MODELS_URL,{signal:e===void 0?t.signal:AbortSignal.any([e,t.signal])});if(!n.ok)throw Error(`AI Gateway model catalog request failed (${n.status}).`);return parseGatewayCatalog(await n.json())}finally{clearTimeout(n)}}function parseGatewayCatalog(e){let t=gatewayCatalogSchema.safeParse(e);if(!t.success)throw Error(`AI Gateway returned an invalid model catalog.`);return t.data}function featuredPriority(e){let t=FEATURED_MODEL_IDS.indexOf(e);return t===-1?FEATURED_MODEL_IDS.length:t}function modelOptionsFromCatalog(e){if(e===void 0)return FALLBACK_MODELS;let t=e.filter(e=>e.type===`language`&&(e.id===DEFAULT_AGENT_MODEL_ID||(e.tags??[]).includes(`web-search`))).map(e=>{let t=e.id.split(`/`)[0]??``;return{value:e.id,label:e.name,hint:providerLabel(t),provider:t,released:e.released}}).sort((e,t)=>{let n=featuredPriority(e.value)-featuredPriority(t.value);if(n!==0)return n;let r=(t.released??-1/0)-(e.released??-1/0);if(r!==0)return r;let i=e.label.localeCompare(t.label);return i===0?e.value.localeCompare(t.value):i});return t.length===0?FALLBACK_MODELS:t.map(({value:e,label:t,hint:n})=>({id:e,label:t,value:e,hint:n,featured:FEATURED_MODEL_IDS.includes(e)||void 0}))}async function buildModelOptions(e,t){try{return modelOptionsFromCatalog(await e(t))}catch{return t?.throwIfAborted(),FALLBACK_MODELS}}function selectModel(t){let r=t.deps??{fetchModels:fetchGatewayCatalog};return{id:`select-model`,async gather({signal:i}){let a=t.presetModel;if(a!==void 0&&a.length>0)return a;let o=await buildModelOptions(r.fetchModels,i),s=t.defaultModel!==void 0&&o.some(e=>e.value===t.defaultModel)?t.defaultModel:o.some(e=>e.value===DEFAULT_AGENT_MODEL_ID)?DEFAULT_AGENT_MODEL_ID:o[0]?.value;return t.asker.ask(select({key:`model`,message:`Which model should your agent use?`,options:o,recommended:s,required:!0,search:!0,placeholder:`type to search`}))},async perform({input:e}){return e},apply(e,t){return{...e,modelId:t}}}}export{fetchGatewayCatalog,modelOptionsFromCatalog,parseGatewayCatalog,selectModel};
1
+ import{select}from"../ask.js";import{z}from"#compiled/zod/index.js";import{DEFAULT_AGENT_MODEL_ID}from"#shared/default-agent-model.js";import{AI_GATEWAY_MODELS_URL,vercelGatewayFetch}from"#internal/gateway.js";const gatewayCatalogModelSchema=z.object({id:z.string(),name:z.string(),type:z.string(),owned_by:z.string(),released:z.number().finite().optional().catch(void 0),tags:z.array(z.string()).optional().catch(void 0),pricing:z.object({service_tiers:z.record(z.string(),z.unknown()).optional().catch(void 0)}).optional().catch(void 0)}),gatewayCatalogSchema=z.object({data:z.array(z.unknown())}).transform(({data:e})=>e.flatMap(e=>{let t=gatewayCatalogModelSchema.safeParse(e);return t.success?[t.data]:[]}));function modelOption(e,t,n,r=!0){return{id:e,label:t,value:e,hint:n,featured:r||void 0}}const FEATURED_MODEL_IDS=[DEFAULT_AGENT_MODEL_ID,`anthropic/claude-opus-4.8`,`openai/gpt-5.5`],FALLBACK_MODELS=[modelOption(DEFAULT_AGENT_MODEL_ID,`GPT-5.6 Luna Fast`,`OpenAI`),modelOption(`anthropic/claude-opus-4.8`,`Claude Opus 4.8`,`Anthropic`),modelOption(`openai/gpt-5.5`,`GPT-5.5`,`OpenAI`),modelOption(`google/gemini-3.5`,`Gemini 3.5`,`Google`,!1)],PROVIDER_BRANDS={openai:`OpenAI`,zai:`Z.AI`,xai:`xAI`,deepseek:`DeepSeek`,moonshotai:`Moonshot AI`};function providerLabel(e){return e.length===0?``:PROVIDER_BRANDS[e]??e.charAt(0).toUpperCase()+e.slice(1)}async function fetchGatewayCatalog(e){let t=new AbortController,n=setTimeout(()=>t.abort(),5e3);try{let n=await vercelGatewayFetch(AI_GATEWAY_MODELS_URL,{signal:e===void 0?t.signal:AbortSignal.any([e,t.signal])});if(!n.ok)throw Error(`AI Gateway model catalog request failed (${n.status}).`);return parseGatewayCatalog(await n.json())}finally{clearTimeout(n)}}function parseGatewayCatalog(e){let t=gatewayCatalogSchema.safeParse(e);if(!t.success)throw Error(`AI Gateway returned an invalid model catalog.`);return t.data}function featuredPriority(e){let t=FEATURED_MODEL_IDS.indexOf(e);return t===-1?FEATURED_MODEL_IDS.length:t}function modelOptionsFromCatalog(e){if(e===void 0)return FALLBACK_MODELS;let t=e.filter(e=>e.type===`language`&&(e.id===DEFAULT_AGENT_MODEL_ID||(e.tags??[]).includes(`web-search`))).map(e=>{let t=e.id.split(`/`)[0]??``;return{value:e.id,label:e.name,hint:providerLabel(t),provider:t,released:e.released}}).sort((e,t)=>{let n=featuredPriority(e.value)-featuredPriority(t.value);if(n!==0)return n;let r=(t.released??-1/0)-(e.released??-1/0);if(r!==0)return r;let i=e.label.localeCompare(t.label);return i===0?e.value.localeCompare(t.value):i});return t.length===0?FALLBACK_MODELS:t.map(({value:e,label:t,hint:n})=>({id:e,label:t,value:e,hint:n,featured:FEATURED_MODEL_IDS.includes(e)||void 0}))}async function buildModelOptions(e,t){try{return modelOptionsFromCatalog(await e(t))}catch{return t?.throwIfAborted(),FALLBACK_MODELS}}function selectModel(t){let r=t.deps??{fetchModels:fetchGatewayCatalog};return{id:`select-model`,async gather({signal:i}){let a=t.presetModel;if(a!==void 0&&a.length>0)return a;let o=await buildModelOptions(r.fetchModels,i),s=t.defaultModel!==void 0&&o.some(e=>e.value===t.defaultModel)?t.defaultModel:o.some(e=>e.value===DEFAULT_AGENT_MODEL_ID)?DEFAULT_AGENT_MODEL_ID:o[0]?.value;return t.asker.ask(select({key:`model`,message:`Which model should your agent use?`,options:o,recommended:s,required:!0,search:!0,placeholder:`type to search`}))},async perform({input:e}){return e},apply(e,t){return{...e,modelId:t}}}}export{fetchGatewayCatalog,modelOptionsFromCatalog,parseGatewayCatalog,selectModel};
@@ -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.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.47.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,n){let r=parseChatGptModelSelection(e);return r===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(r)}),\n${reasoningTemplateLine(n)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,n){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(n.model)!==void 0?renderAgentTemplate(n.model,n.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,n.appName).replaceAll(`__EVE_INIT_MODEL__`,n.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(n.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(n.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(n.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,n.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,n.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,n.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,n.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,n.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,n.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{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.58`,DEFAULT_CONNECT_PACKAGE_VERSION=`1.0.0`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.47.2`,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,n){let r=parseChatGptModelSelection(e);return r===void 0?BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n)):`import { defineAgent } from "eve";\nimport { chatgpt } from "eve/models/openai";\n\nexport default defineAgent({\n model: chatgpt(${JSON.stringify(r)}),\n${reasoningTemplateLine(n)}});\n`}function reasoningTemplateLine(e){return e===void 0||e===`provider-default`?``:` reasoning: "${e}",\n`}function renderTemplate(e,n){return e===BASE_AGENT_TEMPLATE&&parseChatGptModelSelection(n.model)!==void 0?renderAgentTemplate(n.model,n.reasoning):e.replaceAll(`__EVE_INIT_APP_NAME__`,n.appName).replaceAll(`__EVE_INIT_MODEL__`,n.model).replaceAll(`__EVE_INIT_REASONING__`,reasoningTemplateLine(n.reasoning)).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(n.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(n.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(n.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,n.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,n.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,n.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,n.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,n.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,n.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__",
@@ -2,4 +2,4 @@
2
2
  * Model used when an agent does not declare one, baked in by `eve init`, and
3
3
  * pre-selected in the setup model picker.
4
4
  */
5
- export declare const DEFAULT_AGENT_MODEL_ID = "zai/glm-5.2";
5
+ export declare const DEFAULT_AGENT_MODEL_ID = "openai/gpt-5.6-luna-fast";
@@ -1 +1 @@
1
- const DEFAULT_AGENT_MODEL_ID=`zai/glm-5.2`;export{DEFAULT_AGENT_MODEL_ID};
1
+ const DEFAULT_AGENT_MODEL_ID=`openai/gpt-5.6-luna-fast`;export{DEFAULT_AGENT_MODEL_ID};
@@ -21,9 +21,7 @@ For a static AI Gateway model ID, you can make the same source change from the
21
21
  project root with `eve set --model anthropic/claude-opus-4.8` or from the local
22
22
  dev TUI with `/model anthropic/claude-opus-4.8`.
23
23
 
24
- The root `agent.ts` can be omitted when no runtime config is needed. eve then selects its default `agent.ts` source at the same slot, configured with `zai/glm-5.2`; authoring the file replaces that source. GLM 5.2 does not support image input; choose a
25
- vision-capable model or [route image inputs to Gemini
26
- Flash](./guides/dynamic-capabilities#route-image-inputs-to-a-vision-model).
24
+ The root `agent.ts` can be omitted when no runtime config is needed. eve then selects its default `agent.ts` source at the same slot, configured with `openai/gpt-5.6-luna-fast`; authoring the file replaces that source.
27
25
  When `agent.ts` is present, `model` is required.
28
26
 
29
27
  A config that selects a static Gateway model is compile-only. A config that contains a dynamic model or a direct-provider `LanguageModel` remains a runtime entry because eve must resolve that authored value while the agent runs. See [Authored module lifecycle](./reference/typescript-api#authored-module-lifecycle).
@@ -72,15 +72,17 @@ Every [subagent](../subagents) starts with its own fresh state, whether it's a b
72
72
  `defineState` holds conversation-scoped working memory that lives and dies with
73
73
  the session, including counters, the current plan, and what the user has told
74
74
  you this conversation. It is the agent's short-term memory, persisted durably
75
- for the life of the session. For context that must outlive a session, connect a
76
- first-class [memory provider](../memory) to your own application store. Use a
77
- general [connection](../connections) instead when the data should be queried
78
- only through explicit model tool calls rather than recalled automatically.
75
+ for the life of the session. For context that must outlive a session, configure
76
+ a first-class [memory provider](../memory). Use the built-in file provider, a
77
+ third-party provider, or a custom provider for application-specific storage and
78
+ retrieval. Use a general [connection](../connections) instead when the data
79
+ should be queried only through explicit model tool calls rather than recalled
80
+ automatically.
79
81
 
80
82
  ## What to read next
81
83
 
82
84
  - Read state inside dynamic resolvers → [Dynamic capabilities](../guides/dynamic-capabilities)
83
85
  - How step durability works → [Execution model & durability](../concepts/execution-model-and-durability)
84
86
  - The `ctx` accessors available alongside state → [TypeScript API Reference](../reference/typescript-api)
85
- - Tenant-scoped long-term memory in your own store → [Multi-tenant memory](../patterns/multi-tenant-memory)
87
+ - Tenant-scoped long-term memory with any provider → [Multi-tenant memory](../patterns/multi-tenant-memory)
86
88
  - First-class recall, capture, and provider tools → [Memory](../memory)
package/docs/memory.md CHANGED
@@ -1,24 +1,47 @@
1
1
  ---
2
2
  title: "Memory"
3
- description: "Recall and capture scoped, cross-session context with built-in or custom eve memory providers."
3
+ description: "Connect scoped, cross-session context to built-in, third-party, or custom memory providers."
4
4
  ---
5
5
 
6
- Memory connects an agent to application-owned storage that can outlive one
7
- session. A provider recalls relevant context before a turn, can capture the
8
- settled conversation afterward, and can expose tools that operate on the same
9
- locked scope.
6
+ Memory is a provider-backed capability for context that outlives one session.
7
+ Each memory slot binds a provider to an eve-managed scope and visibility
8
+ policy. The provider decides how to retrieve relevant context, whether to
9
+ capture conversation history, and which model-facing operations to offer.
10
10
 
11
- eve owns the lifecycle and model-facing history. A provider owns storage,
12
- retrieval, retention, and deletion. Use the built-in bounded file provider for
13
- model-maintained facts, or implement a provider for application-specific
14
- retrieval and capture.
11
+ Breaking down the boundary between eve and the provider:
12
+
13
+ | eve owns | The provider owns |
14
+ | ---------------------------------------------------------------- | ------------------------------------------------------- |
15
+ | Path-derived slots and qualified provider-tool names | Storage and indexing |
16
+ | Namespace and trusted scope resolution | Retrieval, ranking, and formatting |
17
+ | Lifecycle timing, recalled-message attribution, and supersession | Capture and extraction |
18
+ | Recall visibility in model context | Retention, deletion, and model-facing memory operations |
19
+
20
+ This lets a bounded text file, a hosted semantic-memory service, or an
21
+ application-specific store participate in the same agent lifecycle without
22
+ sharing a storage model.
23
+
24
+ ## Choose a provider
25
+
26
+ Every memory slot needs a provider. Choose one based on how the agent should
27
+ recall and maintain memory:
28
+
29
+ | Provider | Status | Use it for |
30
+ | --------------- | -------------- | ------------------------------------------------------------------------- |
31
+ | `fileMemory()` | Built into eve | A bounded, model-maintained list of durable facts and preferences |
32
+ | Supermemory | Coming soon | The first third-party implementation of the eve provider contract |
33
+ | Custom provider | Supported | Application-specific retrieval, capture, retention, or model-facing tools |
34
+
35
+ Supermemory is building the first third-party provider for eve. It is not
36
+ available yet; until it is released, use `fileMemory()` or implement the
37
+ provider contract described below.
15
38
 
16
39
  ## Use file memory
17
40
 
18
- `fileMemory()` keeps one indexed document for each resolved scope and gives the
19
- model `save_memory` and `remove_memory` tools. It recalls the document before
20
- each turn and after compaction, but does not automatically extract facts from
21
- the conversation.
41
+ `fileMemory()` keeps one indexed document for each resolved scope. The document
42
+ lives in the selected backend, not in the agent's sandbox filesystem. The
43
+ provider recalls it before each turn and after compaction, and gives the model
44
+ `save_memory` and `remove_memory` tools.
22
45
 
23
46
  ```ts title="agent/memory/profile.ts"
24
47
  import { defineMemory } from "eve/memory";
@@ -32,18 +55,20 @@ export default defineMemory({
32
55
  });
33
56
  ```
34
57
 
35
- The `profile` slot exposes `profile__save_memory` and
36
- `profile__remove_memory`. Saved entries are normalized, assigned permanent
37
- numeric indexes, and recalled as one stable keyed message. Removing an entry
38
- replaces the previous recalled document at the next boundary, including when
39
- the final entry is removed, so stale content does not remain in model context.
58
+ The filename creates the `profile` slot, so the provider tools are exposed as
59
+ `profile__save_memory` and `profile__remove_memory`. The slot description is
60
+ prepended to both tool descriptions to tell the model what belongs in this
61
+ slot.
62
+
63
+ File memory does not automatically extract facts from a conversation. The
64
+ model decides when to call its save and remove tools. Entries receive permanent
65
+ numeric indexes, and the provider recalls the document as one stable keyed
66
+ message so updated or empty documents replace stale context.
40
67
 
41
68
  The provider rejects rather than truncates or evicts data. `maxCharacters`
42
69
  defaults to 4,000 characters and caps the exact recalled message, including
43
- its heading, removal guidance, indexes, separators, and saved text.
44
- `save_memory` rejects an entry when the resulting message would exceed that
45
- limit. Each normalized entry can contain up to 2,048 UTF-8 bytes, and the
46
- stored document can contain up to 65,536 bytes:
70
+ its heading and removal guidance. Each normalized entry can contain up to
71
+ 2,048 UTF-8 bytes, and the stored document can contain up to 65,536 bytes:
47
72
 
48
73
  ```ts
49
74
  provider: fileMemory({ maxCharacters: 8_000 });
@@ -71,102 +96,68 @@ provider: fileMemory({ backend: inMemory() });
71
96
  ```
72
97
 
73
98
  `inMemory()` loses its contents when its backend instance or process is
74
- replaced. For another durable store, implement `MemoryDocumentBackend` from
75
- `eve/memory/file`. Its `write()` method conditionally replaces the complete
99
+ replaced. For another durable document store, implement `MemoryDocumentBackend`
100
+ from `eve/memory/file`. Its `write()` method conditionally replaces the complete
76
101
  document and must throw `MemoryDocumentConflictError` when `expectedVersion`
77
- is stale. Use `vercelBlob()` from `eve/memory/file/vercel` when you want to
78
- configure Vercel Blob credentials or an object prefix explicitly.
102
+ is stale. Use `vercelBlob()` from `eve/memory/file/vercel` to configure Vercel
103
+ Blob credentials or an object prefix explicitly.
104
+
105
+ A file backend changes where `fileMemory()` stores its document. It does not
106
+ change file memory's recall behavior or tools. Implement a memory provider
107
+ instead when you need semantic retrieval, automatic capture, or different
108
+ model-facing operations.
79
109
 
80
110
  ## Add a memory slot
81
111
 
82
112
  Create `agent/memory.ts` for one slot named `memory`, or use
83
- `agent/memory/<slot>.ts` for named slots. These forms are mutually exclusive.
84
-
85
- ```ts title="agent/memory/profile.ts"
86
- import { defineMemory, type MemoryOperationContext } from "eve/memory";
87
- import { byPrincipal } from "eve/memory/scope";
88
- import { defineTool } from "eve/tools";
89
- import { z } from "zod";
90
- import { profileStore } from "../lib/profile-store";
91
-
92
- async function recallProfile(ctx: MemoryOperationContext) {
93
- const profile = await profileStore.get(ctx.memory.scope.key);
94
- if (profile === null) return null;
95
-
96
- return {
97
- messages: [{ id: "profile", content: JSON.stringify(profile) }],
98
- };
99
- }
100
-
101
- export default defineMemory({
102
- description: "Manage durable facts and preferences for the current caller.",
103
- scope: byPrincipal,
104
- provider: {
105
- recall: {
106
- "turn.started": recallProfile,
107
- "compaction.completed": recallProfile,
108
- },
109
-
110
- capture: {
111
- async "turn.completed"(ctx) {
112
- await profileStore.observe(ctx.memory.scope.key, ctx.messages, {
113
- operationId: ctx.operationId,
114
- });
115
- },
116
- },
117
-
118
- async tools(ctx) {
119
- return {
120
- save: defineTool({
121
- description: "Save one durable profile fact.",
122
- inputSchema: z.object({ key: z.string(), value: z.string() }),
123
- async execute(input) {
124
- await profileStore.put(ctx.memory.scope.key, input);
125
- return { saved: true };
126
- },
127
- }),
128
- };
129
- },
130
- },
131
- });
113
+ `agent/memory/<slot>.ts` for multiple named slots. The flat file and directory
114
+ forms are mutually exclusive:
115
+
116
+ ```text
117
+ agent/
118
+ memory/
119
+ profile.ts
120
+ workspace.ts
132
121
  ```
133
122
 
134
- The filename is the slot name. The provider tool above is exposed as
135
- `profile__save`; the slot description is prepended to the tool description.
136
- Provider tools are ordinary `defineTool()` values, so schemas, approvals, and
137
- `toModelOutput` work normally. Their callbacks remain replayable after a
138
- process restart or deployment.
139
-
140
- Use `defineMemoryProvider()` when several slots share one provider or when you
141
- want its contract checked separately:
123
+ Each slot independently binds a provider, description, scope, namespace, and
124
+ visibility policy. The same provider can back several slots without merging
125
+ their recalled context or tools. The default namespace includes the slot name,
126
+ so `profile` and `workspace` remain separate even if both resolve the same
127
+ scope.
142
128
 
143
- ```ts
144
- import { defineMemoryProvider, type MemoryOperationContext } from "eve/memory";
129
+ For example, `profile.ts` can use `fileMemory()` with `byPrincipal`, while
130
+ `workspace.ts` uses another `fileMemory()` instance with a trusted workspace
131
+ scope:
145
132
 
146
- async function recall(ctx: MemoryOperationContext) {
147
- return await recallFromStore(ctx.memory.scope.key, ctx.messages);
148
- }
133
+ ```ts title="agent/memory/workspace.ts"
134
+ import { defineMemory } from "eve/memory";
135
+ import { fileMemory } from "eve/memory/file";
149
136
 
150
- export const provider = defineMemoryProvider({
151
- recall: {
152
- "turn.started": recall,
153
- "compaction.completed": recall,
137
+ export default defineMemory({
138
+ description: "Remember shared conventions for the authenticated workspace.",
139
+ provider: fileMemory(),
140
+ scope(ctx) {
141
+ const workspaceId = ctx.session.auth.current?.attributes.workspaceId;
142
+ return typeof workspaceId === "string" ? workspaceId : null;
154
143
  },
155
144
  });
156
145
  ```
157
146
 
158
147
  ## Choose a scope
159
148
 
160
- `scope` decides who or what shares memory. Set it to a string or `null`, or use
161
- a resolver that returns a string, a tuple of strings, or `null`. Resolve tenant
162
- and caller identity from trusted authentication or channel metadata, never from
163
- model input:
149
+ `scope` decides who or what shares a slot's memory. Set it to a string or
150
+ `null`, or use a resolver that returns a string, a tuple of strings, or `null`.
151
+ Resolve tenant and caller identity from trusted authentication or channel
152
+ metadata, never from model input:
164
153
 
165
154
  ```ts title="agent/memory/account.ts"
166
155
  import { defineMemory } from "eve/memory";
156
+ import { fileMemory } from "eve/memory/file";
167
157
 
168
158
  export default defineMemory({
169
- scope: (ctx) => {
159
+ provider: fileMemory(),
160
+ scope(ctx) {
170
161
  const caller = ctx.session.auth.current;
171
162
  const tenantId = caller?.attributes.tenantId;
172
163
 
@@ -176,7 +167,6 @@ export default defineMemory({
176
167
 
177
168
  return [tenantId, caller.principalId];
178
169
  },
179
- provider,
180
170
  });
181
171
  ```
182
172
 
@@ -188,37 +178,66 @@ that returned `null`, without logging the resolved value.
188
178
  `byPrincipal` uses `auth.current`. It disables memory for anonymous and runtime
189
179
  principals and returns the shared `local-dev` scope during local development.
190
180
  Use a custom resolver when the boundary also needs a tenant, channel, or
191
- conversation identifier.
181
+ conversation identifier. See [Multi-tenant memory](./patterns/multi-tenant-memory)
182
+ for a complete scoped setup.
192
183
 
193
- eve validates the namespace and scope, then gives the provider:
184
+ eve validates and locks the namespace and scope before calling the provider.
185
+ Every provider operation receives:
194
186
 
195
187
  - `memory.scope.key`: a versioned, opaque digest for storage lookup;
196
188
  - `memory.scope.namespace`: the resolved namespace;
197
189
  - `memory.scope.value`: the resolved string or tuple;
198
190
  - `memory.slot`: the path-derived slot name.
199
191
 
200
- Use `memory.scope.key` as the provider partition key. It preserves tuple
201
- boundaries and does not persist raw scope components in eve's durable
202
- attribution.
192
+ Providers must use `memory.scope.key` as the partition key for every read and
193
+ write. The key preserves tuple boundaries and keeps raw scope components out of
194
+ eve's durable attribution.
195
+
196
+ ## Choose visibility
197
+
198
+ `visibility` controls which previously recalled records eve includes in model
199
+ context after a slot's scope changes. It does not change the scope passed to
200
+ the provider.
201
+
202
+ | Value | Behavior after a scope change |
203
+ | ------------------- | ------------------------------------------------------------------ |
204
+ | `"scope"` (default) | Hide recalled records from the slot's earlier scope |
205
+ | `"session"` | Keep earlier recalled records visible within the current namespace |
206
+
207
+ Use `"session"` only when every scope that can appear in the session belongs
208
+ to one trusted audience:
209
+
210
+ ```ts
211
+ export default defineMemory({
212
+ provider: fileMemory(),
213
+ scope: byPrincipal,
214
+ visibility: "session",
215
+ });
216
+ ```
217
+
218
+ Namespace remains an isolation boundary in both modes. Applications that need
219
+ hard isolation between participants must use separate sessions; visibility
220
+ cannot undo information already included in an assistant response.
203
221
 
204
222
  ## Choose a namespace
205
223
 
206
224
  The namespace separates an application's memory domains before scope is
207
- applied. Omit `namespace` for `defaultNamespace`, which includes the graph
225
+ applied. Omit `namespace` to use `defaultNamespace`, which includes the graph
208
226
  node and slot plus a deployment-aware identity:
209
227
 
210
- - production and other Vercel environments use the project and environment;
211
- - Preview also uses the branch or deployment identity;
212
- - local development uses a digest of the application root, never the raw path.
228
+ - Production and other Vercel environments use the project and environment.
229
+ - Preview also uses the branch or deployment identity.
230
+ - Local development uses a digest of the application root, never the raw path.
213
231
 
214
- Redeployments keep the same production namespace. Preview branches do not
215
- share memory accidentally. Set a string or resolver for an explicit domain:
232
+ Redeployments keep the same production namespace, while unrelated Preview
233
+ branches do not share memory accidentally. Set a string or resolver when you
234
+ need an explicit application domain:
216
235
 
217
236
  ```ts
218
237
  export default defineMemory({
219
238
  namespace: "acme-support-v1",
239
+ provider: fileMemory(),
220
240
  scope: byPrincipal,
221
- provider,
222
241
  });
223
242
  ```
224
243
 
@@ -226,13 +245,84 @@ A custom namespace is complete; eve adds no hidden suffix. Returning `null`
226
245
  disables the slot. Scope resolves first, so a disabled scope never invokes the
227
246
  namespace resolver.
228
247
 
248
+ ## Build a custom provider
249
+
250
+ Build a provider when the available providers do not match your storage or
251
+ retrieval model. Most application authors do not need this layer: a provider
252
+ package should expose a `MemoryProvider` or a configured provider factory that
253
+ you pass to `defineMemory()`.
254
+
255
+ A provider can implement three surfaces:
256
+
257
+ | Surface | Responsibility |
258
+ | --------- | ------------------------------------------------------------------------ |
259
+ | `recall` | Return relevant messages at turn start and, optionally, after compaction |
260
+ | `capture` | Observe settled history after a turn or before compaction |
261
+ | `tools` | Return model-facing operations bound to the same locked scope |
262
+
263
+ Each helper
264
+ deliberately throws until you connect it to a database or memory-service SDK:
265
+
266
+ ```ts title="agent/lib/semantic-memory-provider.ts"
267
+ import {
268
+ defineMemoryProvider,
269
+ type MemoryOperationContext,
270
+ type MemoryRecallResult,
271
+ type MemoryToolsContext,
272
+ type MemoryToolSet,
273
+ } from "eve/memory";
274
+
275
+ async function recallFromYourService(_ctx: MemoryOperationContext): Promise<MemoryRecallResult> {
276
+ throw new Error("Replace with retrieval from your memory service.");
277
+ }
278
+
279
+ async function captureWithYourService(_ctx: MemoryOperationContext): Promise<void> {
280
+ throw new Error("Replace with capture into your memory service.");
281
+ }
282
+
283
+ async function createToolsForYourService(_ctx: MemoryToolsContext): Promise<MemoryToolSet | null> {
284
+ throw new Error("Replace with tools backed by your memory service.");
285
+ }
286
+
287
+ export const semanticMemoryProvider = defineMemoryProvider({
288
+ recall: {
289
+ "turn.started": recallFromYourService,
290
+ "compaction.completed": recallFromYourService,
291
+ },
292
+ capture: {
293
+ "turn.completed": captureWithYourService,
294
+ "compaction.requested": captureWithYourService,
295
+ },
296
+ tools: createToolsForYourService,
297
+ });
298
+ ```
299
+
300
+ Omit any optional handler that the provider does not need. For example,
301
+ `fileMemory()` implements recall and tools but no automatic capture. After
302
+ replacing the stubs, bind the provider to a slot like any other provider:
303
+
304
+ ```ts title="agent/memory/profile.ts"
305
+ import { defineMemory } from "eve/memory";
306
+ import { byPrincipal } from "eve/memory/scope";
307
+ import { semanticMemoryProvider } from "../lib/semantic-memory-provider";
308
+
309
+ export default defineMemory({
310
+ provider: semanticMemoryProvider,
311
+ scope: byPrincipal,
312
+ });
313
+ ```
314
+
315
+ Provider tools are ordinary `defineTool()` values, so schemas, approvals, and
316
+ `toModelOutput` work normally. eve qualifies each returned key with the slot
317
+ name and keeps its callback replayable after a process restart or deployment.
318
+
229
319
  ## Recall behavior
230
320
 
231
- Register recall handlers under their lifecycle keys. `"turn.started"` is
232
- required and runs before the model. `"compaction.completed"` is optional and
233
- runs after a compaction checkpoint. A recall handler returns `{ messages }`,
234
- `null`, or `undefined`. Each recalled item becomes an untrusted user-role
235
- message; provider content is never promoted to system instructions.
321
+ `recall["turn.started"]` is required and runs before the model.
322
+ `recall["compaction.completed"]` is optional and runs after a compaction
323
+ checkpoint. A recall handler returns `{ messages }`, `null`, or `undefined`.
324
+ Each recalled item becomes an untrusted user-role message; provider content is
325
+ never promoted to system instructions.
236
326
 
237
327
  ```ts
238
328
  return {
@@ -250,13 +340,8 @@ identical. Omitting an earlier item from a later result does not delete it.
250
340
 
251
341
  All active slots lock their scopes before any recall runs. They see the same
252
342
  pre-recall history, and eve commits their validated results atomically. Each
253
- call receives a stable `operationId`; use it as an idempotency key for writes
254
- performed by capture handlers.
255
-
256
- The default `visibility: "scope"` hides a slot's prior recalled records when
257
- its scope changes. Set `visibility: "session"` only when those records are
258
- safe to retain for the rest of the session across scope changes. Namespace
259
- and slot boundaries still apply.
343
+ call receives a stable `operationId`; providers can use it as an idempotency
344
+ key for writes performed by capture handlers.
260
345
 
261
346
  ## Lifecycle and compaction
262
347
 
@@ -293,7 +378,7 @@ provider's external store. A later turn can recall that data again.
293
378
  - A completed-turn capture failure is diagnosed after the response and does
294
379
  not rewrite the completed turn.
295
380
 
296
- Keep provider operations idempotent, enforce backend size and retention
381
+ Provider operations should be idempotent, enforce their own size and retention
297
382
  policies, and treat recalled content as user-controlled data.
298
383
 
299
384
  ## Limits and overrides
@@ -311,8 +396,8 @@ with the consuming agent or subagent.
311
396
 
312
397
  ## What to read next
313
398
 
314
- - [Multi-tenant memory](./patterns/multi-tenant-memory): define a tenant and
315
- caller scope for an application store.
399
+ - [Multi-tenant memory](./patterns/multi-tenant-memory): isolate any provider by
400
+ authenticated tenant and caller.
316
401
  - [State](./concepts/state): keep durable working data inside one session.
317
402
  - [Default harness](./concepts/default-harness): understand compaction and
318
403
  context controls.