UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

1 lines 8.56 kB
import{createHash}from"node:crypto";import{createFrameworkUserMessage}from"#harness/messages.js";import{identityHistoryViewProjector}from"#shared/history-view.js";const MEMORY_NAMESPACE_MAX_BYTES=1024,MEMORY_SCOPE_COMPONENT_MAX_BYTES=1024,MEMORY_SCOPE_TUPLE_MAX_COMPONENTS=16,MEMORY_CANONICAL_KEY_INPUT_MAX_BYTES=4096,MEMORY_ITEM_ID_MAX_BYTES=1024,MEMORY_RAW_RECORD_MAX_COUNT=512,MEMORY_RAW_RECORD_MAX_BYTES=262144,MEMORY_MESSAGE_METADATA_KEY=`eve.memory`,MEMORY_SESSION_STATE_KEY=`eve.memory`;function createMemoryLock(e){validateMemoryNamespace(e.namespace),validateMemoryScopeValue(e.scope);let t=encodeScalar(`namespace`,e.namespace),n=encodeScope(e.scope);if(t.byteLength+n.byteLength>4096)throw Error(`Memory slot "${e.slot}" namespace and scope encoding exceeds ${MEMORY_CANONICAL_KEY_INPUT_MAX_BYTES} UTF-8 bytes.`);let r=digest(`memns1_`,t),i=digest(`memscope1_`,n),a=Buffer.concat([Buffer.from(`eve-memory-composite-v1\0`),lengthPrefix(Buffer.from(r)),lengthPrefix(Buffer.from(i))]),o=Object.freeze({key:digest(`memscope1_`,a),namespace:e.namespace,value:Array.isArray(e.scope)?Object.freeze([...e.scope]):e.scope});return Object.freeze({namespaceKey:r,scope:o,scopeKey:i,slot:e.slot,turn:e.turn,visibility:e.visibility})}function validateMemoryRecallResult(e,t){if(e==null)return[];if(typeof e!=`object`||Array.isArray(e))throw Error(`Memory slot "${t}" recall() must return { messages }, null, or undefined.`);let n=Object.keys(e).filter(e=>e!==`messages`);if(n.length>0)throw Error(`Memory slot "${t}" recall() returned unknown key(s): ${n.join(`, `)}.`);if(!Array.isArray(e.messages))throw Error(`Memory slot "${t}" recall().messages must be an array.`);let r=new Set;return e.messages.map((e,n)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Memory slot "${t}" recall message ${n} must be an object.`);let i=Object.keys(e).filter(e=>e!==`content`&&e!==`id`);if(i.length>0)throw Error(`Memory slot "${t}" recall message ${n} has unknown key(s): ${i.join(`, `)}.`);if(typeof e.content!=`string`||e.content.trim().length===0)throw Error(`Memory slot "${t}" recall message ${n} content must be non-blank.`);if(e.id===void 0)return Object.freeze({content:e.content});if(typeof e.id!=`string`||e.id.length===0)throw Error(`Memory slot "${t}" recall message ${n} id must be non-empty.`);if(utf8Bytes(e.id)>1024)throw Error(`Memory slot "${t}" recall message ${n} id exceeds ${MEMORY_ITEM_ID_MAX_BYTES} UTF-8 bytes.`);if(r.has(e.id))throw Error(`Memory slot "${t}" recall() returned duplicate id "${e.id}".`);return r.add(e.id),Object.freeze({content:e.content,itemKey:digest(`memitem1_`,encodeScalar(`item`,e.id))})})}function applyMemoryRecallBatches(e){let t={...readMemorySessionState(e.state).operationDigests},n=latestKeyedRecords(e.history),r=[];for(let i of e.batches){let e=recallBatchDigest(i.messages),a=t[i.operationId];if(a!==void 0){if(a!==e)throw Error(`Memory recall operation "${i.operationId}" replayed with a different result.`);continue}t[i.operationId]=e;for(let[e,t]of i.messages.entries()){let a={batchIndex:e,itemKey:t.itemKey,namespaceKey:i.lock.namespaceKey,operationId:i.operationId,scopeKey:i.lock.scopeKey,slot:i.lock.slot,version:1};if(t.itemKey!==void 0){let e=memoryItemIdentity(a);if(n.get(e)?.content===t.content)continue;n.set(e,{attribution:a,content:t.content})}r.push(attributeMemoryRecord(t.content,a))}}let i=Object.fromEntries(Object.entries(t).slice(-1024));return{history:[...e.history,...r],state:writeMemorySessionState(e.state,{locks:Object.fromEntries(e.batches.map(e=>[e.lock.slot,e.lock])),operationDigests:i})}}function projectMemoryHistory(e){let t=latestKeyedRecordIndexes(e.messages);return e.messages.flatMap((n,r)=>{let i=readMemoryRecordAttribution(n);if(i===null)return[n];let a=e.locks[i.slot];return a===void 0||a.namespaceKey!==i.namespaceKey||a.visibility===`scope`&&a.scopeKey!==i.scopeKey||i.itemKey!==void 0&&t.get(memoryItemIdentity(i))!==r?[]:[stripMemoryRecordAttribution(n)]})}function projectMemoryHistoryFromSessionState(e){let t=identityHistoryViewProjector(e);return projectMemoryHistory({locks:readMemorySessionState(e.state).locks,messages:t})}function canonicalizeMemoryRecords(e){let t=latestKeyedRecordIndexes(e),n=[],r=[];for(let[i,a]of e.entries()){let e=readMemoryRecordAttribution(a);if(e===null){r.push(a);continue}(e.itemKey===void 0||t.get(memoryItemIdentity(e))===i)&&n.push(a)}return{memory:n,ordinary:r}}function shouldCanonicalizeMemory(e){let t=0,n=0;for(let r of e)readMemoryRecordAttribution(r)!==null&&(t+=1,n+=serializedBytes(r));return t>512||n>262144}function clearMemorySessionState(e){if(e===void 0||!Object.hasOwn(e,MEMORY_SESSION_STATE_KEY))return e??{};let{[MEMORY_SESSION_STATE_KEY]:t,...n}=e;return n}function readMemoryLocks(e){return readMemorySessionState(e).locks}function validateMemoryNamespace(e){if(e.trim().length===0)throw Error(`Memory namespace must be non-empty.`);if(utf8Bytes(e)>1024)throw Error(`Memory namespace exceeds ${MEMORY_NAMESPACE_MAX_BYTES} UTF-8 bytes.`)}function validateMemoryScopeValue(e){let t=Array.isArray(e)?e:[e];if(Array.isArray(e)&&t.length>16)throw Error(`Memory scope tuple exceeds 16 components.`);if(t.length===0)throw Error(`Memory scope tuple must not be empty.`);for(let[e,n]of t.entries()){if(typeof n!=`string`||n.trim().length===0)throw Error(`Memory scope component ${e} must be a non-empty string.`);if(utf8Bytes(n)>1024)throw Error(`Memory scope component ${e} exceeds ${MEMORY_SCOPE_COMPONENT_MAX_BYTES} UTF-8 bytes.`)}}function encodeScope(e){return typeof e==`string`?encodeScalar(`scope-scalar`,e):Buffer.concat([Buffer.from(`scope-tuple-v1\0`),uint32(e.length),...e.map(e=>lengthPrefix(Buffer.from(e,`utf8`)))])}function encodeScalar(e,t){return Buffer.concat([Buffer.from(`${e}-v1\0`),lengthPrefix(Buffer.from(t,`utf8`))])}function lengthPrefix(e){return Buffer.concat([uint32(e.byteLength),e])}function uint32(e){let t=Buffer.allocUnsafe(4);return t.writeUInt32BE(e),t}function digest(e,t){return`${e}${createHash(`sha256`).update(t).digest(`base64url`)}`}function recallBatchDigest(e){return createHash(`sha256`).update(JSON.stringify(e)).digest(`base64url`)}function attributeMemoryRecord(e,t){return createFrameworkUserMessage(`memory.load`,e,{[MEMORY_MESSAGE_METADATA_KEY]:t})}function readMemoryRecordAttribution(e){if(typeof e!=`object`||!e)return null;let t=Reflect.get(e,`metadata`);if(typeof t!=`object`||!t)return null;let n=Reflect.get(t,MEMORY_MESSAGE_METADATA_KEY);if(typeof n!=`object`||!n)return null;let r=n;if(r.version!==1||typeof r.batchIndex!=`number`||typeof r.namespaceKey!=`string`||typeof r.operationId!=`string`||typeof r.scopeKey!=`string`||typeof r.slot!=`string`||r.itemKey!==void 0&&typeof r.itemKey!=`string`)throw Error(`Durable history contains invalid eve memory attribution.`);return r}function stripMemoryRecordAttribution(e){let t=Reflect.get(e,`metadata`);if(typeof t!=`object`||!t)return e;let{[MEMORY_MESSAGE_METADATA_KEY]:n,...r}=t,{metadata:i,...a}=e;return Object.keys(r).length===0?a:Object.assign(a,{metadata:r})}function latestKeyedRecordIndexes(e){let t=new Map;for(let[n,r]of e.entries()){let e=readMemoryRecordAttribution(r);e?.itemKey!==void 0&&t.set(memoryItemIdentity(e),n)}return t}function latestKeyedRecords(e){let t=new Map;for(let n of e){let e=readMemoryRecordAttribution(n);if(e?.itemKey===void 0)continue;let r=Reflect.get(n,`content`),i=typeof r==`string`?r:``;t.set(memoryItemIdentity(e),{attribution:e,content:i})}return t}function memoryItemIdentity(e){return JSON.stringify([e.slot,e.namespaceKey,e.scopeKey,e.itemKey])}function readMemorySessionState(e){let t=e?.[MEMORY_SESSION_STATE_KEY];if(typeof t!=`object`||!t||Array.isArray(t))return{locks:{},operationDigests:{}};let n=Reflect.get(t,`locks`),r=Reflect.get(t,`operationDigests`);return{locks:typeof n==`object`&&n&&!Array.isArray(n)?n:{},operationDigests:typeof r==`object`&&r&&!Array.isArray(r)?r:{}}}function writeMemorySessionState(e,t){return{...e,[MEMORY_SESSION_STATE_KEY]:t}}function serializedBytes(e){return Buffer.byteLength(JSON.stringify(e),`utf8`)}function utf8Bytes(e){return Buffer.byteLength(e,`utf8`)}export{MEMORY_CANONICAL_KEY_INPUT_MAX_BYTES,MEMORY_ITEM_ID_MAX_BYTES,MEMORY_NAMESPACE_MAX_BYTES,MEMORY_RAW_RECORD_MAX_BYTES,MEMORY_RAW_RECORD_MAX_COUNT,MEMORY_SCOPE_COMPONENT_MAX_BYTES,MEMORY_SCOPE_TUPLE_MAX_COMPONENTS,applyMemoryRecallBatches,canonicalizeMemoryRecords,clearMemorySessionState,createMemoryLock,projectMemoryHistory,projectMemoryHistoryFromSessionState,readMemoryLocks,shouldCanonicalizeMemory,validateMemoryRecallResult};