eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 5.04 kB
JavaScript
import{EVE_CREATE_SESSION_ROUTE_PATH,createEveCancelTurnRoutePath,createEveContinueSessionRoutePath}from"#protocol/routes.js";import{EVE_SESSION_ID_HEADER,isCurrentTurnBoundaryEvent}from"#protocol/message.js";import{ClientError}from"#client/client-error.js";import{advanceSession}from"#client/session-utils.js";import{createClientUrl}from"#client/url.js";import{MessageResponse}from"#client/message-response.js";import{CancelTurnResponseSchema}from"#protocol/cancel-turn.js";import{followStreamIterable}from"#client/open-stream.js";import{serializeOutputSchema}from"#shared/tool-schema.js";var ClientSession=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}get state(){return this.#t}async send(e){let t=normalizeSendTurnInput(e),n=this.#t,{continuationToken:r,sessionId:i}=await this.#n(t,n);return this.#t===n&&(this.#t={...n,sessionId:i}),new MessageResponse({continuationToken:r,createStream:()=>this.#r(i,r,n,t),sessionId:i})}async cancel(e){let n=this.#t.sessionId;if(!n)throw Error(`Session has no session ID. Send a message first.`);let r=createClientUrl(this.#e.host,createEveCancelTurnRoutePath(n)),i=await this.#e.resolveHeaders();i.set(`content-type`,`application/json`);let o=await fetch(r,withRedirectPolicy({headers:i,method:`POST`,body:e?JSON.stringify(e):void 0},this.#e.redirect)),c=await o.text();if(!o.ok)throw new ClientError(o.status,c,o.headers);let u;try{u=JSON.parse(c)}catch{throw Error(`Cancel route returned invalid JSON (${o.status}).`)}let d=CancelTurnResponseSchema.safeParse(u);if(!d.success||d.data.sessionId!==n)throw Error(`Cancel route returned an invalid response (${o.status}).`);return{sessionId:d.data.sessionId,status:d.data.status}}stream(e){let t=this.#t.sessionId;if(!t)throw Error(`Session has no session ID. Send a message first.`);return this.#i(t,e)}async#n(t,i){let a=i.sessionId?createEveContinueSessionRoutePath(i.sessionId):EVE_CREATE_SESSION_ROUTE_PATH,o=createClientUrl(this.#e.host,a),c=await this.#e.resolveHeaders(t.headers);c.set(`content-type`,`application/json`);let l=createHandleMessageBody({input:t,outputSchema:serializeOutputSchema(t.outputSchema),session:i});if(l===null)throw Error(`Session.send requires a non-empty message, inputResponses, or both.`);let u=await postTurnWithRetry({body:JSON.stringify(l),headers:c,mustDeliver:(t.inputResponses?.length??0)>0,redirect:this.#e.redirect,signal:t.signal,url:o}),f=await u.json(),p=(typeof f.sessionId==`string`?f.sessionId:void 0)??u.headers.get(EVE_SESSION_ID_HEADER)?.trim()??i.sessionId;if(!p)throw Error(`Message route did not return a session id.`);return{continuationToken:typeof f.continuationToken==`string`?f.continuationToken:void 0,sessionId:p}}async*#r(e,t,n,r){let a=[];try{for await(let t of followStreamIterable({host:this.#e.host,resolveHeaders:()=>this.#e.resolveHeaders(r.headers),redirect:this.#e.redirect,streamReconnectPolicy:r.streamReconnectPolicy,sessionId:e,signal:r.signal,startIndex:n.sessionId===e?n.streamIndex:0}))if(a.push(t),yield t,isCurrentTurnBoundaryEvent(t))break}finally{this.#t=advanceSession({continuationToken:t,events:a,preserveCompletedSessions:this.#e.preserveCompletedSessions,sessionId:e,session:n})}}async*#i(e,t){let n=this.#t,r=t?.startIndex??n.streamIndex,i=[];try{for await(let n of followStreamIterable({host:this.#e.host,resolveHeaders:()=>this.#e.resolveHeaders(),redirect:this.#e.redirect,streamReconnectPolicy:t?.streamReconnectPolicy,sessionId:e,signal:t?.signal,startIndex:r}))i.push(n),yield n}finally{r>=0&&(this.#t=advanceSession({continuationToken:n.continuationToken,events:i,preserveCompletedSessions:this.#e.preserveCompletedSessions,session:{...n,sessionId:e,streamIndex:r},sessionId:e}))}}};async function postTurnWithRetry(e){let t=e.mustDeliver?10:1,n,r,i;for(let o=0;o<t;o+=1){let s=await fetch(e.url,{body:e.body,headers:e.headers,method:`POST`,redirect:e.redirect,signal:e.signal??null});if(s.ok)return s;if(n=s.status,r=await s.text(),i=s.headers,!isRetryableDeliveryFailure(s.status,r))throw new ClientError(s.status,r,s.headers);o<t-1&&await sleep(200)}throw new ClientError(n??0,r??`Failed to deliver session turn.`,i)}function isRetryableDeliveryFailure(e,t){return e===500&&/target session was not found/i.test(t)}async function sleep(e){await new Promise(t=>setTimeout(t,e))}function normalizeSendTurnInput(e){return typeof e==`string`?{message:e}:e}function createHandleMessageBody(e){let t={};return e.input.message!==void 0&&(t.message=e.input.message),e.input.inputResponses!==void 0&&e.input.inputResponses.length>0&&(t.inputResponses=e.input.inputResponses),e.input.clientContext!==void 0&&(t.clientContext=e.input.clientContext),e.outputSchema!==void 0&&(t.outputSchema=e.outputSchema),e.session.continuationToken!==void 0&&(t.continuationToken=e.session.continuationToken),Object.keys(t).length===0||e.session.continuationToken===void 0&&t.message===void 0||e.session.continuationToken!==void 0&&t.message===void 0&&t.inputResponses===void 0?null:t}function withRedirectPolicy(e,t){return t===void 0?e:{...e,redirect:t}}export{ClientSession};