eve 0.24.3 → 0.24.4

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/channel/adapter.d.ts +4 -2
  3. package/dist/src/channel/adapter.js +1 -1
  4. package/dist/src/channel/session.d.ts +5 -1
  5. package/dist/src/channel/types.d.ts +7 -6
  6. package/dist/src/chunks/{use-eve-agent-CFBTHlPx.js → use-eve-agent-BP9FpQsJ.js} +15 -4
  7. package/dist/src/chunks/{use-eve-agent-dEGV09g_.js → use-eve-agent-Dwa2j1wE.js} +15 -4
  8. package/dist/src/cli/dev/tui/runner.d.ts +2 -2
  9. package/dist/src/cli/dev/tui/runner.js +1 -1
  10. package/dist/src/client/index.d.ts +1 -1
  11. package/dist/src/client/message-reducer.js +1 -1
  12. package/dist/src/client/open-stream.js +1 -1
  13. package/dist/src/client/session-utils.js +1 -1
  14. package/dist/src/client/session.d.ts +4 -2
  15. package/dist/src/client/session.js +1 -1
  16. package/dist/src/client/types.d.ts +3 -2
  17. package/dist/src/discover/filesystem.js +1 -1
  18. package/dist/src/execution/durable-session-migrations/turn-workflow.d.ts +1 -0
  19. package/dist/src/execution/durable-session-migrations/turn-workflow.js +1 -1
  20. package/dist/src/execution/next-driver-action.d.ts +7 -0
  21. package/dist/src/execution/settle-cancelled-turn-step.d.ts +16 -0
  22. package/dist/src/execution/settle-cancelled-turn-step.js +1 -0
  23. package/dist/src/execution/subagent-hitl-proxy.js +1 -1
  24. package/dist/src/execution/terminal-session-failure-step.d.ts +6 -0
  25. package/dist/src/execution/terminal-session-failure-step.js +1 -0
  26. package/dist/src/execution/turn-cancellation-control.d.ts +40 -0
  27. package/dist/src/execution/turn-cancellation-control.js +1 -0
  28. package/dist/src/execution/turn-dispatch.d.ts +14 -1
  29. package/dist/src/execution/turn-dispatch.js +1 -1
  30. package/dist/src/execution/turn-execution-cursor.d.ts +2 -1
  31. package/dist/src/execution/turn-execution-cursor.js +1 -1
  32. package/dist/src/execution/turn-workflow.d.ts +9 -1
  33. package/dist/src/execution/turn-workflow.js +1 -1
  34. package/dist/src/execution/workflow-entry.js +1 -1
  35. package/dist/src/execution/workflow-steps.d.ts +8 -6
  36. package/dist/src/execution/workflow-steps.js +1 -1
  37. package/dist/src/harness/active-turn-id.d.ts +11 -0
  38. package/dist/src/harness/active-turn-id.js +1 -0
  39. package/dist/src/harness/cancelled-turn-emission.d.ts +11 -0
  40. package/dist/src/harness/cancelled-turn-emission.js +1 -0
  41. package/dist/src/harness/emission.d.ts +4 -2
  42. package/dist/src/harness/emission.js +1 -1
  43. package/dist/src/harness/proxy-input-requests.d.ts +5 -0
  44. package/dist/src/harness/proxy-input-requests.js +1 -1
  45. package/dist/src/harness/session-limit-enforcement.js +1 -1
  46. package/dist/src/harness/tool-loop.js +2 -2
  47. package/dist/src/internal/application/compiled-artifacts.js +3 -2
  48. package/dist/src/internal/application/package.js +1 -1
  49. package/dist/src/internal/authored-module-loader.js +2 -2
  50. package/dist/src/internal/bundler/nitro-rolldown.d.ts +18 -2
  51. package/dist/src/internal/bundler/nitro-rolldown.js +1 -1
  52. package/dist/src/internal/nitro/dev-runtime-source-snapshot-copy.js +1 -1
  53. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  54. package/dist/src/internal/workflow/development-world-client.js +1 -1
  55. package/dist/src/internal/workflow/development-world-server.js +1 -1
  56. package/dist/src/internal/workflow/local-world-data-directory.d.ts +2 -0
  57. package/dist/src/internal/workflow/local-world-data-directory.js +1 -0
  58. package/dist/src/internal/workflow-bundle/builder-support.js +3 -3
  59. package/dist/src/internal/workflow-bundle/builder.js +2 -2
  60. package/dist/src/internal/workflow-bundle/vercel-workflow-output.js +1 -1
  61. package/dist/src/protocol/message.d.ts +23 -3
  62. package/dist/src/protocol/message.js +2 -2
  63. package/dist/src/public/channels/discord/discordChannel.d.ts +1 -0
  64. package/dist/src/public/channels/eve.js +1 -1
  65. package/dist/src/public/channels/github/githubChannel.d.ts +1 -0
  66. package/dist/src/public/channels/linear/linearChannel.d.ts +1 -0
  67. package/dist/src/public/channels/slack/slackChannel.d.ts +1 -0
  68. package/dist/src/public/channels/teams/teamsChannel.d.ts +1 -0
  69. package/dist/src/public/channels/telegram/telegramChannel.d.ts +1 -0
  70. package/dist/src/public/channels/twilio/twilioChannel.d.ts +1 -0
  71. package/dist/src/public/definitions/channel.d.ts +5 -4
  72. package/dist/src/public/definitions/channel.js +1 -1
  73. package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
  74. package/dist/src/services/dev-client/runtime-artifacts.d.ts +2 -2
  75. package/dist/src/services/dev-client.d.ts +10 -18
  76. package/dist/src/services/dev-client.js +1 -1
  77. package/dist/src/setup/scaffold/create/extension.js +0 -1
  78. package/dist/src/setup/scaffold/create/project.js +1 -3
  79. package/dist/src/shared/continuation-token.d.ts +2 -0
  80. package/dist/src/shared/continuation-token.js +1 -0
  81. package/dist/src/svelte/index.js +1 -1
  82. package/dist/src/svelte/use-eve-agent.js +1 -1
  83. package/dist/src/vue/index.js +1 -1
  84. package/dist/src/vue/use-eve-agent.js +1 -1
  85. package/docs/concepts/execution-model-and-durability.md +1 -1
  86. package/docs/concepts/sessions-runs-and-streaming.md +13 -4
  87. package/docs/guides/client/continuations.mdx +14 -1
  88. package/docs/guides/client/streaming.mdx +14 -1
  89. package/docs/guides/deployment.md +1 -1
  90. package/docs/reference/cli.md +1 -1
  91. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
1
  import{createLogger,logError}from"#internal/logging.js";import{hasInternalRefScheme}from"#internal/attachments/url-refs.js";import{EVE_INFO_ROUTE_PATH}from"#protocol/routes.js";import{EVE_MESSAGE_STREAM_CONTENT_TYPE,EVE_MESSAGE_STREAM_FORMAT,EVE_MESSAGE_STREAM_VERSION,EVE_SESSION_ID_HEADER,EVE_STREAM_FORMAT_HEADER,EVE_STREAM_VERSION_HEADER}from"#protocol/message.js";import{isInputResponse}from"#runtime/input/types.js";import{parseJsonObject}from"#shared/json.js";import{GET,POST,defineChannel}from"#public/definitions/channel.js";import"ai";import{parseSessionCallback}from"#channel/session-callback.js";import{readAgentInfoRouteResponse}from"#internal/nitro/routes/channel-route-context.js";import{routeAuth}from"#public/channels/auth.js";import{collectUploadPolicyViolations,formatUploadPolicyViolation,mergeUploadPolicy}from"#public/channels/upload-policy.js";const log=createLogger(`eve.channel`);function defaultEveAuth(e){return e.eve.caller}function eveChannel(e){let t=mergeUploadPolicy(e.uploadPolicy);return defineChannel({cors:normalizeEveCors(e.cors),routes:[GET(EVE_INFO_ROUTE_PATH,async(t,n)=>{let r=await routeAuth(t,e.auth);if(r instanceof Response)return r;let i=readAgentInfoRouteResponse(n);return i===void 0?Response.json({error:`Agent info route requires internal channel dispatch context.`,ok:!1},{status:500}):await i()}),POST(`/eve/v1/session`,async(n,{send:r})=>{let i=await routeAuth(n,e.auth);if(i instanceof Response)return i;let a=i,o;try{o=await n.json()}catch{return Response.json({error:`Invalid JSON body.`,ok:!1},{status:400})}if(typeof o!=`object`||!o)return Response.json({error:`Expected a JSON object.`,ok:!1},{status:400});let s=parseCreateBody(o);if(s instanceof Response)return s;let c=checkUploadPolicy(s,t);if(c!==null)return c;let l=await resolveOnMessage({auth:a,config:e,message:s.message,request:n});if(l instanceof Response)return l;if(!l.dispatch)return droppedMessageResponse();let u=`eve:${crypto.randomUUID()}`,d=await r(createSendPayload(s,mergeContext(s.context,l.context)),{auth:l.auth,callback:s.callback,continuationToken:u,mode:s.mode});return Response.json({continuationToken:d.continuationToken,ok:!0,sessionId:d.id},{headers:{"cache-control":`no-store`,[EVE_SESSION_ID_HEADER]:d.id},status:202})}),POST(`/eve/v1/session/:sessionId`,async(n,{send:r,getSession:i,params:a})=>{let o=await routeAuth(n,e.auth);if(o instanceof Response)return o;let s=o,c=a.sessionId;if(!c)return Response.json({error:`Missing session id.`,ok:!1},{status:400});try{i(c)}catch{return Response.json({error:`Session not found.`,ok:!1},{status:404})}let l;try{l=await n.json()}catch{return Response.json({error:`Invalid JSON body.`,ok:!1},{status:400})}if(typeof l!=`object`||!l)return Response.json({error:`Expected a JSON object.`,ok:!1},{status:400});let u=parseContinueBody(l);if(u instanceof Response)return u;let d=checkUploadPolicy(u,t);if(d!==null)return d;let f=u.context,p=s;if(u.message!==void 0){let t=await resolveOnMessage({auth:s,config:e,message:u.message,request:n,sessionId:c});if(t instanceof Response)return t;if(!t.dispatch)return droppedMessageResponse();f=mergeContext(u.context,t.context),p=t.auth}let m=await r({inputResponses:u.inputResponses,message:u.message,context:f,outputSchema:u.outputSchema},{auth:p,continuationToken:u.continuationToken});return Response.json({ok:!0,sessionId:m.id},{headers:{"cache-control":`no-store`,[EVE_SESSION_ID_HEADER]:m.id},status:200})}),GET(`/eve/v1/session/:sessionId/stream`,async(t,{getSession:n,params:r})=>{let l=await routeAuth(t,e.auth);if(l instanceof Response)return l;let u=r.sessionId;if(!u)return Response.json({error:`Missing session id.`,ok:!1},{status:400});let d=parseStartIndex(t);if(d instanceof Response)return d;try{let e=serializeAsNdjson(await n(u).getEventStream({startIndex:d}));return new Response(e,{headers:{"cache-control":`no-store, no-transform`,"content-type":EVE_MESSAGE_STREAM_CONTENT_TYPE,"x-accel-buffering":`no`,[EVE_SESSION_ID_HEADER]:u,[EVE_STREAM_FORMAT_HEADER]:EVE_MESSAGE_STREAM_FORMAT,[EVE_STREAM_VERSION_HEADER]:EVE_MESSAGE_STREAM_VERSION}})}catch{return Response.json({error:`Session not found.`,ok:!1},{status:404})}})],events:e.events})}function normalizeEveCors(e){if(e===void 0||e===!1)return!1;if(e===!0)return!0;let t={};return e.origin!==void 0&&(t.origin=normalizeEveCorsOrigin(e.origin)),e.methods!==void 0&&(t.methods=e.methods),e.allowedHeaders!==void 0&&(t.allowHeaders=e.allowedHeaders),e.exposedHeaders!==void 0&&(t.exposeHeaders=e.exposedHeaders),e.credentials!==void 0&&(t.credentials=e.credentials),e.maxAge!==void 0&&(t.maxAge=e.maxAge),e.preflightStatus!==void 0&&(t.preflight={statusCode:e.preflightStatus}),t}function normalizeEveCorsOrigin(e){return e===`*`||e===`null`?e:typeof e==`string`?[e]:e}async function resolveOnMessage(e){let n=e.config.onMessage??defaultOnMessage,r;try{r=await n({eve:e.sessionId===void 0?{caller:e.auth,request:e.request}:{caller:e.auth,request:e.request,sessionId:e.sessionId}},e.message)}catch(n){let r=logError(log,`onMessage handler failed`,n,{sessionId:e.sessionId});return Response.json({error:`onMessage handler failed.`,errorId:r,ok:!1},{status:500})}return r==null?{dispatch:!1}:r.context===void 0?{auth:r.auth,dispatch:!0}:{auth:r.auth,context:r.context,dispatch:!0}}function defaultOnMessage(e){return{auth:defaultEveAuth(e)}}function droppedMessageResponse(){return new Response(null,{headers:{"cache-control":`no-store`},status:204})}function parseCreateBody(e){let t=parseMessageField(e.message);if(t instanceof Response)return t;let n=parseClientContextField(e.clientContext);if(n instanceof Response)return n;let r=parseCallbackField(e.callback);if(r instanceof Response)return r;let i=parseModeField(e.mode);if(i instanceof Response)return i;let a=parseOutputSchemaField(e.outputSchema);return a instanceof Response?a:t===void 0?Response.json({error:`Missing or empty 'message' field.`,ok:!1},{status:400}):{callback:r,message:t,mode:i,context:n,outputSchema:a}}function parseContinueBody(e){let t=typeof e.continuationToken==`string`&&e.continuationToken.length>0?e.continuationToken:void 0;if(t===void 0)return Response.json({error:`Missing or empty 'continuationToken' field.`,ok:!1},{status:400});let n=parseMessageField(e.message);if(n instanceof Response)return n;let r=parseInputResponses(e.inputResponses);if(r instanceof Response)return r;let i=parseClientContextField(e.clientContext);if(i instanceof Response)return i;let a=parseOutputSchemaField(e.outputSchema);return a instanceof Response?a:n===void 0&&r===void 0?Response.json({error:`Expected a non-empty 'message', a non-empty 'inputResponses' array, or both.`,ok:!1},{status:400}):{message:n,continuationToken:t,inputResponses:r,context:i,outputSchema:a}}function createSendPayload(e,t=e.context){if(t===void 0&&e.outputSchema===void 0)return e.message;let n={message:e.message};return t!==void 0&&(n.context=t),e.outputSchema!==void 0&&(n.outputSchema=e.outputSchema),n}function parseOutputSchemaField(e){if(e!==void 0)try{return parseJsonObject(e)}catch{return Response.json({error:`Expected 'outputSchema' to be a JSON-serializable object.`,ok:!1},{status:400})}}function parseCallbackField(e){if(e===void 0)return;let t=parseSessionCallback(e);return t.ok?t.callback:Response.json({error:t.message,ok:!1},{status:400})}function parseModeField(e){if(e!==void 0)return e===`conversation`||e===`task`?e:Response.json({error:`Expected 'mode' to be either 'conversation' or 'task'.`,ok:!1},{status:400})}function parseMessageField(e){if(e===void 0)return;if(typeof e==`string`)return e.length>0?e:void 0;if(!Array.isArray(e))return Response.json({error:`Expected 'message' to be a string or an array of text/file parts.`,ok:!1},{status:400});if(e.length===0)return;let t=[];for(let n of e){let e=parseMessagePart(n);if(e instanceof Response)return e;t.push(e)}return t}function parseMessagePart(e){if(typeof e!=`object`||!e)return Response.json({error:`Expected each message part to be an object.`,ok:!1},{status:400});let t=e;if(t.type===`text`)return typeof t.text!=`string`||t.text.length===0?Response.json({error:`Text parts require a non-empty 'text' string.`,ok:!1},{status:400}):{type:`text`,text:t.text};if(t.type===`file`){if(typeof t.mediaType!=`string`||t.mediaType.length===0)return Response.json({error:`File parts require a non-empty 'mediaType' string.`,ok:!1},{status:400});if(typeof t.data!=`string`)return Response.json({error:`File parts require a 'data' string (base64, data URL, or URL).`,ok:!1},{status:400});if(hasInternalRefScheme(t.data))return Response.json({error:`File part 'data' must not use a framework-internal ref scheme.`,ok:!1},{status:400});let e={type:`file`,mediaType:t.mediaType,data:t.data};return typeof t.filename==`string`&&t.filename.length>0&&(e.filename=t.filename),e}return Response.json({error:`Unsupported message part type "${String(t.type)}". Use 'text' or 'file'.`,ok:!1},{status:400})}function checkUploadPolicy(e,t){if(!e.message)return null;let n=collectUploadPolicyViolations(e.message,t);if(n.length===0)return null;let[r]=n;if(!r)return null;let i=r.kind===`too-large`?413:415;return Response.json({error:formatUploadPolicyViolation(r),ok:!1,violations:n.map(e=>e.kind===`too-large`?{byteLength:e.byteLength,filename:e.filename,kind:e.kind,limit:e.limit,mediaType:e.mediaType}:{allowedMediaTypes:e.allowedMediaTypes,filename:e.filename,kind:e.kind,mediaType:e.mediaType})},{status:i})}function parseInputResponses(e){if(e===void 0)return;if(!Array.isArray(e)||e.length===0)return Response.json({error:`Expected 'inputResponses' to be a non-empty array.`,ok:!1},{status:400});let t=e.filter(isInputResponse);return t.length===e.length?t:Response.json({error:`Expected every 'inputResponses' entry to match the HITL response schema.`,ok:!1},{status:400})}function mergeContext(e,t){return e===void 0?t:t===void 0?e:[...e,...t]}function parseClientContextField(e){if(e!==void 0){if(typeof e==`string`)return e.length>0?[toClientContextMessage(e)]:void 0;if(Array.isArray(e))return e.length===0?void 0:e.every(e=>typeof e==`string`&&e.length>0)?e.map(e=>toClientContextMessage(e)):Response.json({error:`Expected 'clientContext' array entries to be non-empty strings.`,ok:!1},{status:400});if(typeof e!=`object`||!e)return Response.json({error:`Expected 'clientContext' to be a string, string array, or JSON object.`,ok:!1},{status:400});try{let t=parseJsonObject(e);return[toClientContextMessage(JSON.stringify(t))]}catch{return Response.json({error:`Expected 'clientContext' to be a JSON-serializable object.`,ok:!1},{status:400})}}}function toClientContextMessage(e){return`Client context:
2
- ${e}`}function parseStartIndex(e){let t=new URL(e.url).searchParams.get(`startIndex`);if(t===null)return;let n=Number.parseInt(t,10);return!Number.isSafeInteger(n)||n<0?Response.json({error:`Expected startIndex to be a non-negative integer.`,ok:!1},{status:400}):n}function serializeAsNdjson(e){let t=new TextEncoder;return e.pipeThrough(new TransformStream({transform(e,n){n.enqueue(t.encode(`${JSON.stringify(e)}\n`))}}))}export{defaultEveAuth,eveChannel};
2
+ ${e}`}function parseStartIndex(e){let t=new URL(e.url).searchParams.get(`startIndex`);if(t===null)return;let n=Number(t);return!/^-?\d+$/.test(t)||!Number.isSafeInteger(n)?Response.json({error:`Expected startIndex to be an integer.`,ok:!1},{status:400}):n}function serializeAsNdjson(e){let t=new TextEncoder;return e.pipeThrough(new TransformStream({transform(e,n){n.enqueue(t.encode(`${JSON.stringify(e)}\n`))}}))}export{defaultEveAuth,eveChannel};
@@ -88,6 +88,7 @@ export interface GitHubChannelEvents {
88
88
  readonly "session.failed"?: GitHubSessionFailedHandler;
89
89
  readonly "session.waiting"?: GitHubEventHandler<"session.waiting">;
90
90
  readonly "turn.completed"?: GitHubEventHandler<"turn.completed">;
91
+ readonly "turn.cancelled"?: GitHubEventHandler<"turn.cancelled">;
91
92
  readonly "turn.failed"?: GitHubEventHandler<"turn.failed">;
92
93
  readonly "turn.started"?: GitHubEventHandler<"turn.started">;
93
94
  }
@@ -102,6 +102,7 @@ export interface LinearChannelEvents {
102
102
  readonly "input.requested"?: LinearEventHandler<"input.requested">;
103
103
  readonly "turn.failed"?: LinearEventHandler<"turn.failed">;
104
104
  readonly "turn.completed"?: LinearEventHandler<"turn.completed">;
105
+ readonly "turn.cancelled"?: LinearEventHandler<"turn.cancelled">;
105
106
  readonly "session.failed"?: LinearSessionFailedHandler;
106
107
  readonly "session.completed"?: LinearEventHandler<"session.completed">;
107
108
  readonly "session.waiting"?: LinearEventHandler<"session.waiting">;
@@ -256,6 +256,7 @@ export interface SlackChannelEvents {
256
256
  readonly "input.requested"?: SlackEventHandler<"input.requested">;
257
257
  readonly "turn.failed"?: SlackEventHandler<"turn.failed">;
258
258
  readonly "turn.completed"?: SlackEventHandler<"turn.completed">;
259
+ readonly "turn.cancelled"?: SlackEventHandler<"turn.cancelled">;
259
260
  readonly "session.failed"?: SlackSessionFailedHandler;
260
261
  readonly "session.completed"?: SlackEventHandler<"session.completed">;
261
262
  readonly "session.waiting"?: SlackEventHandler<"session.waiting">;
@@ -99,6 +99,7 @@ export interface TeamsChannelEvents {
99
99
  readonly "input.requested"?: TeamsEventHandler<"input.requested">;
100
100
  readonly "turn.failed"?: TeamsEventHandler<"turn.failed">;
101
101
  readonly "turn.completed"?: TeamsEventHandler<"turn.completed">;
102
+ readonly "turn.cancelled"?: TeamsEventHandler<"turn.cancelled">;
102
103
  readonly "session.failed"?: TeamsSessionFailedHandler;
103
104
  readonly "session.completed"?: TeamsEventHandler<"session.completed">;
104
105
  readonly "session.waiting"?: TeamsEventHandler<"session.waiting">;
@@ -73,6 +73,7 @@ export interface TelegramChannelEvents {
73
73
  readonly "input.requested"?: TelegramEventHandler<"input.requested">;
74
74
  readonly "turn.failed"?: TelegramEventHandler<"turn.failed">;
75
75
  readonly "turn.completed"?: TelegramEventHandler<"turn.completed">;
76
+ readonly "turn.cancelled"?: TelegramEventHandler<"turn.cancelled">;
76
77
  readonly "session.failed"?: TelegramSessionFailedHandler;
77
78
  readonly "session.completed"?: TelegramEventHandler<"session.completed">;
78
79
  readonly "session.waiting"?: TelegramEventHandler<"session.waiting">;
@@ -94,6 +94,7 @@ export interface TwilioChannelEvents {
94
94
  readonly "input.requested"?: TwilioEventHandler<"input.requested">;
95
95
  readonly "turn.failed"?: TwilioEventHandler<"turn.failed">;
96
96
  readonly "turn.completed"?: TwilioEventHandler<"turn.completed">;
97
+ readonly "turn.cancelled"?: TwilioEventHandler<"turn.cancelled">;
97
98
  readonly "session.failed"?: TwilioSessionFailedHandler;
98
99
  readonly "session.completed"?: TwilioEventHandler<"session.completed">;
99
100
  readonly "session.waiting"?: TwilioEventHandler<"session.waiting">;
@@ -105,10 +105,10 @@ export interface Agent {
105
105
  * existing session. Used by the framework's HTTP session-stream route and by
106
106
  * any user-authored route that exposes an event-streaming endpoint.
107
107
  *
108
- * Pass `options.startIndex` to skip events the caller has already
109
- * consumed the framework HTTP session-stream route uses this to forward
110
- * the `startIndex` query parameter so reconnecting clients resume from
111
- * the next unread event instead of replaying the session from the start.
108
+ * Nonnegative `options.startIndex` values skip events the caller has already
109
+ * consumed. Negative values read relative to the current tail (`-1` starts
110
+ * at the latest event). The framework HTTP session-stream route forwards
111
+ * the `startIndex` query parameter unchanged.
112
112
  */
113
113
  getEventStream(sessionId: string, options?: GetEventStreamOptions): Promise<ReadableStream<HandleMessageStreamEvent>>;
114
114
  }
@@ -172,6 +172,7 @@ export interface ChannelEvents<TCtx = void> {
172
172
  readonly "input.requested"?: ChannelEventHandler<"input.requested", TCtx>;
173
173
  readonly "turn.failed"?: ChannelEventHandler<"turn.failed", TCtx>;
174
174
  readonly "turn.completed"?: ChannelEventHandler<"turn.completed", TCtx>;
175
+ readonly "turn.cancelled"?: ChannelEventHandler<"turn.cancelled", TCtx>;
175
176
  readonly "session.failed"?: ChannelSessionFailedHandler<TCtx>;
176
177
  readonly "session.completed"?: ChannelEventHandler<"session.completed", TCtx>;
177
178
  readonly "session.waiting"?: ChannelEventHandler<"session.waiting", TCtx>;
@@ -1 +1 @@
1
- import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,PATCH,POST,PUT,WS}from"#channel/routes.js";const DISABLED_ROUTE_SENTINEL_KIND=`eve:disabled-channel`;function disableRoute(){return{kind:DISABLED_ROUTE_SENTINEL_KIND}}function isDisabledRouteSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_ROUTE_SENTINEL_KIND}function defineChannel(t){let n=buildAdapter(t),i=normalizeChannelCors(t.cors);return{__kind:CHANNEL_SENTINEL,routes:t.routes,adapter:n,cors:i,receive:t.receive}}function buildAdapter(e){let r=e.state!=null,a=e.context!=null,o=e.fetchFile!==void 0,s=e.metadata,c=r||a||s!==void 0,l={},u=!1,d=[`turn.started`,`actions.requested`,`action.result`,`message.completed`,`message.appended`,`reasoning.appended`,`reasoning.completed`,`input.requested`,`turn.failed`,`turn.completed`,`session.failed`,`session.completed`,`session.waiting`,`authorization.required`,`authorization.completed`],f=e.events;for(let e of d){let t=f?.[e];t&&(u=!0,l[e]=(r,i)=>{let a={...i,continuationToken:i.session?.continuationToken??``,setContinuationToken:e=>i.session?.setContinuationToken(e)};return e===`session.failed`?t(r,a):t(r,a,buildCallbackContext())})}return!c&&!u&&!o?{kind:e.kindHint??HTTP_ADAPTER_KIND}:{kind:e.kindHint??`defineChannel`,state:r?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:s===void 0?void 0:{metadata(e){return s(e)}},createAdapterContext(t){let n=t.state,r=t.session;return{...a?e.context(n,r):{},state:n,ctx:t.ctx,session:r}},deliver(e){return defaultDeliverResult(e)},...l}}export{DELETE,GET,PATCH,POST,PUT,WS,defineChannel,disableRoute,isDisabledRouteSentinel};
1
+ import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,PATCH,POST,PUT,WS}from"#channel/routes.js";const DISABLED_ROUTE_SENTINEL_KIND=`eve:disabled-channel`;function disableRoute(){return{kind:DISABLED_ROUTE_SENTINEL_KIND}}function isDisabledRouteSentinel(e){return typeof e==`object`&&!!e&&e.kind===DISABLED_ROUTE_SENTINEL_KIND}function defineChannel(t){let n=buildAdapter(t),i=normalizeChannelCors(t.cors);return{__kind:CHANNEL_SENTINEL,routes:t.routes,adapter:n,cors:i,receive:t.receive}}const eventTypes=Object.keys({"turn.started":null,"actions.requested":null,"action.result":null,"message.completed":null,"message.appended":null,"reasoning.appended":null,"reasoning.completed":null,"input.requested":null,"turn.failed":null,"turn.completed":null,"turn.cancelled":null,"session.failed":null,"session.completed":null,"session.waiting":null,"authorization.required":null,"authorization.completed":null});function buildAdapter(e){let r=e.state!=null,a=e.context!=null,o=e.fetchFile!==void 0,s=e.metadata,c=r||a||s!==void 0,l={},u=!1,d=e.events;for(let e of eventTypes){let t=d?.[e];t&&(u=!0,l[e]=(r,i)=>{let a={...i,continuationToken:i.session?.continuationToken??``,setContinuationToken:e=>i.session?.setContinuationToken(e)};return e===`session.failed`?t(r,a):t(r,a,buildCallbackContext())})}return!c&&!u&&!o?{kind:e.kindHint??HTTP_ADAPTER_KIND}:{kind:e.kindHint??`defineChannel`,state:r?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:s===void 0?void 0:{metadata(e){return s(e)}},createAdapterContext(t){let n=t.state,r=t.session;return{...a?e.context(n,r):{},state:n,ctx:t.ctx,session:r}},deliver(e){return defaultDeliverResult(e)},...l}}export{DELETE,GET,PATCH,POST,PUT,WS,defineChannel,disableRoute,isDisabledRouteSentinel};
@@ -1,4 +1,4 @@
1
- import{z}from"#compiled/zod/index.js";import{LOAD_SKILL_TOOL_NAME}from"#runtime/skills/fragment-context.js";import{MockLanguageModelV3}from"ai/test";import{FINAL_OUTPUT_TOOL_NAME}from"#runtime/framework-tools/final-output.js";import{BOOTSTRAP_RUNTIME_MODEL_ID,BOOTSTRAP_RUNTIME_SYSTEM_PROMPT}from"#runtime/agent/bootstrap.js";import{createBootstrapGenerateResult,createBootstrapStreamResult,estimateTokenCount,getLastUserPromptText,getPromptContentText,getPromptText}from"#runtime/agent/bootstrap-model-utils.js";import{createMockAuthoredToolInput,formatToolOutput,resolveMockFixtureToken,resolveWeatherCity}from"#runtime/agent/mock-model-fixtures.js";import{findRelevantSkill,getActivatedSkillIds,getAvailableSkills}from"#runtime/agent/mock-model-skill-selection.js";import{createJsonSchemaSample}from"#runtime/agent/mock-structured-output.js";const authoredRuntimeModelMocks=new Map,bootstrapWeatherPayloadSchema=z.object({city:z.string(),condition:z.string(),summary:z.string(),temperatureF:z.number().finite()}).strict();function shouldMockAuthoredRuntimeModels(){return process.env.NODE_ENV===`test`||process.env.EVE_MOCK_AUTHORED_MODELS===`1`}function createMockAuthoredRuntimeModel(e){let t=authoredRuntimeModelMocks.get(e.id);if(t!==void 0)return t;let r=new MockLanguageModelV3({modelId:e.id,provider:`eve-runtime-mock`,doGenerate:async t=>createMockModelResult(t,e.id),doStream:async t=>createBootstrapStreamResult(createMockModelResult(t,e.id))});return authoredRuntimeModelMocks.set(e.id,r),r}function createMockModelResult(e,t){let n=getLastAuthoredToolResult(e.prompt);if(n!==null){let r=createFollowUpToolCallResult({modelId:t,options:e,result:n});if(r!==null)return r}else{let n=createSkillLoadResult(e.prompt,t)??createAuthoredToolCallResult(e,t);if(n!==null)return n}let r=createFinalOutputResult(e,t);if(r!==null)return r;let i=n===null?createAssistantMessage(e.prompt):formatToolResultReply(n,e.prompt);return createBootstrapGenerateResult({inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(i),text:i})}function createFinalOutputResult(e,t){let n=getAvailableTools(e).find(e=>e.name===FINAL_OUTPUT_TOOL_NAME);if(n===void 0)return null;let i=createJsonSchemaSample(n.inputSchema);return createToolCallGenerateResult({input:i,inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(JSON.stringify(i)),toolCallId:createToolCallId(FINAL_OUTPUT_TOOL_NAME),toolName:FINAL_OUTPUT_TOOL_NAME})}function resolveMockAuthoredRuntimeModel(e){return!shouldMockAuthoredRuntimeModels()||e.id===BOOTSTRAP_RUNTIME_MODEL_ID?null:createMockAuthoredRuntimeModel(e)}function createSkillLoadResult(e,n){let r=getLastUserPromptText(e);if(r===null||getActivatedSkillIds(e).length>0)return null;let i=findRelevantSkill(getAvailableSkills(e),r);return i===null?null:createToolCallGenerateResult({input:{skill:i.name},inputTokens:estimateTokenCount(getPromptText(e)),modelId:n,outputTokens:estimateTokenCount(i.name),toolCallId:`call_load_skill`,toolName:LOAD_SKILL_TOOL_NAME})}function createAuthoredToolCallResult(e,t){let n=getLastUserPromptText(e.prompt);if(n===null)return null;let r=findRelevantTool(getAvailableTools(e),n);if(r===null)return null;let i=createMockAuthoredToolInput(r,n,resolveWeatherCity(n));return createToolCallGenerateResult({input:i,inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(Object.values(i).join(` `)),toolCallId:createToolCallId(r.name),toolName:r.name})}function createFollowUpToolCallResult(e){let t=findNextExplicitToolAfterResult({previousToolName:e.result.toolName,prompt:e.options.prompt,tools:getAvailableTools(e.options)});if(t===null)return null;let n=createFollowUpToolInput(e.result.output);return n===null?null:createToolCallGenerateResult({input:n,inputTokens:estimateTokenCount(getPromptText(e.options.prompt)),modelId:e.modelId,outputTokens:estimateTokenCount(Object.values(n).join(` `)),toolCallId:createToolCallId(t.name),toolName:t.name})}function createAssistantMessage(e){let t=getLastUserPromptText(e)??`Hello from eve`,n=getSystemPromptLabels(e),r=resolveSystemProbe(e),i=resolveMockFixtureToken(e);return i===null?n.length>0?r===null?`Bootstrap reply [${n.join(`, `)}]: ${t}`:`Bootstrap reply [${n.join(`, `)}; probe=${r}]: ${t}`:r===null?`Bootstrap reply: ${t}`:`Bootstrap reply [probe=${r}]: ${t}`:i}function formatToolResultReply(e,t){if(e.isError)return`Local weather tool failed: ${formatToolOutput(e.output)}`;if(isWeatherPayload(e.output))return`Used local weather tool for ${e.output.city}: ${e.output.condition}, ${e.output.temperatureF}F. ${e.output.summary}`;let n=getLastUserPromptText(t)??`Hello from eve`;return`Used ${e.toolName} for "${n}": ${formatToolOutput(e.output)}`}function createToolCallGenerateResult(e){return{content:[{input:JSON.stringify(e.input),toolCallId:e.toolCallId,toolName:e.toolName,type:`tool-call`}],finishReason:{raw:void 0,unified:`tool-calls`},response:{id:`bootstrap-response`,modelId:e.modelId,timestamp:new Date(`2026-03-16T00:00:00.000Z`)},usage:{inputTokens:{cacheRead:0,cacheWrite:0,noCache:e.inputTokens,total:e.inputTokens},outputTokens:{reasoning:0,text:e.outputTokens,total:e.outputTokens}},warnings:[]}}function getAvailableTools(e){return(e.tools??[]).flatMap(e=>e.type===`function`?[{description:e.description,inputSchema:`inputSchema`in e?e.inputSchema:void 0,name:e.name,outputSchema:`outputSchema`in e?e.outputSchema:void 0}]:[])}function getLastAuthoredToolResult(e){for(let n of[...e].reverse()){if(n.role===`user`)return null;if(!(n.role!==`tool`&&n.role!==`assistant`)){for(let e of[...n.content].reverse())if(!(typeof e==`string`||e.type!==`tool-result`)&&e.toolName!==LOAD_SKILL_TOOL_NAME)return{isError:e.output.type===`error-json`||e.output.type===`error-text`||e.output.type===`execution-denied`,output:e.output.type===`execution-denied`?{reason:e.output.reason??null,type:e.output.type}:e.output.value,toolCallId:e.toolCallId,toolName:e.toolName}}}return null}function findNextExplicitToolAfterResult(e){let t=getLastUserPromptText(e.prompt);if(t===null)return null;let n=normalizeText(t),r=n.indexOf(normalizeText(e.previousToolName));return r<0?null:e.tools.filter(t=>t.name!==e.previousToolName).flatMap(e=>{let t=n.indexOf(normalizeText(e.name),r+1);return t<0?[]:[{index:t,tool:e}]}).sort((e,t)=>e.index-t.index)[0]?.tool??null}function createFollowUpToolInput(e){return isRecord(e)&&typeof e.stepKey==`string`?{stepKey:e.stepKey}:null}function getSystemPromptLabels(e){let t=e.filter(e=>e.role===`system`);if(t.length===0)return[];let n=t.flatMap(e=>{let t=getPromptContentText(e.content);if(t.startsWith(`Available skills
1
+ import{z}from"#compiled/zod/index.js";import{LOAD_SKILL_TOOL_NAME}from"#runtime/skills/fragment-context.js";import{MockLanguageModelV3}from"ai/test";import{FINAL_OUTPUT_TOOL_NAME}from"#runtime/framework-tools/final-output.js";import{BOOTSTRAP_RUNTIME_MODEL_ID,BOOTSTRAP_RUNTIME_SYSTEM_PROMPT}from"#runtime/agent/bootstrap.js";import{createBootstrapGenerateResult,createBootstrapStreamResult,estimateTokenCount,getLastUserPromptText,getPromptContentText,getPromptText}from"#runtime/agent/bootstrap-model-utils.js";import{createMockAuthoredToolInput,formatToolOutput,resolveMockFixtureToken,resolveWeatherCity}from"#runtime/agent/mock-model-fixtures.js";import{findRelevantSkill,getActivatedSkillIds,getAvailableSkills}from"#runtime/agent/mock-model-skill-selection.js";import{createJsonSchemaSample}from"#runtime/agent/mock-structured-output.js";const authoredRuntimeModelMocks=new Map,bootstrapWeatherPayloadSchema=z.object({city:z.string(),condition:z.string(),summary:z.string(),temperatureF:z.number().finite()}).strict();function shouldMockAuthoredRuntimeModels(){return process.env.NODE_ENV===`test`||process.env.EVE_MOCK_AUTHORED_MODELS===`1`}function createMockAuthoredRuntimeModel(e){let t=authoredRuntimeModelMocks.get(e.id);if(t!==void 0)return t;let r=new MockLanguageModelV3({modelId:e.id,provider:`eve-runtime-mock`,doGenerate:async t=>createMockModelResult(t,e.id),doStream:async t=>createBootstrapStreamResult(createMockModelResult(t,e.id))});return authoredRuntimeModelMocks.set(e.id,r),r}function createMockModelResult(e,t){let n=getLastAuthoredToolResult(e.prompt);if(n!==null){let r=createFollowUpToolCallResult({modelId:t,options:e,result:n});if(r!==null)return r}else{let n=createSubagentDelegationResult(e,t)??createSkillLoadResult(e.prompt,t)??createAuthoredToolCallResult(e,t);if(n!==null)return n}let r=createFinalOutputResult(e,t);if(r!==null)return r;let i=n===null?createAssistantMessage(e.prompt):formatToolResultReply(n,e.prompt);return createBootstrapGenerateResult({inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(i),text:i})}function createFinalOutputResult(e,t){let n=getAvailableTools(e).find(e=>e.name===FINAL_OUTPUT_TOOL_NAME);if(n===void 0)return null;let i=createJsonSchemaSample(n.inputSchema);return createToolCallGenerateResult({input:i,inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(JSON.stringify(i)),toolCallId:createToolCallId(FINAL_OUTPUT_TOOL_NAME),toolName:FINAL_OUTPUT_TOOL_NAME})}function resolveMockAuthoredRuntimeModel(e){return!shouldMockAuthoredRuntimeModels()||e.id===BOOTSTRAP_RUNTIME_MODEL_ID?null:createMockAuthoredRuntimeModel(e)}function createSkillLoadResult(e,n){let r=getLastUserPromptText(e);if(r===null||getActivatedSkillIds(e).length>0)return null;let i=findRelevantSkill(getAvailableSkills(e),r);return i===null?null:createToolCallGenerateResult({input:{skill:i.name},inputTokens:estimateTokenCount(getPromptText(e)),modelId:n,outputTokens:estimateTokenCount(i.name),toolCallId:`call_load_skill`,toolName:LOAD_SKILL_TOOL_NAME})}const SUBAGENT_TOOL_NAME=`agent`,SUBAGENT_DELEGATION_DIRECTIVE=/\bdelegate\s+to\s+a\s+subagent\s*:\s*(.+)$/iu;function createSubagentDelegationResult(e,t){let n=getLastUserPromptText(e.prompt);if(n===null)return null;let r=SUBAGENT_DELEGATION_DIRECTIVE.exec(n);if(r?.[1]===void 0||getAvailableTools(e).find(e=>e.name===SUBAGENT_TOOL_NAME)===void 0)return null;let i={message:r[1].trim()};return createToolCallGenerateResult({input:i,inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(i.message),toolCallId:createToolCallId(SUBAGENT_TOOL_NAME),toolName:SUBAGENT_TOOL_NAME})}function createAuthoredToolCallResult(e,t){let n=getLastUserPromptText(e.prompt);if(n===null)return null;let r=findRelevantTool(getAvailableTools(e),n);if(r===null)return null;let i=createMockAuthoredToolInput(r,n,resolveWeatherCity(n));return createToolCallGenerateResult({input:i,inputTokens:estimateTokenCount(getPromptText(e.prompt)),modelId:t,outputTokens:estimateTokenCount(Object.values(i).join(` `)),toolCallId:createToolCallId(r.name),toolName:r.name})}function createFollowUpToolCallResult(e){let t=findNextExplicitToolAfterResult({previousToolName:e.result.toolName,prompt:e.options.prompt,tools:getAvailableTools(e.options)});if(t===null)return null;let n=createFollowUpToolInput(e.result.output);return n===null?null:createToolCallGenerateResult({input:n,inputTokens:estimateTokenCount(getPromptText(e.options.prompt)),modelId:e.modelId,outputTokens:estimateTokenCount(Object.values(n).join(` `)),toolCallId:createToolCallId(t.name),toolName:t.name})}function createAssistantMessage(e){let t=getLastUserPromptText(e)??`Hello from eve`,n=getSystemPromptLabels(e),r=resolveSystemProbe(e),i=resolveMockFixtureToken(e);return i===null?n.length>0?r===null?`Bootstrap reply [${n.join(`, `)}]: ${t}`:`Bootstrap reply [${n.join(`, `)}; probe=${r}]: ${t}`:r===null?`Bootstrap reply: ${t}`:`Bootstrap reply [probe=${r}]: ${t}`:i}function formatToolResultReply(e,t){if(e.isError)return`Local weather tool failed: ${formatToolOutput(e.output)}`;if(isWeatherPayload(e.output))return`Used local weather tool for ${e.output.city}: ${e.output.condition}, ${e.output.temperatureF}F. ${e.output.summary}`;let n=getLastUserPromptText(t)??`Hello from eve`;return`Used ${e.toolName} for "${n}": ${formatToolOutput(e.output)}`}function createToolCallGenerateResult(e){return{content:[{input:JSON.stringify(e.input),toolCallId:e.toolCallId,toolName:e.toolName,type:`tool-call`}],finishReason:{raw:void 0,unified:`tool-calls`},response:{id:`bootstrap-response`,modelId:e.modelId,timestamp:new Date(`2026-03-16T00:00:00.000Z`)},usage:{inputTokens:{cacheRead:0,cacheWrite:0,noCache:e.inputTokens,total:e.inputTokens},outputTokens:{reasoning:0,text:e.outputTokens,total:e.outputTokens}},warnings:[]}}function getAvailableTools(e){return(e.tools??[]).flatMap(e=>e.type===`function`?[{description:e.description,inputSchema:`inputSchema`in e?e.inputSchema:void 0,name:e.name,outputSchema:`outputSchema`in e?e.outputSchema:void 0}]:[])}function getLastAuthoredToolResult(e){for(let n of[...e].reverse()){if(n.role===`user`)return null;if(!(n.role!==`tool`&&n.role!==`assistant`)){for(let e of[...n.content].reverse())if(!(typeof e==`string`||e.type!==`tool-result`)&&e.toolName!==LOAD_SKILL_TOOL_NAME)return{isError:e.output.type===`error-json`||e.output.type===`error-text`||e.output.type===`execution-denied`,output:e.output.type===`execution-denied`?{reason:e.output.reason??null,type:e.output.type}:e.output.value,toolCallId:e.toolCallId,toolName:e.toolName}}}return null}function findNextExplicitToolAfterResult(e){let t=getLastUserPromptText(e.prompt);if(t===null)return null;let n=normalizeText(t),r=n.indexOf(normalizeText(e.previousToolName));return r<0?null:e.tools.filter(t=>t.name!==e.previousToolName).flatMap(e=>{let t=n.indexOf(normalizeText(e.name),r+1);return t<0?[]:[{index:t,tool:e}]}).sort((e,t)=>e.index-t.index)[0]?.tool??null}function createFollowUpToolInput(e){return isRecord(e)&&typeof e.stepKey==`string`?{stepKey:e.stepKey}:null}function getSystemPromptLabels(e){let t=e.filter(e=>e.role===`system`);if(t.length===0)return[];let n=t.flatMap(e=>{let t=getPromptContentText(e.content);if(t.startsWith(`Available skills
2
2
  `))return[];let n=t.split(`
3
3
  `).map(e=>e.trim()).filter(e=>e.length>0),r=[];for(let e of n){if(e===BOOTSTRAP_RUNTIME_SYSTEM_PROMPT||e===`Available skills`)continue;let t=/^System \((.+)\)$/.exec(e);if(t?.[1]){r.push(t[1]);continue}let n=/^Skill \((.+)\)$/.exec(e);n?.[1]&&r.push(n[1])}if(r.length>0)return r;let i=n.find(e=>e!==BOOTSTRAP_RUNTIME_SYSTEM_PROMPT&&e!==`Available skills`);return i===void 0?[]:[i]});return[...new Set(n)]}function findRelevantTool(e,n){let r=normalizeText(n),i=e.find(e=>e.name!==`agent`&&e.name!==LOAD_SKILL_TOOL_NAME&&r.includes(normalizeText(e.name)));return i===void 0?/\b(forecast|temperature|weather|wind|rain|snow)\b/u.test(r)?e.find(e=>/\b(forecast|temperature|weather|wind|rain|snow)\b/u.test(normalizeText(`${e.name} ${e.description??``}`)))??null:null:i}function normalizeText(e){return e.toLowerCase().replace(/[^a-z0-9]+/gu,` `).trim()}function createToolCallId(e){return`call_${e.toLowerCase().replace(/[^a-z0-9]+/gu,`_`).replace(/^_+|_+$/gu,``)||`tool`}`}function resolveSystemProbe(e){let t=e.filter(e=>e.role===`system`).map(e=>getPromptContentText(e.content)).join(`
4
4
  `);return/hmr-probe:\s*([^\n]+)/iu.exec(t)?.[1]?.trim()||null}function isWeatherPayload(e){return bootstrapWeatherPayloadSchema.safeParse(e).success}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{createMockAuthoredRuntimeModel,resolveMockAuthoredRuntimeModel,shouldMockAuthoredRuntimeModels};
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Reads the dev server's runtime-artifacts revision — an opaque token that
3
3
  * changes whenever the authored-source watcher recompiles (HMR). Consumers
4
- * compare successive values to detect "the agent changed under me": the dev
5
- * client rotates an active session before the next turn or while idle.
4
+ * compare successive values to detect "the agent changed under me" and
5
+ * refresh the development UI before the next turn.
6
6
  *
7
7
  * Never throws: any transport failure, non-2xx response, or malformed body
8
8
  * resolves to `undefined`, so callers treat "unknown" and "unreachable" the
@@ -1,50 +1,42 @@
1
1
  import type { UserContent } from "ai";
2
- import type { ClientSession } from "#client/session.js";
3
2
  import type { InputResponse } from "#runtime/input/types.js";
4
3
  /**
5
- * Tracks local dev runtime-artifact revisions and starts a fresh session for
6
- * normal prompts after HMR, while preserving the current session for
7
- * input-response resumes.
4
+ * Tracks local dev runtime-artifact revisions so callers can refresh their
5
+ * presentation before delivering the next turn.
8
6
  */
9
- export interface DevelopmentRuntimeArtifactSessionRefresher {
7
+ export interface DevelopmentRuntimeArtifactRefresher {
10
8
  /**
11
9
  * Clears the remembered runtime-artifact revision.
12
10
  */
13
11
  clear(): void;
14
12
  /**
15
- * Returns the session that should dispatch the next turn.
13
+ * Refreshes the artifact revision before a normal turn.
16
14
  */
17
15
  refresh(input: {
18
- readonly createSession: () => ClientSession;
19
16
  readonly inputResponses?: readonly InputResponse[];
20
17
  readonly message?: string | UserContent;
21
18
  readonly onRuntimeArtifactsChanged?: (change: DevelopmentRuntimeArtifactChange) => void | Promise<void>;
22
- readonly session: ClientSession;
23
- }): Promise<ClientSession>;
19
+ }): Promise<void>;
24
20
  /**
25
21
  * Forces one rebuild after a local setup action wrote authored source.
26
22
  */
27
23
  refreshAfterSourceChange(input: {
28
- readonly createSession: () => ClientSession;
29
24
  readonly onRuntimeArtifactsChanged?: (change: DevelopmentRuntimeArtifactChange) => void | Promise<void>;
30
- readonly session: ClientSession;
31
- }): Promise<ClientSession>;
25
+ }): Promise<void>;
32
26
  /**
33
27
  * Checks for a runtime-artifact revision change while the UI is idle.
34
28
  */
35
29
  refreshIdle(input: {
36
- readonly createSession: () => ClientSession;
37
30
  readonly onRuntimeArtifactsChanged?: (change: DevelopmentRuntimeArtifactChange) => void | Promise<void>;
38
- readonly session: ClientSession;
39
- }): Promise<ClientSession>;
31
+ }): Promise<void>;
40
32
  }
41
33
  export interface DevelopmentRuntimeArtifactChange {
42
34
  readonly previousRevision: string;
43
35
  readonly revision: string;
44
36
  }
45
37
  /**
46
- * Creates a revision-aware local dev session refresher.
38
+ * Creates a revision-aware local dev runtime-artifact refresher.
47
39
  */
48
- export declare function createDevelopmentRuntimeArtifactSessionRefresher(input: {
40
+ export declare function createDevelopmentRuntimeArtifactRefresher(input: {
49
41
  readonly serverUrl: string;
50
- }): DevelopmentRuntimeArtifactSessionRefresher;
42
+ }): DevelopmentRuntimeArtifactRefresher;
@@ -1 +1 @@
1
- import{isLocalDevelopmentServerUrl}from"#services/dev-client/local-host.js";import{readDevelopmentRuntimeArtifactsRevision,rebuildDevelopmentRuntimeArtifacts}from"#services/dev-client/runtime-artifacts.js";var LocalDevelopmentRuntimeArtifactSessionRefresher=class{#e;#t;#n;constructor(t){this.#e=isLocalDevelopmentServerUrl(t.serverUrl),this.#t=t.serverUrl}clear(){this.#n=void 0}async refresh(e){return shouldRefreshRuntimeArtifactsForTurn(e)?await this.#r({...e,rebuild:!0}):e.session}async refreshIdle(e){return await this.#r({...e,rebuild:!1})}async refreshAfterSourceChange(e){return await this.#r({...e,force:!0,rebuild:!0,resetActiveSession:!0})}async#r(e){if(!this.#e)return e.session;let n=(e.rebuild?await rebuildDevelopmentRuntimeArtifacts({force:e.force,serverUrl:this.#t}):void 0)??await readDevelopmentRuntimeArtifactsRevision({serverUrl:this.#t});if(n===void 0)return e.session;let r=e.session,i=this.#n,a=i!==void 0&&i!==n;return(a||e.resetActiveSession===!0)&&r.state.continuationToken!==void 0&&(r=e.createSession()),a&&await e.onRuntimeArtifactsChanged?.({previousRevision:i,revision:n}),this.#n=n,r}};function shouldRefreshRuntimeArtifactsForTurn(e){return e.message!==void 0&&(e.inputResponses?.length??0)===0}function createDevelopmentRuntimeArtifactSessionRefresher(e){return new LocalDevelopmentRuntimeArtifactSessionRefresher(e)}export{createDevelopmentRuntimeArtifactSessionRefresher};
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};
@@ -24,7 +24,6 @@ export default defineExtension({
24
24
  .env*
25
25
  .eve
26
26
  .vercel
27
- .workflow-data
28
27
  .output
29
28
  .nitro
30
29
  dist
@@ -1,4 +1,4 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.26`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.24.3`,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){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.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{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.26`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.24.4`,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){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.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__",
@@ -58,7 +58,6 @@ You are a helpful assistant.
58
58
  .env*
59
59
  .eve
60
60
  .vercel
61
- .workflow-data
62
61
  .next
63
62
  .output
64
63
  .nitro
@@ -68,7 +67,6 @@ dist
68
67
  `,".vercelignore":`node_modules
69
68
  .env*
70
69
  .eve
71
- .workflow-data
72
70
  .next
73
71
  .output
74
72
  .nitro
@@ -0,0 +1,2 @@
1
+ /** Removes the runtime-owned channel namespace from a continuation token. */
2
+ export declare function toChannelLocalContinuationToken(namespacedToken: string): string;
@@ -0,0 +1 @@
1
+ function toChannelLocalContinuationToken(e){let t=e.indexOf(`:`);return t<0?e:e.slice(t+1)}export{toChannelLocalContinuationToken};
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-dEGV09g_.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-Dwa2j1wE.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-dEGV09g_.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-Dwa2j1wE.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CFBTHlPx.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BP9FpQsJ.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-CFBTHlPx.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BP9FpQsJ.js";
2
2
 
3
3
  export { useEveAgent };
@@ -15,7 +15,7 @@ Work nests in three levels:
15
15
 
16
16
  Every turn runs as a durable workflow, built on the open-source [Workflow SDK](https://workflow-sdk.dev/) (Vercel Workflow when you deploy on Vercel). eve checkpoints progress and serializes durable state at each step boundary. Your code runs inside a managed step, so tools, the sandbox, and subagents feel synchronous even though the session underneath them is durable.
17
17
 
18
- The Workflow SDK is not inherently tied to Vercel. In local development and in a self-deployed `eve start` process, eve uses the SDK's local world by default; that world persists workflow runs on disk under `.workflow-data` and dispatches through the same Nitro-hosted workflow routes. On Vercel, the same workflow code runs against Vercel Workflow instead, which adds platform features such as latest production deployment routing and dashboard run metadata.
18
+ The Workflow SDK is not inherently tied to Vercel. In local development and in a self-deployed `eve start` process, eve uses the SDK's local world by default; that world persists workflow runs on disk under `.eve/.workflow-data` and dispatches through the same Nitro-hosted workflow routes. On Vercel, the same workflow code runs against Vercel Workflow instead, which adds platform features such as latest production deployment routing and dashboard run metadata.
19
19
 
20
20
  When a Vercel production deployment changes, the next model turn in an existing session uses that deployment's current instructions, model, and tools. The durable session keeps its conversation history and authored state, so identity-based channels such as Telegram private chats and Twilio phone-number conversations adopt agent updates without requiring a new session.
21
21
 
@@ -58,7 +58,8 @@ The stream is newline-delimited JSON (NDJSON), one event per line:
58
58
  | `step.failed` | A model step failed; carries `{ code, message, details? }`. |
59
59
  | `turn.completed` | The turn finished. |
60
60
  | `turn.failed` | The turn failed; carries `{ code, message, details? }`. |
61
- | `session.waiting` | The session parked, waiting for the next input (a message, an answer). |
61
+ | `turn.cancelled` | The turn was cancelled before finishing; always followed by `session.waiting`. |
62
+ | `session.waiting` | The session parked for the next input; carries the current channel-owned `continuationToken`. |
62
63
  | `session.failed` | The session failed. |
63
64
  | `session.completed` | The session reached a terminal end. |
64
65
 
@@ -70,11 +71,11 @@ Note: consider the privacy, confidentiality, and user-experience implications fo
70
71
 
71
72
  A delegated subagent publishes progress on its own child-session stream. The parent only emits `subagent.called` with a `childSessionId`, which a client uses to attach.
72
73
 
73
- `step.failed` and `turn.failed` carry `{ code, message, details? }` for the failed fragment or turn, and `session.failed` is the terminal session-level variant. When a turn requested an output schema, the finalized payload lands on `result.completed` as `data.result` before the turn boundary. `authorization.required` carries the sign-in challenge (`data.authorization` may include `url`, `userCode`, `expiresAt`, `instructions`), and `authorization.completed` carries `data.outcome` (`"authorized" | "declined" | "failed" | "timed-out"`).
74
+ `step.failed` and `turn.failed` carry `{ code, message, details? }` for the failed fragment or turn, and `session.failed` is the terminal session-level variant. `turn.cancelled` is not a failure: the cancelled turn ends without any failure event, `session.waiting` follows, and the session accepts the next message normally — whatever the turn streamed before cancellation stays on the stream, while durable history keeps only what had already settled. When a turn requested an output schema, the finalized payload lands on `result.completed` as `data.result` before the turn boundary. `authorization.required` carries the sign-in challenge (`data.authorization` may include `url`, `userCode`, `expiresAt`, `instructions`), and `authorization.completed` carries `data.outcome` (`"authorized" | "declined" | "failed" | "timed-out"`).
74
75
 
75
76
  ## Send a follow-up message
76
77
 
77
- Once the session is waiting (you'll see `session.waiting`), POST your follow-up to the session endpoint with the stored continuation token:
78
+ Once the session is waiting (you'll see `session.waiting`), POST your follow-up to the session endpoint with `event.data.continuationToken`:
78
79
 
79
80
  ```bash
80
81
  curl -X POST http://127.0.0.1:3000/eve/v1/session/<sessionId> \
@@ -90,12 +91,20 @@ For deterministic ordering, send one follow-up at a time and wait for the next `
90
91
 
91
92
  ## Reconnect and rewind
92
93
 
93
- The stream is durable. Every event is recorded before a step completes, so the whole stream is replayable. Pass `startIndex` to reconnect by event count and pick up where you dropped off, or rewind to the start:
94
+ The stream is durable. Every event is recorded before a step completes, so the whole stream is replayable. A nonnegative `startIndex` is an absolute event count: use it to pick up where you dropped off or pass `0` to rewind to the start.
94
95
 
95
96
  ```bash
96
97
  curl "http://127.0.0.1:3000/eve/v1/session/<sessionId>/stream?startIndex=<count>"
97
98
  ```
98
99
 
100
+ A negative `startIndex` reads relative to the stream's current tail. For example, `-1` reads the latest event, which is normally `session.waiting` for a resumable session:
101
+
102
+ ```bash
103
+ curl "http://127.0.0.1:3000/eve/v1/session/<sessionId>/stream?startIndex=-1"
104
+ ```
105
+
106
+ This gives a consumer that only persisted `sessionId` a lightweight way to recover the current `continuationToken`. Because a tail-relative position does not resolve to an absolute consumed-event count, client tail reads do not automatically reconnect or advance the stored cursor.
107
+
99
108
  ## Use the client from TypeScript
100
109
 
101
110
  For scripts, server-to-server calls, tests, evals, and custom UIs, `eve/client` wraps these routes in a typed client so you don't hand-roll the POST and NDJSON stream loop.
@@ -64,7 +64,7 @@ The shorthand can send a follow-up, but it doesn't know the previous stream curs
64
64
 
65
65
  ## Waiting, completed, and failed sessions
66
66
 
67
- When a turn ends with `session.waiting`, the client preserves the state so the next send continues the conversation.
67
+ When a turn ends with `session.waiting`, the event carries the current channel-owned `data.continuationToken` and the client preserves the state so the next send continues the conversation.
68
68
 
69
69
  When a turn ends with `session.completed` or `session.failed`, the client resets its local state. The next send starts a fresh durable session:
70
70
 
@@ -115,6 +115,19 @@ for await (const event of session.stream()) {
115
115
 
116
116
  `stream()` attaches to an existing run; to send new user input, use `send()`. For overriding the cursor with `startIndex` and the full reconnection model, see [Streaming](./streaming#open-a-stream-manually).
117
117
 
118
+ If you only persisted `sessionId`, read the latest durable event to recover the current token from a waiting session:
119
+
120
+ ```ts
121
+ const session = client.session({ sessionId, streamIndex: 0 });
122
+
123
+ for await (const event of session.stream({ startIndex: -1 })) {
124
+ if (event.type === "session.waiting") {
125
+ await saveContinuationToken(event.data.continuationToken);
126
+ }
127
+ break;
128
+ }
129
+ ```
130
+
118
131
  ## What to read next
119
132
 
120
133
  - [Streaming](./streaming): stream events and reconnect by index
@@ -70,7 +70,7 @@ The most common UI events are:
70
70
  | `action.result` | Show tool call results. |
71
71
  | `input.requested` | Pause the UI for approval or a question answer. |
72
72
  | `result.completed` | Read structured output from an [output schema](./output-schema). |
73
- | `session.waiting` | Enable the composer for the next turn. |
73
+ | `session.waiting` | Enable the composer and read the current `data.continuationToken`. |
74
74
  | `session.completed` | Mark the conversation terminal. |
75
75
  | `session.failed` | Mark the conversation failed. |
76
76
 
@@ -124,6 +124,19 @@ for await (const event of session.stream({ startIndex: 0 })) {
124
124
  }
125
125
  ```
126
126
 
127
+ Nonnegative values are absolute event indexes. Negative values read relative to the stream's current tail, so `-1` reads the latest event:
128
+
129
+ ```ts
130
+ for await (const event of session.stream({ startIndex: -1 })) {
131
+ if (event.type === "session.waiting") {
132
+ console.log(event.data.continuationToken);
133
+ }
134
+ break;
135
+ }
136
+ ```
137
+
138
+ Tail-relative attachments do not automatically reconnect or advance the session's stored absolute `streamIndex`. Break after the event you need when using one as a tail lookup.
139
+
127
140
  `stream()` throws if the session has no `sessionId`, because there's no stream to attach to before the first send.
128
141
 
129
142
  ## Abort a request
@@ -140,7 +140,7 @@ Eve writes the standard Nitro output under `.output/` instead of Vercel Build Ou
140
140
 
141
141
  Self-deployed agents should make the Vercel-specific choices explicit:
142
142
 
143
- - Let the Workflow SDK use its default local world, which stores workflow state under `.workflow-data`, configure your host so that directory is on persistent storage, or select another world with `experimental.workflow.world` in the root `agent.ts`. When you select a custom world, install a world package built against the same `@workflow/*` line as your eve release (currently the `5.0.0-beta` line). The npm `latest` tag may lag, so pin the version explicitly, for example `pnpm add @workflow/world-postgres@5.0.0-beta.x`. The Workflow SDK rejects worlds with an incompatible runtime protocol during initialization.
143
+ - Let the Workflow SDK use its default local world, which stores workflow state under `.eve/.workflow-data`; configure your host so that directory is on persistent storage, or select another world with `experimental.workflow.world` in the root `agent.ts`. When you select a custom world, install a world package built against the same `@workflow/*` line as your eve release (currently the `5.0.0-beta` line). The npm `latest` tag may lag, so pin the version explicitly, for example `pnpm add @workflow/world-postgres@5.0.0-beta.x`. The Workflow SDK rejects worlds with an incompatible runtime protocol during initialization.
144
144
  - If you put a reverse proxy or ingress in front of eve, forward **both** `/eve/` and `/.well-known/workflow/`. The workflow world delivers run callbacks to `/.well-known/workflow/v1/flow`; a proxy restricted to `/eve/` lets sessions start but silently stalls runs forever, because the callbacks never reach eve.
145
145
  - Install the AI SDK package for your provider, then use a direct provider model object and `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` when you want no Gateway dependency.
146
146
  - Use `AI_GATEWAY_API_KEY` if you still want Gateway routing from a non-Vercel host.
@@ -158,7 +158,7 @@ For bearer tokens or custom schemes, pass explicit headers with `-H`.
158
158
 
159
159
  Local dev records the last ready URL per resolved app root in `.eve/dev-server-state.v1.json`. A second interactive `eve dev` reconnects only when that URL is loopback and healthy; each terminal UI creates a fresh client session while sharing the server process. A stale or malformed record is replaced when eve starts a new server. Passing `--host`, `--port`, or a `PORT` environment value skips reconnection and reports a healthy recorded server instead.
160
160
 
161
- Local dev keeps immutable runtime source snapshots under `.eve/dev-runtime/snapshots/` so in-flight turns hold a consistent code revision while new turns pick up rebuilds. After a generation is superseded, `eve dev` retains it for at least 30 minutes and also retains the five most recently superseded generations, regardless of the configured Workflow World. The active generation is never pruned. Old runtime snapshots and local sandbox templates are pruned in the background. For manual cleanup, stop `eve dev` before deleting `.eve/dev-runtime/snapshots/` or `.eve/sandbox-cache/local/templates/`. A turn that remains unfinished beyond the automatic retention window can no longer resume after its generation is pruned.
161
+ Local dev keeps immutable runtime source snapshots under `.eve/dev-runtime/snapshots/` so in-flight turns hold a consistent code revision while new turns pick up rebuilds. The terminal REPL keeps its logical session across successful rebuilds, so the next turn continues the conversation on the latest generation; use `/new` to start a fresh session. After a generation is superseded, `eve dev` retains it for at least 30 minutes and also retains the five most recently superseded generations, regardless of the configured Workflow World. The active generation is never pruned. Old runtime snapshots and local sandbox templates are pruned in the background. For manual cleanup, stop `eve dev` before deleting `.eve/dev-runtime/snapshots/` or `.eve/sandbox-cache/local/templates/`. A turn that remains unfinished beyond the automatic retention window can no longer resume after its generation is pruned.
162
162
 
163
163
  ## `eve link`
164
164
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.24.3",
3
+ "version": "0.24.4",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [