eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 10.3 kB
JavaScript
import{toError}from"#shared/errors.js";import{createEventDeduper}from"#protocol/event-dedupe.js";import{isCurrentTurnBoundaryEvent}from"#protocol/message.js";import{updatePendingAuthorizations}from"#client/session-utils.js";import{Client}from"#client/client.js";import{consumeMessageResponse,getMessageResponseDeliveryId}from"#client/message-response.js";import{SessionEventStream}from"#client/session-event-stream.js";import{EveAgentProjection}from"#client/eve-agent-projection.js";import{OptimisticMessageSubmissions}from"#client/optimistic-message-submissions.js";import{assertExclusiveTurnInput,createAbortSignal,createActiveTurn,followSteeredTurns,isAbortError,isSettledSessionTail,toTerminalStreamFailureError,waitWithSignal}from"#client/eve-agent-store-helpers.js";const detachStore=Symbol(`detachEveAgentStore`),attachStore=Symbol(`attachEveAgentStore`);var EveAgentStore=class{#e;#t;#n=!1;#r;#i=new Set;#a;#o;#s;#c=new Set;#l=createEventDeduper();#u;#d={};#f;#p;#m;#h=0;#g;#_;#v;#y;#b;#x=`ready`;constructor(e){this.#t=e.prewarm??!1,this.#a=e.session!==void 0,this.#e=this.#a?void 0:new Client({auth:e.auth,headers:e.headers,host:e.host??``});let t=[];for(let n of e.initialEvents??[])this.#l.admit(n)&&t.push(n),updatePendingAuthorizations(this.#i,n);this.#p=t,this.#s=new EveAgentProjection(e.reducer,this.#p),this.#o=e.optimistic??!0,this.#m=new OptimisticMessageSubmissions(this.#s,this.#o),this.#y=e.session??(e.initialSession===void 0?void 0:this.#e?.sessions.attach(e.initialSession.sessionId,{streamIndex:e.initialSession.streamIndex})),this.#b=this.#P()}get snapshot(){return this.#b}setCallbacks(e){this.#d=e}subscribe(e){return this.#c.add(e),()=>{this.#c.delete(e)}}prewarm(){if(this.#g!==void 0)return this.#g;if(this.#y!==void 0||this.#a)return Promise.resolve();if(this.#u!==void 0)return this.#u.response.then(e=>{if(e===void 0)throw this.#f??new DOMException(`Session creation was aborted.`,`AbortError`)});let e=this.#e;if(e===void 0)return Promise.reject(Error(`This eve agent store does not own a session client.`));let t=this.#h,n=new AbortController;this.#_=n;let r=(async()=>{try{let r=await e.sessions.create({signal:n.signal});if(t!==this.#h)return;this.#y=r.session,this.#f=void 0,this.#x===`error`&&(this.#x=`ready`),this.#d.onSessionChange?.(r.session.state),this.#F(),this.#E()}catch(e){throw t===this.#h&&this.#u===void 0&&this.#f===void 0&&(this.#f=toError(e),this.#x=`error`,this.#d.onError?.(this.#f),this.#F()),e}})();this.#g=r;let clear=()=>{this.#g===r&&(this.#g=void 0,this.#_=void 0)};return r.then(clear,clear),r}async send(e){return await this.#S(e,this.#d.prepareSend)}async#S(e,t){if(this.#u!==void 0){if(this.#x===`resuming`)throw Error(`eve session is resuming.`);return await this.#w(this.#u,e,t)}let n=createActiveTurn(e=>e.acceptedFollowUps>0&&this.#y!==void 0?this.#y.cancel():e.response.then(e=>e===void 0?{status:`no_active_turn`}:e.cancel()));this.#u=n,this.#f=void 0,this.#x=`submitted`,this.#F();let r;try{let i=await(t===void 0?e:waitWithSignal(Promise.resolve(t(e)),createAbortSignal(e.signal,n.abortController.signal)));if(assertExclusiveTurnInput(i),!this.#k(n))return;let a=this.#m.submit(i,this.#p.length);this.#A(i),this.#F();let o={...i,signal:createAbortSignal(i.signal,n.abortController.signal)},s=await this.#T(o),c=s.response;if(r=s.reader,!this.#k(n))return;n.resolveResponse(c),this.#M(this.#m.correlate(a,getMessageResponseDeliveryId(c),this.#p))&&this.#F();for await(let e of consumeMessageResponse(c,r)){if(!this.#k(n))return;n.receivedFollowUps+=n.receivedFollowUpEvents.get(e)??0,n.receivedFollowUpEvents.delete(e)}if(!this.#k(n)||(await followSteeredTurns(n,r,()=>this.#k(n)),!this.#k(n)))return;this.#x=this.#f===void 0?`ready`:`error`}catch(e){if(!this.#k(n))return;if(isAbortError(e))this.#x=`ready`,this.#m.fail(toError(e));else{let t=this.#f!==void 0;this.#f??=toError(e),this.#x=`error`,this.#m.fail(this.#f),t||this.#d.onError?.(this.#f)}}finally{r?.[Symbol.dispose](),this.#O(n)}}resume(){if(this.#v!==void 0)return this.#v;let e=this.#C();this.#v=e;let clear=()=>{this.#v===e&&(this.#v=void 0)};return e.then(clear,clear),e}async#C(){if(this.#x===`resuming`||this.#x===`streaming`||this.#x===`submitted`)throw Error(`eve session is already processing a turn.`);if(this.#y===void 0)throw Error(`An eve session is required before resuming.`);let e=this.#y,t=createActiveTurn(()=>e.cancel());this.#u=t,t.resolveResponse(void 0),this.#f=void 0,this.#x=`resuming`,this.#F();let n;try{let r=this.#E({catchUp:!0,startIndex:this.#p.length===e.state.streamIndex?e.state.streamIndex:0});if(n=r.subscribe(t.abortController.signal),await r.caughtUp,!this.#k(t))return;n.discard();let i=this.#p.at(-1);if(i!==void 0&&this.#N(i),i!==void 0&&this.#f===void 0&&!isSettledSessionTail(this.#p)){this.#x=`streaming`,this.#F();for await(let e of n){if(!this.#k(t))return;if(t.receivedFollowUps+=t.receivedFollowUpEvents.get(e)??0,t.receivedFollowUpEvents.delete(e),isCurrentTurnBoundaryEvent(e)&&this.#i.size===0)break}}await followSteeredTurns(t,n,()=>this.#k(t)),this.#k(t)&&(this.#x=this.#f===void 0?`ready`:`error`)}catch(e){if(!this.#k(t))return;isAbortError(e)?this.#x=`ready`:(this.#f=toError(e),this.#x=`error`,this.#d.onError?.(this.#f))}finally{n?.[Symbol.dispose](),this.#O(t)}}cancel(){let e=this.#u;return e===void 0?this.#x===`streaming`&&this.#y!==void 0?this.#y.cancel():Promise.resolve({status:`no_active_turn`}):e.cancel()}[attachStore](){this.#n=!0,this.#t&&this.#y===void 0&&this.prewarm().catch(()=>{})}[detachStore](){this.#n=!1,this.#r?.close(),this.#r=void 0,this.#u?.abortController.abort(),this.#D(),this.#v=void 0}reset(){this.#r?.close(),this.#r=void 0,this.#i.clear();let e=this.#u;this.#u=void 0,e?.resolveResponse(void 0),e?.resolveCompletion(),e?.abortController.abort(),this.#D(),this.#v=void 0,this.#a||(this.#y=void 0),this.#p=[],this.#l=createEventDeduper(),this.#m.reset(),this.#s.reset(),this.#f=void 0,this.#x=`ready`,this.#d.onSessionChange?.(this.#y?.state),this.#F(),this.#t&&this.#n&&this.prewarm().catch(()=>{})}async#w(e,t,n){if(t.message===void 0||t.turnPolicy!==`steer`)throw Error(`eve session is already processing a turn. Send a message with turnPolicy: "steer" to guide it at the next boundary.`);let r=this.#h,i=createAbortSignal(t.signal,e.abortController.signal),a=await waitWithSignal(Promise.resolve(n?.(t)),i)??t;if(r!==this.#h)return;if(assertExclusiveTurnInput(a),a.message===void 0||a.turnPolicy!==`steer`)throw Error(`An in-flight follow-up requires a message with turnPolicy: "steer".`);if(!this.#k(e))return await this.#S(a);let o=this.#m.submit(a,this.#p.length);o!==void 0&&e.followUpSubmissionIds.add(o),this.#F(),this.#E({headers:a.headers,streamReconnectPolicy:a.streamReconnectPolicy});let s;s=(async()=>{try{let t=createAbortSignal(a.signal,e.abortController.signal);if(await waitWithSignal(e.response,t),!this.#k(e)||this.#y===void 0)throw Error(`The active eve turn ended before the follow-up could be sent.`);let{message:n,...r}=a,i=await this.#y.send(n,{...r,signal:t});e.acceptedFollowUps+=1,this.#M(this.#m.correlate(o,getMessageResponseDeliveryId(i),this.#p))&&this.#F()}catch(t){throw this.#k(e)&&(this.#m.fail(toError(t),o),this.#F()),t}finally{e.followUpDispatches.delete(s)}})(),e.followUpDispatches.add(s),await s,await e.completion}async#T(e){let t={headers:e.headers,streamReconnectPolicy:e.streamReconnectPolicy};if(this.#g!==void 0&&(await waitWithSignal(this.#g.catch(e=>{if(this.#y!==void 0)throw e}),e.signal),e.signal?.throwIfAborted()),this.#y===void 0){if(this.#e===void 0)throw Error(`An external eve session is required before sending.`);if(e.message===void 0)throw Error(`Cannot answer an input request before the session starts.`);let n=await this.#e.sessions.create({...e,message:e.message});return e.signal?.throwIfAborted(),this.#y=n.session,this.#d.onSessionChange?.(n.session.state),this.#F(),{response:n.response,reader:this.#E(t).subscribe(e.signal)}}let n=this.#E(t).subscribe(e.signal);try{if(e.inputResponses===void 0){let{message:t,...r}=e;return{response:await this.#y.send(t,r),reader:n}}let{inputResponses:t,...r}=e;return{response:await this.#y.respond(t,r),reader:n}}catch(e){throw n[Symbol.dispose](),e}}#E(e={}){if(this.#r!==void 0&&!this.#r.ended)return this.#r.setOptions(e),this.#r;if(this.#y===void 0)throw Error(`A session is required before opening its stream.`);let t=this.#y,n=this.#h;return this.#r=new SessionEventStream(t,{...e,onEvent:e=>{n===this.#h&&this.#j(e)},onError:e=>{n===this.#h&&(this.#r=void 0,this.#u===void 0&&(this.#f=toError(e),this.#x=`error`,this.#d.onError?.(this.#f),this.#F()))}}),this.#r}#D(){this.#h+=1,this.#_?.abort(),this.#_=void 0,this.#g=void 0}#O(e){if(this.#k(e)){e.resolveResponse(void 0),this.#u=void 0;try{this.#d.onSessionChange?.(this.#y?.state),this.#F(),this.#d.onFinish?.(this.#b)}finally{e.resolveCompletion()}}}#k(e){return this.#u===e}#A(e){e.inputResponses!==void 0&&e.inputResponses.length!==0&&this.#s.append({data:{createdAt:Date.now(),responses:e.inputResponses},type:`client.input.responded`})}#j(e){if(!this.#l.admit(e))return;let t=this.#x===`streaming`;updatePendingAuthorizations(this.#i,e),this.#p=[...this.#p,e],this.#M(this.#m.apply(e)),this.#d.onEvent?.(e),this.#N(e);let n=isCurrentTurnBoundaryEvent(e)&&this.#i.size===0;this.#x!==`resuming`&&this.#f===void 0&&(`data`in e&&`turnId`in e.data&&(this.#x=`streaming`),this.#u===void 0&&n&&(this.#x=`ready`)),this.#d.onSessionChange?.(this.#y?.state),this.#F(),this.#u===void 0&&t&&n&&this.#d.onFinish?.(this.#b)}#M(e){if(e===void 0)return!1;let t=0;for(let n of e.ids)this.#u?.followUpSubmissionIds.delete(n)&&(t+=1);if(t>0&&this.#u!==void 0){if(e.alreadyProjected)this.#u.receivedFollowUps+=t;else{let n=this.#u.receivedFollowUpEvents.get(e.event)??0;this.#u.receivedFollowUpEvents.set(e.event,n+t)}}return!0}#N(e){let t=toTerminalStreamFailureError(e);t!==void 0&&(this.#x=`error`,this.#m.failAll(t),this.#f===void 0&&(this.#f=t,this.#d.onError?.(t)))}#P(){return{data:this.#s.data,error:this.#f,events:this.#p,session:this.#y?.state,status:this.#x}}#F(){this.#b=this.#P();for(let e of this.#c)e()}};function detachEveAgentStore(e){e[detachStore]()}function attachEveAgentStore(e){e[attachStore]()}export{EveAgentStore,attachEveAgentStore,detachEveAgentStore};