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.
- package/CHANGELOG.md +13 -0
- package/dist/src/cli/dev/run-interactive-ui.d.ts +19 -0
- package/dist/src/cli/dev/run-interactive-ui.js +1 -0
- package/dist/src/cli/dev/tui/agent-header.js +1 -1
- package/dist/src/cli/dev/tui/remote-auth-command.js +1 -1
- package/dist/src/cli/dev/tui/remote-connection.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +6 -0
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/terminal-renderer.d.ts +6 -0
- package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
- package/dist/src/cli/dev/tui/tui.d.ts +16 -0
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/cli/run.js +3 -3
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/nitro/host/start-development-server.js +1 -1
- package/dist/src/services/dev-client.js +1 -1
- package/dist/src/setup/boxes/select-model.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/shared/default-agent-model.d.ts +1 -1
- package/dist/src/shared/default-agent-model.js +1 -1
- package/docs/agent-config.md +1 -3
- package/docs/concepts/state.md +7 -5
- package/docs/memory.md +210 -125
- package/docs/patterns/multi-tenant-memory.md +45 -111
- package/docs/reference/cli.md +1 -1
- package/package.json +1 -1
|
@@ -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{
|
|
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{
|
|
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,`
|
|
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.
|
|
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__",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const DEFAULT_AGENT_MODEL_ID=`
|
|
1
|
+
const DEFAULT_AGENT_MODEL_ID=`openai/gpt-5.6-luna-fast`;export{DEFAULT_AGENT_MODEL_ID};
|
package/docs/agent-config.md
CHANGED
|
@@ -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 `
|
|
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).
|
package/docs/concepts/state.md
CHANGED
|
@@ -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,
|
|
76
|
-
first-class [memory provider](../memory)
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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: "
|
|
3
|
+
description: "Connect scoped, cross-session context to built-in, third-party, or custom memory providers."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Memory
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
each turn and after compaction,
|
|
21
|
-
|
|
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
|
|
36
|
-
`profile__remove_memory`.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
44
|
-
|
|
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`
|
|
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`
|
|
78
|
-
|
|
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.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`
|
|
138
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
147
|
-
|
|
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
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
161
|
-
a resolver that returns a string, a tuple of strings, or `null`.
|
|
162
|
-
and caller identity from trusted authentication or channel
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
201
|
-
boundaries and
|
|
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`
|
|
225
|
+
applied. Omit `namespace` to use `defaultNamespace`, which includes the graph
|
|
208
226
|
node and slot plus a deployment-aware identity:
|
|
209
227
|
|
|
210
|
-
-
|
|
211
|
-
- Preview also uses the branch or deployment identity
|
|
212
|
-
-
|
|
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
|
|
215
|
-
share memory accidentally. Set a string or resolver
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
|
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
|
-
|
|
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):
|
|
315
|
-
|
|
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.
|