eve 0.80.1 → 0.81.1

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 (169) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/src/channel/channel-address.d.ts +1 -0
  3. package/dist/src/channel/channel-address.js +1 -1
  4. package/dist/src/channel/inactive-prompt.d.ts +46 -0
  5. package/dist/src/channel/inactive-prompt.js +1 -0
  6. package/dist/src/channel/principal-identity.d.ts +2 -0
  7. package/dist/src/channel/principal-identity.js +1 -1
  8. package/dist/src/channel/session-not-active-error.d.ts +16 -0
  9. package/dist/src/channel/session-not-active-error.js +1 -0
  10. package/dist/src/chunks/{use-eve-agent-Vsl-S0yI.js → use-eve-agent-Bp6nbhfV.js} +6 -0
  11. package/dist/src/chunks/{use-eve-agent-Ch-Q-s12.js → use-eve-agent-DLkjJIm3.js} +6 -0
  12. package/dist/src/compiled/.vendor-stamp.json +2 -2
  13. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  14. package/dist/src/compiled/@eve/catalog/index.js +1 -1
  15. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  16. package/dist/src/compiler/extension-compatibility.js +1 -1
  17. package/dist/src/context/provider.d.ts +5 -0
  18. package/dist/src/context/providers/connection.js +1 -1
  19. package/dist/src/context/run-step.js +1 -1
  20. package/dist/src/eve-channel/create-request.d.ts +2 -1
  21. package/dist/src/eve-channel/index.js +1 -1
  22. package/dist/src/eve-channel/request.d.ts +2 -1
  23. package/dist/src/eve-channel/request.js +1 -2
  24. package/dist/src/eve-channel/support.d.ts +9 -1
  25. package/dist/src/eve-channel/support.js +2 -1
  26. package/dist/src/eve-channel/types.d.ts +18 -0
  27. package/dist/src/execution/agent-sessions/steps.js +1 -1
  28. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  29. package/dist/src/execution/hook-ownership.d.ts +8 -0
  30. package/dist/src/execution/hook-ownership.js +1 -1
  31. package/dist/src/execution/sandbox/bindings/vercel-lookup.d.ts +6 -0
  32. package/dist/src/execution/sandbox/bindings/vercel-lookup.js +1 -1
  33. package/dist/src/execution/sandbox/bindings/vercel.js +2 -2
  34. package/dist/src/execution/session/entry-input.d.ts +19 -5
  35. package/dist/src/execution/session/entry-input.js +1 -1
  36. package/dist/src/execution/session/entry.js +1 -1
  37. package/dist/src/execution/session/handoff-steps.d.ts +75 -13
  38. package/dist/src/execution/session/handoff-steps.js +1 -1
  39. package/dist/src/execution/session/handoff.d.ts +69 -27
  40. package/dist/src/execution/session/handoff.js +1 -1
  41. package/dist/src/execution/session/program.js +1 -1
  42. package/dist/src/execution/session/run-watch.d.ts +22 -0
  43. package/dist/src/execution/session/run-watch.js +1 -0
  44. package/dist/src/execution/session-inbox/address.d.ts +4 -1
  45. package/dist/src/execution/session-inbox/end-stranded-session.js +1 -1
  46. package/dist/src/execution/session-inbox/hook-release.d.ts +9 -2
  47. package/dist/src/execution/session-inbox/hook-release.js +1 -1
  48. package/dist/src/execution/session-inbox/inbox.d.ts +66 -6
  49. package/dist/src/execution/session-inbox/inbox.js +1 -1
  50. package/dist/src/execution/session-inbox/resume.d.ts +4 -3
  51. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  52. package/dist/src/execution/workflow-runtime.d.ts +6 -11
  53. package/dist/src/execution/workflow-runtime.js +1 -1
  54. package/dist/src/extensions/code/extension/extension.d.ts +2 -0
  55. package/dist/src/extensions/code/extension/extension.js +1 -1
  56. package/dist/src/extensions/git/extension/extension.d.ts +3 -0
  57. package/dist/src/extensions/git/extension/extension.js +1 -1
  58. package/dist/src/extensions/git/extension/lib/attribution.d.ts +22 -0
  59. package/dist/src/extensions/git/extension/lib/attribution.js +4 -0
  60. package/dist/src/extensions/git/extension/lib/gh-tool.js +1 -1
  61. package/dist/src/extensions/git/extension/lib/github-shell.d.ts +13 -1
  62. package/dist/src/extensions/git/extension/lib/github-shell.js +1 -1
  63. package/dist/src/extensions/git/extension/lib/sandbox.d.ts +1 -1
  64. package/dist/src/harness/authorization-event-fields.d.ts +3 -0
  65. package/dist/src/harness/authorization-event-fields.js +1 -1
  66. package/dist/src/harness/hitl/approvals.d.ts +9 -9
  67. package/dist/src/harness/hitl/approvals.js +1 -1
  68. package/dist/src/harness/hitl/approved-calls.d.ts +4 -4
  69. package/dist/src/harness/hitl/approved-calls.js +1 -1
  70. package/dist/src/harness/hitl/candidates.d.ts +7 -1
  71. package/dist/src/harness/hitl/candidates.js +1 -1
  72. package/dist/src/harness/hitl/coordinator.d.ts +2 -2
  73. package/dist/src/harness/hitl/coordinator.js +1 -1
  74. package/dist/src/harness/hitl/index.d.ts +2 -2
  75. package/dist/src/harness/hitl/index.js +1 -1
  76. package/dist/src/harness/hitl/intake.js +1 -1
  77. package/dist/src/harness/hitl/sign-ins.d.ts +10 -0
  78. package/dist/src/harness/hitl/sign-ins.js +1 -1
  79. package/dist/src/harness/inline-tool-authorization.d.ts +10 -11
  80. package/dist/src/harness/inline-tool-authorization.js +1 -1
  81. package/dist/src/harness/model-call/call.js +1 -1
  82. package/dist/src/harness/session-machine/current.d.ts +5 -0
  83. package/dist/src/harness/session-machine/current.js +1 -1
  84. package/dist/src/harness/session-machine/state.d.ts +15 -3
  85. package/dist/src/harness/session-machine/transitions.d.ts +23 -5
  86. package/dist/src/harness/session-machine/transitions.js +1 -1
  87. package/dist/src/harness/step/after-model.js +1 -1
  88. package/dist/src/instrumentation/content.d.ts +1 -1
  89. package/dist/src/instrumentation/content.js +1 -1
  90. package/dist/src/instrumentation/lifecycle.d.ts +4 -2
  91. package/dist/src/internal/application/stamped-package-version.js +1 -1
  92. package/dist/src/internal/authored-definition/connection.js +1 -1
  93. package/dist/src/internal/authored-definition/source-identity.js +1 -1
  94. package/dist/src/internal/mcp/skills.js +1 -1
  95. package/dist/src/internal/workflow/development-world-client.js +1 -1
  96. package/dist/src/protocol/message.d.ts +30 -0
  97. package/dist/src/protocol/message.js +1 -1
  98. package/dist/src/protocol/session-projection.d.ts +8 -1
  99. package/dist/src/protocol/session-projection.js +1 -1
  100. package/dist/src/public/channels/auth.js +1 -1
  101. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +1 -1
  102. package/dist/src/public/channels/chat-sdk/input-actions.d.ts +5 -0
  103. package/dist/src/public/channels/chat-sdk/input-actions.js +2 -2
  104. package/dist/src/public/channels/chat-sdk/messageToUserContent.d.ts +2 -1
  105. package/dist/src/public/channels/chat-sdk/messageToUserContent.js +1 -1
  106. package/dist/src/public/channels/discord/defaults.d.ts +6 -1
  107. package/dist/src/public/channels/discord/defaults.js +2 -2
  108. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  109. package/dist/src/public/channels/photon/attachments.d.ts +5 -2
  110. package/dist/src/public/channels/photon/attachments.js +1 -1
  111. package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -1
  112. package/dist/src/public/channels/slack/api.d.ts +8 -1
  113. package/dist/src/public/channels/slack/api.js +1 -1
  114. package/dist/src/public/channels/slack/approval-cards.js +2 -2
  115. package/dist/src/public/channels/slack/interaction-cards.d.ts +19 -6
  116. package/dist/src/public/channels/slack/interaction-cards.js +1 -1
  117. package/dist/src/public/channels/slack/interactions.js +1 -1
  118. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  119. package/dist/src/public/channels/teams/hitl.d.ts +7 -0
  120. package/dist/src/public/channels/teams/hitl.js +1 -1
  121. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  122. package/dist/src/public/channels/telegram/hitl.d.ts +2 -0
  123. package/dist/src/public/channels/telegram/hitl.js +2 -2
  124. package/dist/src/public/channels/telegram/inbound.d.ts +2 -0
  125. package/dist/src/public/channels/telegram/inbound.js +1 -1
  126. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  127. package/dist/src/public/definitions/channel.js +1 -1
  128. package/dist/src/runtime/connections/mcp-client.d.ts +1 -1
  129. package/dist/src/runtime/connections/mcp-client.js +1 -1
  130. package/dist/src/runtime/connections/registry-types.d.ts +4 -1
  131. package/dist/src/runtime/connections/registry.d.ts +3 -2
  132. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  133. package/dist/src/setup/scaffold/create/project.js +1 -1
  134. package/dist/src/shared/gateway-routing.d.ts +20 -0
  135. package/dist/src/shared/gateway-routing.js +1 -0
  136. package/dist/src/shared/skill-package.js +1 -1
  137. package/dist/src/svelte/index.js +1 -1
  138. package/dist/src/svelte/use-eve-agent.js +1 -1
  139. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  140. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  141. package/dist/src/tracing/eve/agent-otel-provider.js +1 -1
  142. package/dist/src/tracing/eve/gateway.d.ts +5 -0
  143. package/dist/src/tracing/eve/gateway.js +1 -1
  144. package/dist/src/tracing/eve/operation-input.d.ts +0 -7
  145. package/dist/src/tracing/eve/operation-input.js +1 -1
  146. package/dist/src/tracing/eve/policy.d.ts +1 -1
  147. package/dist/src/tracing/eve/policy.js +1 -1
  148. package/dist/src/tracing/eve/session.d.ts +2 -0
  149. package/dist/src/tracing/eve/session.js +1 -1
  150. package/dist/src/vue/index.js +1 -1
  151. package/dist/src/vue/use-eve-agent.js +1 -1
  152. package/docs/README.md +1 -0
  153. package/docs/agent-config.md +22 -0
  154. package/docs/channels/chat-sdk.mdx +4 -2
  155. package/docs/channels/eve.mdx +27 -0
  156. package/docs/channels/photon.mdx +4 -2
  157. package/docs/channels/slack.mdx +4 -0
  158. package/docs/channels/teams.mdx +1 -1
  159. package/docs/channels/telegram.mdx +1 -1
  160. package/docs/code-extension.mdx +20 -6
  161. package/docs/concepts/execution-model-and-durability.mdx +16 -7
  162. package/docs/connections/mcp.mdx +4 -0
  163. package/docs/connections/overview.mdx +2 -2
  164. package/docs/evals/assertions.mdx +21 -0
  165. package/docs/guides/instrumentation/otel.mdx +20 -0
  166. package/docs/patterns/meta.json +1 -0
  167. package/docs/patterns/voice-notes.md +67 -0
  168. package/docs/sandbox/index.mdx +2 -0
  169. package/package.json +1 -1
@@ -1 +1,2 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{defaultEveAuth}from"#eve-channel/types.js";import{SessionStrandedError}from"#channel/session-stranded-error.js";import{workflowEntryReference}from"#execution/workflow-runtime.js";const log=createLogger(`eve.channel`);function healthResponse(){return Response.json({ok:!0,status:`ready`,workflowId:workflowEntryReference.workflowId})}function strandedSessionResponse(e){if(!SessionStrandedError.is(e))return;let t={code:`session_stranded`,error:`This session is no longer available.`,ok:!1};return e.owner.eveVersion!==void 0&&(t.eveVersion=e.owner.eveVersion),Response.json(t,{headers:{"cache-control":`no-store`},status:409})}async function findRemoteAgentBinding(e){let t=await e.parent.getStreamTailIndex();if(t<0)return;let n=(await e.parent.getEventStream({startIndex:0})).getReader(),r;try{for(let i=0;i<=t;i+=1){let t=await n.read();if(t.done)break;r=readRemoteAgentBinding(t.value,e)??r}}finally{await n.cancel().catch(()=>{})}return r}function readRemoteAgentBinding(e,t){if(e.type!==`agent.started`)return;let{data:n}=e;if(n.callId===t.callId&&n.sessionId===t.childSessionId&&n.streamPath===t.childStreamPath&&n.remote!==void 0)return{name:n.name,...n.remote}}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 t=e.config.onMessage??defaultOnMessage,n;try{let r={caller:e.auth,request:e.request};if(n=await t({eve:e.sessionId===void 0?e.invocation===void 0?r:{...r,invocation:e.invocation}:{...r,sessionId:e.sessionId}},e.message),n==null)throw TypeError(`eveChannel onMessage must return an auth result.`)}catch(t){let n=logError(log,`onMessage handler failed`,t,{sessionId:e.sessionId});return Response.json({error:`onMessage handler failed.`,errorId:n,ok:!1},{status:500})}return{auth:n.auth,context:n.context,title:n.title}}function defaultOnMessage(e){return{auth:defaultEveAuth(e)}}export{findRemoteAgentBinding,healthResponse,normalizeEveCors,resolveOnMessage,strandedSessionResponse};
1
+ import{createLogger,logError}from"#internal/logging.js";import{SessionStrandedError}from"#channel/session-stranded-error.js";import{defaultEveAuth}from"#eve-channel/types.js";import{workflowEntryReference}from"#execution/workflow-runtime.js";const log=createLogger(`eve.channel`);function healthResponse(){return Response.json({ok:!0,status:`ready`,workflowId:workflowEntryReference.workflowId})}function strandedSessionResponse(e){if(!SessionStrandedError.is(e))return;let t={code:`session_stranded`,error:`This session is no longer available.`,ok:!1};return e.owner.eveVersion!==void 0&&(t.eveVersion=e.owner.eveVersion),Response.json(t,{headers:{"cache-control":`no-store`},status:409})}async function findRemoteAgentBinding(e){let t=await e.parent.getStreamTailIndex();if(t<0)return;let n=(await e.parent.getEventStream({startIndex:0})).getReader();try{for(let r=0;r<=t;r+=1){let t=await n.read();if(t.done)break;let r=readRemoteAgentBinding(t.value,e);if(r!==void 0)return r}}finally{await n.cancel().catch(()=>{})}}function readRemoteAgentBinding(e,t){if(e.type!==`agent.started`)return;let{data:n}=e;if(n.callId===t.callId&&n.sessionId===t.childSessionId&&n.streamPath===t.childStreamPath&&n.remote!==void 0)return{name:n.name,...n.remote}}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 t=e.config.onMessage??defaultOnMessage,n;try{let r={caller:e.auth,clientContext:e.clientContext,request:e.request};if(n=await t({eve:e.sessionId===void 0?e.invocation===void 0?r:{...r,invocation:e.invocation}:{...r,sessionId:e.sessionId}},e.message),n==null)throw TypeError(`eveChannel onMessage must return an auth result.`)}catch(t){let n=logError(log,`onMessage handler failed`,t,{sessionId:e.sessionId});return Response.json({error:`onMessage handler failed.`,errorId:n,ok:!1},{status:500})}return{auth:n.auth,clientContext:resolveTurnClientContext(n.clientContext,e.clientContext),context:n.context,title:n.title}}function resolveTurnClientContext(e,t){let n=e===void 0?toContextEntries(t).map(e=>`Client context:
2
+ ${e}`):toContextEntries(e);return n.length===0?void 0:n}function toContextEntries(e){return e===void 0||e===``?[]:typeof e==`string`?[e]:Array.isArray(e)?e:[JSON.stringify(e)]}function defaultOnMessage(e){return{auth:defaultEveAuth(e)}}export{findRemoteAgentBinding,healthResponse,normalizeEveCors,resolveOnMessage,resolveTurnClientContext,strandedSessionResponse};
@@ -4,6 +4,7 @@ import type { TrustedForwarders } from "#channel/forwarded-principal.js";
4
4
  import type { AuthFn } from "#public/channels/auth.js";
5
5
  import type { UploadPolicyInput } from "#public/channels/upload-policy.js";
6
6
  import type { FetchFileFunction } from "#shared/channel-definition.js";
7
+ import type { JsonObject } from "#shared/json.js";
7
8
  import type { AudienceContext, Channel, ChannelContinuationOps, ChannelEvents, ChannelMethod } from "#public/definitions/channel.js";
8
9
  import type { ChannelAudience } from "#shared/channel-audience.js";
9
10
  export type { ForwardedAssertion, TrustedForwarders } from "#channel/forwarded-principal.js";
@@ -39,10 +40,17 @@ export interface EveChannelCorsOptions {
39
40
  * fully permissive browser access, or pass an options object to narrow it.
40
41
  */
41
42
  export type EveChannelCors = boolean | EveChannelCorsOptions;
43
+ /**
44
+ * Turn-scoped context: a string, an array of strings, or a JSON object. The same
45
+ * shape the eve client sends as `clientContext`.
46
+ */
47
+ export type ClientContextValue = string | readonly string[] | JsonObject;
42
48
  /** Low-level eve HTTP handle exposed to `eveChannel({ onMessage })`. */
43
49
  export interface EveHandle {
44
50
  /** Route-auth result for the request; `onMessage` chooses session auth by returning `{ auth }`. */
45
51
  readonly caller: SessionAuthContext | null;
52
+ /** The request's `clientContext`, exactly as the client sent it. `undefined` when it sent none. */
53
+ readonly clientContext?: ClientContextValue;
46
54
  /** Replay-stable identity of a trusted remote-subagent create operation. */
47
55
  readonly invocation?: {
48
56
  readonly operationId: string;
@@ -61,7 +69,17 @@ export interface EveMessageContext {
61
69
  */
62
70
  export type EveMessageResult = {
63
71
  readonly auth: SessionAuthContext | null;
72
+ /** Context strings prepended as user messages and kept in session history. */
64
73
  readonly context?: readonly string[];
74
+ /**
75
+ * Context for this turn only, replacing the request's `clientContext`. Like
76
+ * request `clientContext`, it reaches every model call in the turn and is not
77
+ * kept in session history. It takes the same shape as request `clientContext`
78
+ * and becomes model messages the same way, without the `Client context:` label
79
+ * eve adds to client-supplied entries. Return `[]` to drop the request's
80
+ * context; omit it to keep the request's context.
81
+ */
82
+ readonly clientContext?: ClientContextValue;
65
83
  /** Sets the title when creating a workflow or sending its first message after prewarming. */
66
84
  readonly title?: string;
67
85
  };
@@ -1 +1 @@
1
- import{createLogger,logError}from"#internal/logging.js";import{sessionInboxHookToken}from"#execution/session-inbox/address.js";import{ContextContainer,contextStorage}from"#context/container.js";import{createEveCallbackRoutePath}from"#protocol/routes.js";import{agentDelegation}from"#tracing/eve/delegation.js";import{SessionStrandedError}from"#channel/session-stranded-error.js";import{createWorkflowRuntime,dispatchWorkflowSessionCommand,resolveContinuation,waitForCommandHookOwner,waitForSessionInbox}from"#execution/workflow-runtime.js";import{resolveDurableCompiledArtifactsSource}from"#runtime/durable-compiled-artifacts-source.js";import{getStepMetadata,getWorkflowMetadata}from"#compiled/@workflow/core/index.js";import{createWorkflowCallbackUrl,resolveWorkflowCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{resolveAgentAction,resolveAgentStartTarget}from"#execution/agent-sessions/target.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{cancelRemoteAgentTurn,continueRemoteAgentSession,resetRemoteAgentSession,resolveRemoteAgentForAction,startRemoteAgentSession}from"#execution/agent-sessions/remote.js";import{buildSubagentRunInput}from"#subagents/tool.js";import{resolveConversationId}from"#shared/conversation-identity.js";import{stubToolPath}from"#tool-stubs/target.js";const log=createLogger(`execution.agent-sessions`),RUN_FINISHED_REASON=`The workflow run that opened this session finished.`;async function openAgentSessionStep(e){"use step";let{context:t}=e,n=await loadAgentSessionBundle(t),r=resolveAgentAction({bundle:n,callId:t.parent.callId,dynamicSelections:t.dynamicSelections,input:{message:e.message,outputSchema:e.outputSchema,target:e.name}}),i=resolveAgentStartTarget({action:r,bundle:n,dynamicSelections:t.dynamicSelections,isRootSession:t.parent.rootSessionId===t.parent.sessionId}),a={auth:e.auth,bundle:n,context:t,key:e.key,replyTo:e.replyTo};return await agentDelegation.resume({context:t,agentName:r.name},()=>i.kind===`remote`?startRemoteSession({...a,target:i}):startLocalSession({...a,target:i}))}async function sendAgentSessionMessageStep(e){"use step";let{address:t,context:n}=e;if(t.kind===`remote`){let r=await resolveSessionRemote(n,t);await continueRemoteAgentSession({auth:e.auth.current,callback:{callId:n.parent.callId,subagentName:t.name,token:e.replyTo,url:createWorkflowCallbackUrl(t.callbackBaseUrl,createEveCallbackRoutePath(e.replyTo))},message:e.message,outputSchema:e.outputSchema,remote:r,sessionId:t.sessionId});return}if((await commandLocalSession({...e,address:t},{auth:e.auth.current,schedule:n.schedule,caller:{callId:n.parent.callId,replyTo:{kind:`hook`,token:e.replyTo},subagentName:t.name},kind:`send`,payload:{message:e.message,outputSchema:e.outputSchema}}).catch(e=>{throw SessionStrandedError.is(e)?Error(`Agent "${t.name}" can no longer receive messages: ${e.message}`,{cause:e}):e})).status!==`accepted`)throw Error(`Agent "${t.name}" can no longer receive messages; its session ended.`)}async function cancelAgentSessionTurnStep(e){"use step";let{address:t}=e;try{if(t.kind===`remote`){let n=await resolveSessionRemote(e.context,t);await cancelRemoteAgentTurn({remote:n,sessionId:t.sessionId});return}await commandLocalSession({...e,address:t},{kind:`cancel`})}catch(e){logError(log,`failed to cancel an agent session turn`,e,{name:t.name,sessionId:t.sessionId})}}async function endAgentSessionsStep(e){"use step";await Promise.all(e.sessions.map(endAgentSession))}async function endAgentSession(e){let{address:t,context:n}=e;try{if(t.kind===`remote`){let e=await resolveSessionRemote(n,t);await resetRemoteAgentSession({reason:RUN_FINISHED_REASON,remote:e,sessionId:t.sessionId});return}await commandLocalSession({...e,address:t},{kind:`reset`,reason:RUN_FINISHED_REASON})}catch(e){logError(log,`failed to end an agent session`,e,{name:t.name,sessionId:t.sessionId})}}async function commandLocalSession(e,t){let{continuationToken:n,sessionId:r}=e.address,i=await dispatchWorkflowSessionCommand({command:t,sessionId:r});if(i.status===`accepted`||i.status===`reset`)return i;let a=await waitForSessionInbox(r)?r:(await resolveContinuation(n))?.sessionId;return a===void 0?i:await dispatchWorkflowSessionCommand({command:t,sessionId:a})}async function startLocalSession(e){let{auth:t,bundle:n,context:r,target:i}=e,{action:a}=i,{childContinuationToken:o,runInput:s}=buildSubagentRunInput({action:a,auth:t.current,capabilities:r.capabilities,schedule:r.schedule,channelMetadata:r.channelMetadata,continuationKey:e.key,graph:n.graph,inheritedConversation:r.conversation,initiatorAuth:t.initiator,limits:r.limits,parent:createParentContext(r,e.replyTo),sandboxSessionId:r.sandbox.sessionId,selfAgent:i.source.type===`runtime`,session:{continuationToken:e.replyTo,sandboxState:r.sandbox.state,sessionId:r.parent.sessionId},source:i.source}),c=createWorkflowRuntime({compiledArtifactsSource:n.compiledArtifactsSource,dynamicSubagentAgentConfig:i.dynamicSubagentAgentConfig,nodeId:a.nodeId}),l=r.toolStubs;l!==void 0&&(l={...l,agentPath:stubToolPath(l,a.name)});let u=(await contextStorage.run(new ContextContainer({localDevRequest:r.localDevRequest}),()=>c.createSession({...s,toolStubs:l}))).sessionId;return getStepMetadata().attempt>1&&({runId:u}=await waitForCommandHookOwner(sessionInboxHookToken(o))),{continuationToken:o,kind:`local`,name:a.name,nodeId:a.nodeId,sessionId:u}}async function startRemoteSession(e){let{auth:t,bundle:n,context:r,target:i}=e,{action:a}=i,o=resolveRemoteAgentForAction({dynamicRemoteAgent:i.dynamicRemoteAgent,nodeId:a.nodeId,registry:n.subagentRegistry.subagentsByNodeId,remoteAgentName:a.remoteAgentName}),s=resolveWorkflowCallbackBaseUrl(getWorkflowMetadata().url),c=await startRemoteAgentSession({action:a,auth:t.current,callbackBaseUrl:s,capabilities:r.capabilities,initiatorAuth:t.initiator,operationId:`agent-session:${e.key}`,originAudience:r.trace.originAudience,parent:createParentContext(r,e.replyTo),remote:o,session:{continuationToken:e.replyTo}});return{callbackBaseUrl:s,kind:`remote`,forwardPrincipal:o.forwardPrincipal,name:a.remoteAgentName,nodeId:a.nodeId,resolverId:i.dynamicRemoteAgent?.credentialsStepId??a.nodeId,sessionId:c.sessionId,url:o.url}}function createParentContext(e,t){return{continuationToken:t,conversationId:e.trace.conversationId??resolveConversationId(e.parent.rootSessionId),lineage:e.parent,originAudience:e.trace.originAudience,traceContext:e.trace.parentTraceContext,traceRoot:e.traceRoot}}async function resolveSessionRemote(e,t){let n=await loadAgentSessionBundle(e),r=e.dynamicSelections[t.nodeId];return{...resolveRemoteAgentForAction({dynamicRemoteAgent:r?.kind===`remote`?r.remoteAgent:void 0,nodeId:t.nodeId,registry:n.subagentRegistry.subagentsByNodeId,remoteAgentName:t.name}),url:t.url}}async function loadAgentSessionBundle(e){return await getCompiledRuntimeAgentBundle({compiledArtifactsSource:resolveDurableCompiledArtifactsSource(e.bundle.source),nodeId:e.bundle.nodeId})}export{cancelAgentSessionTurnStep,endAgentSessionsStep,openAgentSessionStep,sendAgentSessionMessageStep};
1
+ import{createLogger,logError}from"#internal/logging.js";import{sessionInboxHookToken}from"#execution/session-inbox/address.js";import{ContextContainer,contextStorage}from"#context/container.js";import{createEveCallbackRoutePath}from"#protocol/routes.js";import{SessionStrandedError}from"#channel/session-stranded-error.js";import{agentDelegation}from"#tracing/eve/delegation.js";import{createWorkflowRuntime,dispatchWorkflowSessionCommand,resolveContinuation,waitForCommandHookOwner,waitForSessionInbox}from"#execution/workflow-runtime.js";import{resolveDurableCompiledArtifactsSource}from"#runtime/durable-compiled-artifacts-source.js";import{getStepMetadata,getWorkflowMetadata}from"#compiled/@workflow/core/index.js";import{createWorkflowCallbackUrl,resolveWorkflowCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{resolveAgentAction,resolveAgentStartTarget}from"#execution/agent-sessions/target.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{cancelRemoteAgentTurn,continueRemoteAgentSession,resetRemoteAgentSession,resolveRemoteAgentForAction,startRemoteAgentSession}from"#execution/agent-sessions/remote.js";import{buildSubagentRunInput}from"#subagents/tool.js";import{resolveConversationId}from"#shared/conversation-identity.js";import{stubToolPath}from"#tool-stubs/target.js";const log=createLogger(`execution.agent-sessions`),RUN_FINISHED_REASON=`The workflow run that opened this session finished.`;async function openAgentSessionStep(e){"use step";let{context:t}=e,n=await loadAgentSessionBundle(t),r=resolveAgentAction({bundle:n,callId:t.parent.callId,dynamicSelections:t.dynamicSelections,input:{message:e.message,outputSchema:e.outputSchema,target:e.name}}),i=resolveAgentStartTarget({action:r,bundle:n,dynamicSelections:t.dynamicSelections,isRootSession:t.parent.rootSessionId===t.parent.sessionId}),a={auth:e.auth,bundle:n,context:t,key:e.key,replyTo:e.replyTo};return await agentDelegation.resume({context:t,agentName:r.name},()=>i.kind===`remote`?startRemoteSession({...a,target:i}):startLocalSession({...a,target:i}))}async function sendAgentSessionMessageStep(e){"use step";let{address:t,context:n}=e;if(t.kind===`remote`){let r=await resolveSessionRemote(n,t);await continueRemoteAgentSession({auth:e.auth.current,callback:{callId:n.parent.callId,subagentName:t.name,token:e.replyTo,url:createWorkflowCallbackUrl(t.callbackBaseUrl,createEveCallbackRoutePath(e.replyTo))},message:e.message,outputSchema:e.outputSchema,remote:r,sessionId:t.sessionId});return}if((await commandLocalSession({...e,address:t},{auth:e.auth.current,schedule:n.schedule,caller:{callId:n.parent.callId,replyTo:{kind:`hook`,token:e.replyTo},subagentName:t.name},kind:`send`,payload:{message:e.message,outputSchema:e.outputSchema}}).catch(e=>{throw SessionStrandedError.is(e)?Error(`Agent "${t.name}" can no longer receive messages: ${e.message}`,{cause:e}):e})).status!==`accepted`)throw Error(`Agent "${t.name}" can no longer receive messages; its session ended.`)}async function cancelAgentSessionTurnStep(e){"use step";let{address:t}=e;try{if(t.kind===`remote`){let n=await resolveSessionRemote(e.context,t);await cancelRemoteAgentTurn({remote:n,sessionId:t.sessionId});return}await commandLocalSession({...e,address:t},{kind:`cancel`})}catch(e){logError(log,`failed to cancel an agent session turn`,e,{name:t.name,sessionId:t.sessionId})}}async function endAgentSessionsStep(e){"use step";await Promise.all(e.sessions.map(endAgentSession))}async function endAgentSession(e){let{address:t,context:n}=e;try{if(t.kind===`remote`){let e=await resolveSessionRemote(n,t);await resetRemoteAgentSession({reason:RUN_FINISHED_REASON,remote:e,sessionId:t.sessionId});return}await commandLocalSession({...e,address:t},{kind:`reset`,reason:RUN_FINISHED_REASON})}catch(e){logError(log,`failed to end an agent session`,e,{name:t.name,sessionId:t.sessionId})}}async function commandLocalSession(e,t){let{continuationToken:n,sessionId:r}=e.address,i=await dispatchWorkflowSessionCommand({command:t,sessionId:r});if(i.status===`accepted`||i.status===`reset`)return i;let a=await waitForSessionInbox(r)?r:(await resolveContinuation(n))?.sessionId;return a===void 0?i:await dispatchWorkflowSessionCommand({command:t,sessionId:a})}async function startLocalSession(e){let{auth:t,bundle:n,context:r,target:i}=e,{action:a}=i,{childContinuationToken:o,runInput:s}=buildSubagentRunInput({action:a,auth:t.current,capabilities:r.capabilities,schedule:r.schedule,channelMetadata:r.channelMetadata,continuationKey:e.key,graph:n.graph,inheritedConversation:r.conversation,initiatorAuth:t.initiator,limits:r.limits,parent:createParentContext(r,e.replyTo),sandboxSessionId:r.sandbox.sessionId,selfAgent:i.source.type===`runtime`,session:{continuationToken:e.replyTo,sandboxState:r.sandbox.state,sessionId:r.parent.sessionId},source:i.source}),c=createWorkflowRuntime({compiledArtifactsSource:n.compiledArtifactsSource,dynamicSubagentAgentConfig:i.dynamicSubagentAgentConfig,nodeId:a.nodeId}),l=r.toolStubs;l!==void 0&&(l={...l,agentPath:stubToolPath(l,a.name)});let u=(await contextStorage.run(new ContextContainer({localDevRequest:r.localDevRequest}),()=>c.createSession({...s,toolStubs:l}))).sessionId;return getStepMetadata().attempt>1&&({runId:u}=await waitForCommandHookOwner(sessionInboxHookToken(o))),{continuationToken:o,kind:`local`,name:a.name,nodeId:a.nodeId,sessionId:u}}async function startRemoteSession(e){let{auth:t,bundle:n,context:r,target:i}=e,{action:a}=i,o=resolveRemoteAgentForAction({dynamicRemoteAgent:i.dynamicRemoteAgent,nodeId:a.nodeId,registry:n.subagentRegistry.subagentsByNodeId,remoteAgentName:a.remoteAgentName}),s=resolveWorkflowCallbackBaseUrl(getWorkflowMetadata().url),c=await startRemoteAgentSession({action:a,auth:t.current,callbackBaseUrl:s,capabilities:r.capabilities,initiatorAuth:t.initiator,operationId:`agent-session:${e.key}`,originAudience:r.trace.originAudience,parent:createParentContext(r,e.replyTo),remote:o,session:{continuationToken:e.replyTo}});return{callbackBaseUrl:s,kind:`remote`,forwardPrincipal:o.forwardPrincipal,name:a.remoteAgentName,nodeId:a.nodeId,resolverId:i.dynamicRemoteAgent?.credentialsStepId??a.nodeId,sessionId:c.sessionId,url:o.url}}function createParentContext(e,t){return{continuationToken:t,conversationId:e.trace.conversationId??resolveConversationId(e.parent.rootSessionId),lineage:e.parent,originAudience:e.trace.originAudience,traceContext:e.trace.parentTraceContext,traceRoot:e.traceRoot}}async function resolveSessionRemote(e,t){let n=await loadAgentSessionBundle(e),r=e.dynamicSelections[t.nodeId];return{...resolveRemoteAgentForAction({dynamicRemoteAgent:r?.kind===`remote`?r.remoteAgent:void 0,nodeId:t.nodeId,registry:n.subagentRegistry.subagentsByNodeId,remoteAgentName:t.name}),url:t.url}}async function loadAgentSessionBundle(e){return await getCompiledRuntimeAgentBundle({compiledArtifactsSource:resolveDurableCompiledArtifactsSource(e.bundle.source),nodeId:e.bundle.nodeId})}export{cancelAgentSessionTurnStep,endAgentSessionsStep,openAgentSessionStep,sendAgentSessionMessageStep};
@@ -1 +1 @@
1
- import{runJsProgram}from"#execution/dynamic-workflow/workflow.js";async function runWorkflowProgramTask(e,t){"use workflow";return runJsProgram(e.js,t,{maxSubagents:e.maxSubagents??0})}runWorkflowProgramTask.workflowId=`workflow//eve@0.80.1//runWorkflowProgramTask`;export{runWorkflowProgramTask};
1
+ import{runJsProgram}from"#execution/dynamic-workflow/workflow.js";async function runWorkflowProgramTask(e,t){"use workflow";return runJsProgram(e.js,t,{maxSubagents:e.maxSubagents??0})}runWorkflowProgramTask.workflowId=`workflow//eve@0.81.1//runWorkflowProgramTask`;export{runWorkflowProgramTask};
@@ -9,3 +9,11 @@ export declare function isHookConflictError(error: unknown): error is {
9
9
  readonly name: "HookConflictError";
10
10
  readonly token?: unknown;
11
11
  };
12
+ /**
13
+ * Another run took the hook's token with `experimental_force`. Matched by name:
14
+ * `HookForceClaimedError.is` lives in a module the workflow bundle cannot load.
15
+ */
16
+ export declare function isHookForceClaimedError(error: unknown): error is {
17
+ readonly claimedByRunId: string;
18
+ readonly name: "HookForceClaimedError";
19
+ };
@@ -1 +1 @@
1
- async function claimHookOwnership(e){let t;try{t=await e.getConflict()}catch(t){return await disposeAndThrow(e,t)}if(t!==null)return await disposeAndThrow(e,createHookConflictError(e.token,t.runId))}async function disposeHook(e){await e.dispose()}async function disposeAndThrow(e,t){try{await disposeHook(e)}catch{}throw t}function isHookConflictError(e){return typeof e==`object`&&!!e&&`name`in e&&e.name===`HookConflictError`}function createHookConflictError(e,t){let n=` (run "${t}")`;return Object.assign(Error(`Hook token "${e}" is already in use${n}`),{conflictingRunId:t,name:`HookConflictError`,token:e})}export{claimHookOwnership,disposeHook,isHookConflictError};
1
+ async function claimHookOwnership(e){let t;try{t=await e.getConflict()}catch(t){return await disposeAndThrow(e,t)}if(t!==null)return await disposeAndThrow(e,createHookConflictError(e.token,t.runId))}async function disposeHook(e){await e.dispose()}async function disposeAndThrow(e,t){try{await disposeHook(e)}catch{}throw t}function isHookConflictError(e){return typeof e==`object`&&!!e&&`name`in e&&e.name===`HookConflictError`}function isHookForceClaimedError(e){return typeof e==`object`&&!!e&&`name`in e&&e.name===`HookForceClaimedError`&&`claimedByRunId`in e&&typeof e.claimedByRunId==`string`}function createHookConflictError(e,t){let n=` (run "${t}")`;return Object.assign(Error(`Hook token "${e}" is already in use${n}`),{conflictingRunId:t,name:`HookConflictError`,token:e})}export{claimHookOwnership,disposeHook,isHookConflictError,isHookForceClaimedError};
@@ -4,3 +4,9 @@ export declare function getNamedVercelSandbox(input: {
4
4
  readonly sandboxModule: VercelModule;
5
5
  readonly sandboxName: string;
6
6
  }): Promise<VercelSandbox | null>;
7
+ /** Whether a snapshot exists and finished, so a sandbox can still start from it. */
8
+ export declare function isVercelSnapshotCreated(input: {
9
+ readonly createOptions: VercelCreateOptions;
10
+ readonly sandboxModule: VercelModule;
11
+ readonly snapshotId: string;
12
+ }): Promise<boolean>;
@@ -1 +1 @@
1
- import{getVercelSandboxFetch,resolveVercelSandboxCredentials}from"#execution/sandbox/bindings/vercel-credentials.js";import{isVercelSandboxMissingError}from"#execution/sandbox/bindings/vercel-errors.js";import{errorMessage}from"#execution/sandbox/bindings/vercel-options.js";async function getNamedVercelSandbox(e){try{return await e.sandboxModule.Sandbox.get(await getVercelSandboxGetOptions(e))}catch(t){if(isVercelSandboxMissingError(t))return null;throw Error(`Failed to look up Vercel sandbox "${e.sandboxName}": ${errorMessage(t)}`,{cause:t})}}async function getVercelSandboxGetOptions(e){return{...await resolveVercelSandboxCredentials(e.createOptions),fetch:getVercelSandboxFetch(e.createOptions),name:e.sandboxName,resume:!1,signal:e.createOptions.signal}}export{getNamedVercelSandbox};
1
+ import{getVercelSandboxFetch,resolveVercelSandboxCredentials}from"#execution/sandbox/bindings/vercel-credentials.js";import{isVercelSandboxMissingError,isVercelSnapshotUnavailableError}from"#execution/sandbox/bindings/vercel-errors.js";import{errorMessage}from"#execution/sandbox/bindings/vercel-options.js";async function getNamedVercelSandbox(e){try{return await e.sandboxModule.Sandbox.get(await getVercelSandboxGetOptions(e))}catch(t){if(isVercelSandboxMissingError(t))return null;throw Error(`Failed to look up Vercel sandbox "${e.sandboxName}": ${errorMessage(t)}`,{cause:t})}}async function isVercelSnapshotCreated(e){try{return(await e.sandboxModule.Snapshot.get({...await resolveVercelSandboxCredentials(e.createOptions),fetch:getVercelSandboxFetch(e.createOptions),signal:e.createOptions.signal,snapshotId:e.snapshotId})).status===`created`}catch(t){if(isVercelSandboxMissingError(t)||isVercelSnapshotUnavailableError(t))return!1;throw Error(`Failed to look up Vercel snapshot "${e.snapshotId}": ${errorMessage(t)}`,{cause:t})}}async function getVercelSandboxGetOptions(e){return{...await resolveVercelSandboxCredentials(e.createOptions),fetch:getVercelSandboxFetch(e.createOptions),name:e.sandboxName,resume:!1,signal:e.createOptions.signal}}export{getNamedVercelSandbox,isVercelSnapshotCreated};
@@ -1,2 +1,2 @@
1
- import{isToolSessionId}from"#execution/tool-session/id.js";import{withToolSessionSandboxes}from"#execution/tool-session/sandbox.js";import{streamToBuffer}from"#execution/sandbox/stream-utils.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{SANDBOX_AGENT_ENV,buildSandboxSession}from"#execution/sandbox/session.js";import{VERCEL_EVE_SANDBOX_IMAGE}from"#execution/sandbox/bindings/eve-image.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{createSandboxProviderIdentity}from"#execution/sandbox/provider-identity.js";import{isSandboxPreparedArtifactRecord,providerResourceTargetFiles,sandboxProviderResourceIdentity}from"#shared/sandbox-provider.js";import{SandboxTemplateNotProvisionedError}from"#shared/sandbox-template-error.js";import{adaptMultiplexedCommandToSandboxProcess}from"#execution/sandbox/multiplexed-command.js";import{resolveSandboxModelPath}from"#shared/skill-paths.js";import{isVercelSandboxMissingError,isVercelSandboxNameConflictError,isVercelSnapshotNotFoundError,isVercelSnapshotUnavailableError}from"#execution/sandbox/bindings/vercel-errors.js";import{ensureVercelSandboxTags,errorMessage,resolveVercelSandboxTags}from"#execution/sandbox/bindings/vercel-options.js";import{applyInitialVercelNetworkPolicy,createVercelNetworkPolicySetter,ensureVercelSandboxBaseRuntime,withBaseSetupNetworkPolicy}from"#execution/sandbox/bindings/vercel-base-runtime.js";import{createVercelEveImageSandbox}from"#execution/sandbox/bindings/vercel-create-sdk.js";import{withToolSessionRetention}from"#execution/sandbox/bindings/vercel-tool-sessions.js";import{getNamedVercelSandbox}from"#execution/sandbox/bindings/vercel-lookup.js";import{deleteUnusableVercelSandbox,deleteVercelSandbox,stopVercelSandbox}from"#execution/sandbox/bindings/vercel-lifecycle.js";import{normalizeVercelReadStream}from"#execution/sandbox/bindings/vercel-read-stream.js";function createVercelSandboxProvider(e){let{prepare:t,...n}=e??{};return createVercelSandbox({createOptions:n,prepare:t})}function createVercelSandbox(e={}){let t=e.loadSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),n=e.loadDeleteSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),r={timeout:DEFAULT_SANDBOX_TIMEOUT_MS,...e.createOptions},i=e.createSandbox??createVercelEveImageSandbox;async function openSession(e,a,o,s){let c=requirePreparedVercelTemplate(o),{mounts:l,...u}=a??{},d=isToolSessionId(e.session.id),f={...r,...u},p=d?withToolSessionRetention(f):f,m=resolveVercelSandboxTags(p.tags,{sessionId:e.session.id}),h=await t(),g={createOptions:p,createSandbox:i,resolveSessionCreateOptions:l===void 0?void 0:async()=>({mounts:l}),sandboxModule:h,sessionKey:s,snapshotId:c.snapshotId,tags:m},_;try{if(_=await ensureSession(g),_=await ensureUsableSession({expired:d?isVercelSnapshotNotFoundError:isVercelSnapshotUnavailableError,input:g,loadDeleteSandboxModule:n,session:_}),_.created&&c.snapshotId===void 0&&u.networkPolicy!==void 0)try{await applyInitialVercelNetworkPolicy(_.sandbox,u.networkPolicy)}catch(t){try{await deleteVercelSandbox({createOptions:p,loadDeleteSandboxModule:n,sandbox:_.sandbox})}catch(n){throw AggregateError([t,n],`Failed to apply network policy and discard Vercel sandbox session "${e.session.id}".`,{cause:t})}throw t}}catch(t){throw isVercelSnapshotUnavailableError(t)&&c.snapshotId!==void 0?new SandboxTemplateNotProvisionedError({providerName:`vercel`,templateKey:c.snapshotId}):Error(`Failed to open Vercel sandbox session "${e.session.id}": ${errorMessage(t)}`,{cause:t})}return createHandle({createOptions:p,loadDeleteSandboxModule:n,sandbox:_.sandbox})}return withToolSessionSandboxes({async prepare(n){let a=providerResourceTargetFiles(n.resources);if(e.skipEmptyPreparation===!0&&e.prepare===void 0&&a.length===0)return{};let o=`eve-sbx-tpl-vercel-${createSandboxProviderIdentity({createOptions:vercelIdentityOptions(r),resolvedImage:r.source===void 0?r.image??VERCEL_EVE_SANDBOX_IMAGE:void 0,resources:sandboxProviderResourceIdentity(n.resources),sourceRevision:n.sourceRevision,version:2}).slice(0,32)}`;try{return{snapshotId:(await ensureTemplateWithUnavailableRetry({createOptions:r,createSandbox:i,loadSandboxModule:t,log:n.log,prepareSandbox:e.prepare,seedFiles:a,templateKey:o})).template.snapshotId}}catch(e){throw Error(`Failed to prepare Vercel sandbox snapshot: ${errorMessage(e)}`,{cause:e})}},async resume(e,i,a){requirePreparedVercelTemplate(i);let o=requireVercelSessionState(a),s=await t(),c=await getNamedVercelSandbox({createOptions:r,sandboxModule:s,sandboxName:o.sandboxName});if(c===null)throw Error(`Vercel sandbox session "${o.sandboxName}" no longer exists.`);return await ensureVercelSandboxBaseRuntime(c),createHandle({createOptions:r,loadDeleteSandboxModule:n,sandbox:c})},async start(e,t,n){let i=vercelSessionName(e.session.id,t,n,r);return{handle:await openSession(e,t,n,i),state:{sandboxName:i,version:3}}}})}function vercelSessionName(e,t,n,r){let i=requirePreparedVercelTemplate(n);return`eve-sbx-vercel-${createSandboxProviderIdentity({artifact:i,createOptions:vercelIdentityOptions(r),options:vercelIdentityOptions(t),sessionId:e,version:1}).slice(0,32)}`}function vercelIdentityOptions(e){if(e===void 0)return;let t=new Set([`fetch`,`projectId`,`signal`,`teamId`,`token`]);return Object.fromEntries(Object.entries(e).filter(([e])=>!t.has(e)))}function requireVercelSessionState(e){if(!isSandboxPreparedArtifactRecord(e)||e.version!==3||typeof e.sandboxName!=`string`)throw Error(`Invalid Vercel sandbox session state.`);return{sandboxName:e.sandboxName,version:3}}function requirePreparedVercelTemplate(e){if(!isSandboxPreparedArtifactRecord(e)||e.snapshotId!==void 0&&typeof e.snapshotId!=`string`)throw Error(`Invalid prepared Vercel sandbox artifact.`);return e.snapshotId===void 0?{}:{snapshotId:e.snapshotId}}async function ensureTemplateWithUnavailableRetry(e){try{return await ensureTemplate(e)}catch(t){if(!isVercelSnapshotUnavailableError(t)&&!isVercelSandboxMissingError(t))throw t;return e.log?.(`cached template disappeared; rebuilding sandbox template`),await ensureTemplate(e)}}async function ensureTemplate(e){let t=await e.loadSandboxModule(),n=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:t,sandboxName:e.templateKey}),r=resolveVercelSandboxTags(e.createOptions.tags,e.tags),i=extractAuthorSnapshotId(e.createOptions);n!==null&&isUnprovisionedTerminalTemplateSandbox(n,i)&&(await n.delete(),n=null),n===null?n=await e.createSandbox({sandboxModule:t,createOptions:withBaseSetupNetworkPolicy({...e.createOptions,name:e.templateKey,persistent:!0,tags:r})}):await ensureVercelSandboxTags(n,r);let a=typeof n.currentSnapshotId==`string`&&n.currentSnapshotId.length>0&&n.currentSnapshotId!==i?n.currentSnapshotId:null;if(a!==null)return{template:{sandboxName:n.name,snapshotId:a,templateKey:e.templateKey}};e.log?.(`preparing base runtime inside sandbox`),await ensureVercelSandboxBaseRuntime(n),await applyInitialVercelNetworkPolicy(n,e.createOptions.networkPolicy);let o=buildSandboxSession(createVercelInternalSandboxSession(n,e.createOptions.env),createVercelNetworkPolicySetter(n));await writeVercelSandboxSeedFiles({sandbox:n,seedFiles:e.seedFiles,session:o}),e.log?.(`running sandbox preparation`),await e.prepareSandbox?.(createLoggingSandboxSession({log:e.log,session:o}));let s=await n.snapshot();return{template:{sandboxName:n.name,snapshotId:s.snapshotId,templateKey:e.templateKey}}}async function ensureUsableSession(e){try{return await ensureVercelSandboxBaseRuntime(e.session.sandbox),e.session}catch(t){if(e.session.created||!e.expired(t))throw t}try{await deleteUnusableVercelSandbox({createOptions:e.input.createOptions,loadDeleteSandboxModule:e.loadDeleteSandboxModule,sandbox:e.session.sandbox})}catch(e){if(!isVercelSandboxMissingError(e))throw e}let t=await ensureSession(e.input);return await ensureVercelSandboxBaseRuntime(t.sandbox),t}async function ensureSession(e){let t=e.sessionKey,n=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:e.sandboxModule,sandboxName:t});if(n!==null)return await ensureVercelSandboxTags(n,e.tags),{created:!1,sandbox:n};let r=createSessionCreateParams(e,t,await e.resolveSessionCreateOptions?.());e.tags!==void 0&&(r.tags=e.tags);try{return{created:!0,sandbox:await e.createSandbox({createOptions:r,sandboxModule:e.sandboxModule})}}catch(n){if(!isVercelSandboxNameConflictError(n))throw n;let r=await getNamedVercelSandbox({...e,sandboxName:t});if(r===null)throw n;return{created:!1,sandbox:r}}}function createSessionCreateParams(e,t,n={}){let r={...e.createOptions,...n};if(e.snapshotId===void 0)return withBaseSetupNetworkPolicy({...r,name:t,persistent:!0});let{image:i,runtime:a,source:o,...s}=r;return{...s,name:t,persistent:!0,source:{snapshotId:e.snapshotId,type:`snapshot`}}}function createHandle(e){let{sandbox:t}=e;return{sandbox:{...buildSandboxSession(createVercelInternalSandboxSession(t,e.createOptions.env),createVercelNetworkPolicySetter(t)),name:t.name},async onSandboxDelete(n){await deleteVercelSandbox({createOptions:e.createOptions,loadDeleteSandboxModule:e.loadDeleteSandboxModule,sandbox:t,signal:n?.abortSignal})},async onSandboxStop(){await stopVercelSandbox(t)},async onRuntimeShutdown(){try{await stopVercelSandbox(t)}catch{}}}}function createVercelInternalSandboxSession(e,t){let n={...SANDBOX_AGENT_ENV,...t};return{resolvePath:resolveVercelSandboxPath,async spawn(t){let r=await e.runCommand({args:[`-lc`,t.command],cmd:`bash`,cwd:t.workingDirectory??WORKSPACE_ROOT,detached:!0,env:{...n,...t.env},signal:t.abortSignal});return adaptMultiplexedCommandToSandboxProcess({command:r,getOutput:e=>e.stream})},async readFile(t){return normalizeVercelReadStream(await e.readFile({path:t.path}))},async writeFile(t){let n=await streamToBuffer(t.content),r=await resolveVercelWritePath(e,t.path,t.abortSignal);await e.writeFiles([{content:n,path:r}],{signal:t.abortSignal})},async removePath(t){await e.fs.rm(t.path,{force:t.force,recursive:t.recursive,signal:t.abortSignal})}}}async function writeVercelSandboxSeedFiles(e){if(e.seedFiles.length===0)return;let t=await Promise.all(e.seedFiles.map(async t=>({content:typeof t.content==`string`?Buffer.from(t.content):t.content,path:await resolveSandboxModelPath({path:t.path,sandbox:e.session})})));await e.sandbox.writeFiles(t)}function resolveVercelSandboxPath(e){return e.startsWith(`/`)?e:`${WORKSPACE_ROOT}/${e}`}async function resolveVercelWritePath(e,t,n){let r=await e.runCommand({args:[`-m`,`--`,t],cmd:`realpath`,signal:n}),i=(await r.stdout()).trim();if(r.exitCode!==0||!i.startsWith(`/`)||i.includes(`
2
- `))throw Error(`Failed to resolve Vercel Sandbox write path: ${t}`);return i}function isUnprovisionedTerminalTemplateSandbox(e,t){let n=e.currentSnapshotId;return typeof n==`string`&&n.length>0&&n!==t?!1:e.status===`aborted`||e.status===`failed`||e.status===`stopped`}function extractAuthorSnapshotId(e){let t=e.source;if(t?.type===`snapshot`&&typeof t.snapshotId==`string`)return t.snapshotId}const DEFAULT_SANDBOX_TIMEOUT_MS=18e5;export{createVercelSandbox,createVercelSandboxProvider};
1
+ import{randomBytes}from"node:crypto";import{isToolSessionId}from"#execution/tool-session/id.js";import{withToolSessionSandboxes}from"#execution/tool-session/sandbox.js";import{streamToBuffer}from"#execution/sandbox/stream-utils.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{SANDBOX_AGENT_ENV,buildSandboxSession}from"#execution/sandbox/session.js";import{VERCEL_EVE_SANDBOX_IMAGE}from"#execution/sandbox/bindings/eve-image.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{createSandboxProviderIdentity}from"#execution/sandbox/provider-identity.js";import{isSandboxPreparedArtifactRecord,providerResourceTargetFiles,sandboxProviderResourceIdentity}from"#shared/sandbox-provider.js";import{SandboxTemplateNotProvisionedError}from"#shared/sandbox-template-error.js";import{adaptMultiplexedCommandToSandboxProcess}from"#execution/sandbox/multiplexed-command.js";import{resolveSandboxModelPath}from"#shared/skill-paths.js";import{isVercelSandboxMissingError,isVercelSandboxNameConflictError,isVercelSnapshotNotFoundError,isVercelSnapshotUnavailableError}from"#execution/sandbox/bindings/vercel-errors.js";import{ensureVercelSandboxTags,errorMessage,resolveVercelSandboxTags}from"#execution/sandbox/bindings/vercel-options.js";import{applyInitialVercelNetworkPolicy,createVercelNetworkPolicySetter,ensureVercelSandboxBaseRuntime,withBaseSetupNetworkPolicy}from"#execution/sandbox/bindings/vercel-base-runtime.js";import{createVercelEveImageSandbox}from"#execution/sandbox/bindings/vercel-create-sdk.js";import{withToolSessionRetention}from"#execution/sandbox/bindings/vercel-tool-sessions.js";import{getNamedVercelSandbox,isVercelSnapshotCreated}from"#execution/sandbox/bindings/vercel-lookup.js";import{deleteUnusableVercelSandbox,deleteVercelSandbox,stopVercelSandbox}from"#execution/sandbox/bindings/vercel-lifecycle.js";import{normalizeVercelReadStream}from"#execution/sandbox/bindings/vercel-read-stream.js";function createVercelSandboxProvider(e){let{prepare:t,...n}=e??{};return createVercelSandbox({createOptions:n,prepare:t})}function createVercelSandbox(e={}){let t=e.loadSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),n=e.loadDeleteSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),r={...e.createOptions,timeout:e.createOptions?.timeout??DEFAULT_SANDBOX_TIMEOUT_MS},i=e.createSandbox??createVercelEveImageSandbox;async function openSession(e,a,o,s){let c=requirePreparedVercelTemplate(o),{mounts:l,...u}=a??{},d=isToolSessionId(e.session.id),f={...r,...u},p=d?withToolSessionRetention(f):f,m=resolveVercelSandboxTags(p.tags,{sessionId:e.session.id}),h=await t(),g={createOptions:p,createSandbox:i,resolveSessionCreateOptions:l===void 0?void 0:async()=>({mounts:l}),sandboxModule:h,sessionKey:s,snapshotId:c.snapshotId,tags:m},_;try{if(_=await ensureSession(g),_=await ensureUsableSession({expired:d?isVercelSnapshotNotFoundError:isVercelSnapshotUnavailableError,input:g,loadDeleteSandboxModule:n,session:_}),_.created&&c.snapshotId===void 0&&u.networkPolicy!==void 0)try{await applyInitialVercelNetworkPolicy(_.sandbox,u.networkPolicy)}catch(t){try{await deleteVercelSandbox({createOptions:p,loadDeleteSandboxModule:n,sandbox:_.sandbox})}catch(n){throw AggregateError([t,n],`Failed to apply network policy and discard Vercel sandbox session "${e.session.id}".`,{cause:t})}throw t}}catch(t){throw isVercelSnapshotUnavailableError(t)&&c.snapshotId!==void 0?new SandboxTemplateNotProvisionedError({providerName:`vercel`,templateKey:c.snapshotId}):Error(`Failed to open Vercel sandbox session "${e.session.id}": ${errorMessage(t)}`,{cause:t})}return createHandle({createOptions:p,loadDeleteSandboxModule:n,sandbox:_.sandbox})}return withToolSessionSandboxes({async prepare(n){let a=providerResourceTargetFiles(n.resources);if(e.skipEmptyPreparation===!0&&e.prepare===void 0&&a.length===0)return{};let o=`eve-sbx-tpl-vercel-${createSandboxProviderIdentity({createOptions:vercelIdentityOptions(r),resolvedImage:r.source===void 0?r.image??VERCEL_EVE_SANDBOX_IMAGE:void 0,resources:sandboxProviderResourceIdentity(n.resources),sourceRevision:n.sourceRevision,version:3}).slice(0,32)}`;try{return{snapshotId:await ensureTemplate({createOptions:r,createSandbox:i,loadSandboxModule:t,log:n.log,prepareSandbox:e.prepare,seedFiles:a,templateKey:o})}}catch(e){throw Error(`Failed to prepare Vercel sandbox snapshot: ${errorMessage(e)}`,{cause:e})}},async resume(e,i,a){requirePreparedVercelTemplate(i);let o=requireVercelSessionState(a),s=await t(),c=await getNamedVercelSandbox({createOptions:r,sandboxModule:s,sandboxName:o.sandboxName});if(c===null)throw Error(`Vercel sandbox session "${o.sandboxName}" no longer exists.`);return await ensureVercelSandboxBaseRuntime(c),createHandle({createOptions:r,loadDeleteSandboxModule:n,sandbox:c})},async start(e,t,n){let i=vercelSessionName(e.session.id,t,n,r);return{handle:await openSession(e,t,n,i),state:{sandboxName:i,version:3}}}})}function vercelSessionName(e,t,n,r){let i=requirePreparedVercelTemplate(n);return`eve-sbx-vercel-${createSandboxProviderIdentity({artifact:i,createOptions:vercelIdentityOptions(r),options:vercelIdentityOptions(t),sessionId:e,version:1}).slice(0,32)}`}function vercelIdentityOptions(e){if(e===void 0)return;let t=new Set([`fetch`,`projectId`,`signal`,`teamId`,`token`]);return Object.fromEntries(Object.entries(e).filter(([e])=>!t.has(e)))}function requireVercelSessionState(e){if(!isSandboxPreparedArtifactRecord(e)||e.version!==3||typeof e.sandboxName!=`string`)throw Error(`Invalid Vercel sandbox session state.`);return{sandboxName:e.sandboxName,version:3}}function requirePreparedVercelTemplate(e){if(!isSandboxPreparedArtifactRecord(e)||e.snapshotId!==void 0&&typeof e.snapshotId!=`string`)throw Error(`Invalid prepared Vercel sandbox artifact.`);return e.snapshotId===void 0?{}:{snapshotId:e.snapshotId}}async function ensureTemplate(e){let t=await e.loadSandboxModule(),n=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:t,sandboxName:e.templateKey}),r=n?.sourceSnapshotId;if(r!==void 0&&await isVercelSnapshotCreated({createOptions:e.createOptions,sandboxModule:t,snapshotId:r}))return e.log?.(`reusing prepared sandbox template`),r;let i=await prepareTemplateSnapshot(e,t);return await publishTemplatePointer(e,t,n,i).catch(t=>{e.log?.(`could not publish sandbox template: ${errorMessage(t)}`)}),i}async function prepareTemplateSnapshot(e,t){let n=await e.createSandbox({sandboxModule:t,createOptions:withBaseSetupNetworkPolicy({...e.createOptions,name:`${e.templateKey}-${randomBytes(4).toString(`hex`)}`,persistent:!1})});try{e.log?.(`preparing base runtime inside sandbox`),await ensureVercelSandboxBaseRuntime(n),await applyInitialVercelNetworkPolicy(n,e.createOptions.networkPolicy);let t=buildSandboxSession(createVercelInternalSandboxSession(n,e.createOptions.env),createVercelNetworkPolicySetter(n));return await writeVercelSandboxSeedFiles({sandbox:n,seedFiles:e.seedFiles,session:t}),e.log?.(`running sandbox preparation`),await e.prepareSandbox?.(createLoggingSandboxSession({log:e.log,session:t})),(await n.snapshot()).snapshotId}finally{await n.delete().catch(t=>{e.log?.(`could not delete sandbox ${n.name}: ${errorMessage(t)}`)})}}async function publishTemplatePointer(e,t,n,r){if(n!==null)try{await n.delete()}catch(e){if(!isVercelSandboxMissingError(e))throw e}let{image:i,runtime:a,source:o,...s}=e.createOptions,c;try{c=await e.createSandbox({sandboxModule:t,createOptions:{...s,name:e.templateKey,persistent:!1,source:{snapshotId:r,type:`snapshot`}}})}catch(e){if(isVercelSandboxNameConflictError(e))return;throw e}await stopVercelSandbox(c)}async function ensureUsableSession(e){try{return await ensureVercelSandboxBaseRuntime(e.session.sandbox),e.session}catch(t){if(e.session.created||!e.expired(t))throw t}try{await deleteUnusableVercelSandbox({createOptions:e.input.createOptions,loadDeleteSandboxModule:e.loadDeleteSandboxModule,sandbox:e.session.sandbox})}catch(e){if(!isVercelSandboxMissingError(e))throw e}let t=await ensureSession(e.input);return await ensureVercelSandboxBaseRuntime(t.sandbox),t}async function ensureSession(e){let t=e.sessionKey,n=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:e.sandboxModule,sandboxName:t});if(n!==null)return await ensureVercelSandboxTags(n,e.tags),{created:!1,sandbox:n};let r=createSessionCreateParams(e,t,await e.resolveSessionCreateOptions?.());e.tags!==void 0&&(r.tags=e.tags);try{return{created:!0,sandbox:await e.createSandbox({createOptions:r,sandboxModule:e.sandboxModule})}}catch(n){if(!isVercelSandboxNameConflictError(n))throw n;let r=await getNamedVercelSandbox({...e,sandboxName:t});if(r===null)throw n;return{created:!1,sandbox:r}}}function createSessionCreateParams(e,t,n={}){let r={...e.createOptions,...n};if(e.snapshotId===void 0)return withBaseSetupNetworkPolicy({...r,name:t,persistent:!0});let{image:i,runtime:a,source:o,...s}=r;return{...s,name:t,persistent:!0,source:{snapshotId:e.snapshotId,type:`snapshot`}}}function createHandle(e){let{sandbox:t}=e;return{sandbox:{...buildSandboxSession(createVercelInternalSandboxSession(t,e.createOptions.env),createVercelNetworkPolicySetter(t)),name:t.name},async onSandboxDelete(n){await deleteVercelSandbox({createOptions:e.createOptions,loadDeleteSandboxModule:e.loadDeleteSandboxModule,sandbox:t,signal:n?.abortSignal})},async onSandboxStop(){await stopVercelSandbox(t)},async onRuntimeShutdown(){try{await stopVercelSandbox(t)}catch{}}}}function createVercelInternalSandboxSession(e,t){let n={...SANDBOX_AGENT_ENV,...t};return{resolvePath:resolveVercelSandboxPath,async spawn(t){let r=await e.runCommand({args:[`-lc`,t.command],cmd:`bash`,cwd:t.workingDirectory??WORKSPACE_ROOT,detached:!0,env:{...n,...t.env},signal:t.abortSignal});return adaptMultiplexedCommandToSandboxProcess({command:r,getOutput:e=>e.stream})},async readFile(t){return normalizeVercelReadStream(await e.readFile({path:t.path}))},async writeFile(t){let n=await streamToBuffer(t.content),r=await resolveVercelWritePath(e,t.path,t.abortSignal);await e.writeFiles([{content:n,path:r}],{signal:t.abortSignal})},async removePath(t){await e.fs.rm(t.path,{force:t.force,recursive:t.recursive,signal:t.abortSignal})}}}async function writeVercelSandboxSeedFiles(e){if(e.seedFiles.length===0)return;let t=await Promise.all(e.seedFiles.map(async t=>({content:typeof t.content==`string`?Buffer.from(t.content):t.content,path:await resolveSandboxModelPath({path:t.path,sandbox:e.session})})));await e.sandbox.writeFiles(t)}function resolveVercelSandboxPath(e){return e.startsWith(`/`)?e:`${WORKSPACE_ROOT}/${e}`}async function resolveVercelWritePath(e,t,n){let r=await e.runCommand({args:[`-m`,`--`,t],cmd:`realpath`,signal:n}),i=(await r.stdout()).trim();if(r.exitCode!==0||!i.startsWith(`/`)||i.includes(`
2
+ `))throw Error(`Failed to resolve Vercel Sandbox write path: ${t}`);return i}const DEFAULT_SANDBOX_TIMEOUT_MS=18e5;export{createVercelSandbox,createVercelSandboxProvider};
@@ -2,8 +2,12 @@ import type { TokenUsage } from "#shared/token-usage.js";
2
2
  import type { DeliverHookPayload, RunInput, SessionCommand } from "#channel/types.js";
3
3
  import type { SessionCheckpoint } from "#execution/session/handoff.js";
4
4
  import type { AgentWorkflowRetentionDefinition } from "#shared/agent-definition.js";
5
- /** Version 2 marks source runs whose Workflow SDK supports forced hook claims. */
6
- export declare const SESSION_HANDOFF_VERSION = 2;
5
+ /**
6
+ * Version 3 sources keep their hooks while the successor starts: it takes
7
+ * them over with forced claims and the source hands over what it accepted
8
+ * meanwhile. Earlier sources released their hooks before starting it.
9
+ */
10
+ export declare const SESSION_HANDOFF_VERSION = 3;
7
11
  /**
8
12
  * Serializable workflow-entry input. All runtime state travels via
9
13
  * `serializedContext`, which is produced by `serializeContext(ctx)`
@@ -34,8 +38,9 @@ export type SessionHandoffStart = {
34
38
  */
35
39
  readonly reason: "compaction";
36
40
  /**
37
- * The lone delivery that arrived before the owner could move, processed
38
- * first. Without one, the successor parks until the next input arrives.
41
+ * Only sources older than version 3 send one: the lone delivery that
42
+ * arrived before they could move, processed first. Without one, the
43
+ * successor parks until the next input arrives.
39
44
  */
40
45
  readonly delivery?: DeliverHookPayload;
41
46
  /** Compaction does not extend the session's lifetime. */
@@ -44,9 +49,18 @@ export type SessionHandoffStart = {
44
49
  /** A successor owner started by a previous owner during a handoff. */
45
50
  export type HandoffWorkflowEntryInput = SessionHandoffStart & {
46
51
  readonly activationToken: string;
52
+ /**
53
+ * Version 3: names this attempt, so a duplicate candidate fences itself out
54
+ * and the source's handover reaches the successor that expects it.
55
+ */
56
+ readonly attemptKey?: string;
47
57
  readonly checkpoint: SessionCheckpoint;
48
- /** Omitted by version 1 sources, whose hooks cannot safely be force-claimed. */
58
+ /** Omitted by version 1 sources. */
49
59
  readonly handoffVersion?: number;
60
+ /** Version 3: the source's logical hook set, which the successor takes over. */
61
+ readonly hookTokens?: readonly string[];
62
+ /** Version 3: the source run, whose handover the successor waits for while it lives. */
63
+ readonly sourceRunId?: string;
50
64
  readonly kind: "handoff";
51
65
  readonly ownerDeploymentId: string;
52
66
  readonly sessionWritable: WritableStream<Uint8Array>;
@@ -1 +1 @@
1
- const SESSION_HANDOFF_VERSION=2;export{SESSION_HANDOFF_VERSION};
1
+ const SESSION_HANDOFF_VERSION=3;export{SESSION_HANDOFF_VERSION};
@@ -1 +1 @@
1
- import{attachClientContext,readClientContext}from"#internal/client-context.js";import{SESSION_INBOX_CONTEXT_KEY,sessionCommandHookToken}from"#execution/session-inbox/address.js";import{readSerializedSessionSchedule}from"#context/session-schedule.js";import{getWorkflowMetadata,getWritable}from"#compiled/@workflow/core/index.js";import{isHookConflictError}from"#execution/hook-ownership.js";import{DEFAULT_SESSION_TIMEOUT_MS,sessionTimeoutDeadline}from"#execution/session/timeout.js";import{sessionHookTokens}from"#execution/session/hook-tokens.js";import{createSessionInbox}from"#execution/session-inbox/inbox.js";import{failSession,runPreparedSession}from"#execution/session/program.js";import{createSessionTimeoutControl}from"#execution/session/timeout-control.js";import{recordSessionOwnerStep,signalSessionOwnerActivationStep,stopUntrackedChildSessionsStep,validateSessionCheckpointStep}from"#execution/session/handoff-steps.js";import{hasDelegatedSessionContext}from"#execution/delegated-session-context.js";import{readChannelRequestId,readRootSessionId}from"#execution/eve-workflow-attributes.js";import{createTurnControl}from"#execution/session/turn-control.js";import{resolveInitialTurnCallerStep}from"#subagents/parent-notification.js";import{normalizeSerializableError}from"#execution/workflow-errors.js";import{createSessionStep}from"#execution/create-session-step.js";import{settleContinuationConflictStep}from"#execution/continuation-conflict-step.js";import{migrateSessionCheckpoint}from"#execution/session/checkpoint-migrations.js";async function workflowEntry(e){"use workflow";let t=e.kind===`initial`?await bootInitialOwner(e,getWorkflowMetadata().workflowRunId):await bootHandoffOwner(e);return t===void 0?{output:``}:{output:(await runPreparedSession(t.session,t.inbox)).output}}function stampSessionIdentity(e,t){return{...e,"eve.sessionId":t,[SESSION_INBOX_CONTEXT_KEY]:{sessionId:t}}}async function prepareInitialOwner(e,t){let[n,r]=await Promise.allSettled([e.claimSessionHook(sessionCommandHookToken(t.sessionId)),t.continuationToken===``?Promise.resolve():e.claimSessionHook(t.continuationToken)]);if(n.status===`rejected`)throw n.reason;if(r.status===`rejected`){if(isHookConflictError(r.reason))return{kind:`alias-conflict`};throw r.reason}let i=createTurnControl(),a=t.deadline===void 0?void 0:createSessionTimeoutControl({deadline:t.deadline,sessionId:t.sessionId}),o={kind:`owned`,timeoutControl:a,turnControl:i,async dispose(){i.dispose(),await a?.dispose()}};return a?.start().catch(()=>{}),o}function unwrapSettled(e){if(e.status===`rejected`)throw e.reason;return e.value}async function disposeInitialBoot(e,t){let[n,r]=await Promise.allSettled([t?.dispose(),e.dispose()]);if(n.status===`rejected`)throw n.reason;if(r.status===`rejected`)throw r.reason}async function bootInitialOwner(e,t){let n=stampSessionIdentity(e.serializedContext,t),r=getWritable(),i=createSessionInbox(t),a=e.sessionTimeoutMs??DEFAULT_SESSION_TIMEOUT_MS,o=sessionTimeoutDeadline(a,Date.now()),s=n[`eve.continuationToken`]||``,c=n[`eve.bundle`],l;try{let u=createSessionStep({compiledArtifactsSource:c.source,continuationToken:s,dynamicSubagentAgentConfig:n[`eve.dynamicSubagentAgentConfig`],inheritedLimits:e.limits,nodeId:c.nodeId,outputSchema:e.input.outputSchema,rootSessionId:readRootSessionId(n),sessionId:t}),d=prepareInitialOwner(i,{continuationToken:s,deadline:o,sessionId:t}),f=hasDelegatedSessionContext(n)?resolveInitialTurnCallerStep({serializedContext:n}):Promise.resolve(void 0),[p,m,h]=await Promise.allSettled([u,d,f]);m.status===`fulfilled`&&m.value.kind===`owned`&&(l=m.value);let g=unwrapSettled(p),_=unwrapSettled(m);if(_.kind===`alias-conflict`){e.continuationConflictCommand!==void 0&&await settleContinuationConflictStep({command:e.continuationConflictCommand,continuationToken:s}),await disposeInitialBoot(i,l);return}return{inbox:i,session:{anchor:{kind:`self`},caller:unwrapSettled(h),capabilities:n[`eve.capabilities`],deploymentId:e.ownerDeploymentId,history:g.history,start:e.input.message===void 0?{kind:`first-message`}:{input:createInitialDelivery(e,n),kind:`turn`},initialTurnControl:_.turnControl,retention:e.retention,serializedContext:n,sessionId:t,sessionState:g.state,sessionTimeoutControl:_.timeoutControl,sessionTimeoutMs:a,sessionTimeoutDeadline:o,sessionWritable:r}}}catch(e){return await disposeInitialBoot(i,l),await failSession({error:e,serializedContext:n,sessionId:t,sessionState:void 0,sessionWritable:r})}}async function bootHandoffOwner(e){let{sessionId:t}=e,n=createSessionInbox(t),r,i,a;try{let o=migrateSessionCheckpoint(e.checkpoint),s=await validateSessionCheckpointStep({migration:o,sessionId:t});if(o.kind===`incompatible`||s.kind===`incompatible`){let t=await n.release();await signalSessionOwnerActivationStep({activation:{kind:`incompatible`,payloads:t,reason:`checkpoint-version`},token:e.activationToken});return}({checkpoint:r,childRunIdsToStop:i}=o),a=stampSessionIdentity(r.serializedContext,t),await recordSessionOwnerStep({sessionId:t}),await n.claimSessionHooks(sessionHookTokens({serializedContext:a,sessionState:r.sessionState})),await signalSessionOwnerActivationStep({activation:{kind:`active`},token:e.activationToken})}catch(t){let r=await n.release();await signalSessionOwnerActivationStep({activation:{error:normalizeSerializableError(t),kind:`failed`,payloads:r},token:e.activationToken});return}i.length>0&&await stopUntrackedChildSessionsStep({runIds:i,sessionId:t});let o=e.reason===`compaction`?e.sessionTimeoutDeadline:sessionTimeoutDeadline(r.sessionTimeoutMs,Date.now()),s=o===void 0?void 0:createSessionTimeoutControl({deadline:o,sessionId:t});return{inbox:n,session:{anchor:{kind:`successor`},caller:e.delivery?.caller,capabilities:r.capabilities,deploymentId:e.ownerDeploymentId,history:r.history,start:e.delivery===void 0?{kind:`parked`}:{input:e.delivery,kind:`turn`},retention:r.retention,serializedContext:a,sessionId:t,sessionState:r.sessionState,sessionTimeoutControl:s,sessionTimeoutMs:r.sessionTimeoutMs,sessionTimeoutDeadline:o,sessionWritable:e.sessionWritable}}}function createInitialDelivery(e,t){if(e.input.message!==void 0)return{schedule:readSerializedSessionSchedule(t),deliveryMetadata:t[`eve.channelDelivery`]===void 0?void 0:[{...t[`eve.channelDelivery`],payloadIndex:0}],kind:`deliver`,payloads:[attachClientContext({message:e.input.message,context:e.input.context,outputSchema:e.input.outputSchema,state:e.input.state},readClientContext(e.input))],requestId:readChannelRequestId(t)}}workflowEntry.workflowId=`workflow//eve//workflowEntry`;export{bootInitialOwner,workflowEntry};
1
+ import{attachClientContext,readClientContext}from"#internal/client-context.js";import{SESSION_INBOX_CONTEXT_KEY,sessionCommandHookToken}from"#execution/session-inbox/address.js";import{readSerializedSessionSchedule}from"#context/session-schedule.js";import{createHook,getWorkflowMetadata,getWritable}from"#compiled/@workflow/core/index.js";import{claimHookOwnership,isHookConflictError}from"#execution/hook-ownership.js";import{DEFAULT_SESSION_TIMEOUT_MS,sessionTimeoutDeadline}from"#execution/session/timeout.js";import{sessionHookTokens}from"#execution/session/hook-tokens.js";import{createSessionInbox}from"#execution/session-inbox/inbox.js";import{failSession,runPreparedSession}from"#execution/session/program.js";import{createSessionTimeoutControl}from"#execution/session/timeout-control.js";import{prepareSuccessorStep,signalSessionOwnerActivationStep,stopUntrackedChildSessionsStep}from"#execution/session/handoff-steps.js";import{hasDelegatedSessionContext}from"#execution/delegated-session-context.js";import{readChannelRequestId,readRootSessionId}from"#execution/eve-workflow-attributes.js";import{createTurnControl}from"#execution/session/turn-control.js";import{HANDOFF_LEASE_RETENTION,handoffLeaseToken,sendSessionHandover}from"#execution/session/handoff.js";import{resolveInitialTurnCallerStep}from"#subagents/parent-notification.js";import{normalizeSerializableError}from"#execution/workflow-errors.js";import{createSessionStep}from"#execution/create-session-step.js";import{settleContinuationConflictStep}from"#execution/continuation-conflict-step.js";import{migrateSessionCheckpoint}from"#execution/session/checkpoint-migrations.js";import{abandonHandoverIfRunEnds,watchRunEnd}from"#execution/session/run-watch.js";async function workflowEntry(e){"use workflow";let t=e.kind===`initial`?await bootInitialOwner(e,getWorkflowMetadata().workflowRunId):(e.handoffVersion??0)>=3?await bootForcedHandoffOwner(e):await bootReleasedHandoffOwner(e);return t===void 0?{output:``}:{output:(await runPreparedSession(t.session,t.inbox)).output}}function stampSessionIdentity(e,t){return{...e,"eve.sessionId":t,[SESSION_INBOX_CONTEXT_KEY]:{sessionId:t}}}async function prepareInitialOwner(e,t){let[n,r]=await Promise.allSettled([e.claimSessionHook(sessionCommandHookToken(t.sessionId)),t.continuationToken===``?Promise.resolve():e.claimSessionHook(t.continuationToken)]);if(n.status===`rejected`)throw n.reason;if(r.status===`rejected`){if(isHookConflictError(r.reason))return{kind:`alias-conflict`};throw r.reason}let i=createTurnControl(),a=t.deadline===void 0?void 0:createSessionTimeoutControl({deadline:t.deadline,sessionId:t.sessionId}),o={kind:`owned`,timeoutControl:a,turnControl:i,async dispose(){i.dispose(),await a?.dispose()}};return a?.start().catch(()=>{}),o}function unwrapSettled(e){if(e.status===`rejected`)throw e.reason;return e.value}async function disposeInitialBoot(e,t){let[n,r]=await Promise.allSettled([t?.dispose(),e.dispose()]);if(n.status===`rejected`)throw n.reason;if(r.status===`rejected`)throw r.reason}async function bootInitialOwner(e,t){let n=stampSessionIdentity(e.serializedContext,t),r=getWritable(),i=createSessionInbox(t),a=e.sessionTimeoutMs??DEFAULT_SESSION_TIMEOUT_MS,o=sessionTimeoutDeadline(a,Date.now()),s=n[`eve.continuationToken`]||``,c=n[`eve.bundle`],l;try{let u=createSessionStep({compiledArtifactsSource:c.source,continuationToken:s,dynamicSubagentAgentConfig:n[`eve.dynamicSubagentAgentConfig`],inheritedLimits:e.limits,nodeId:c.nodeId,outputSchema:e.input.outputSchema,rootSessionId:readRootSessionId(n),sessionId:t}),d=prepareInitialOwner(i,{continuationToken:s,deadline:o,sessionId:t}),f=hasDelegatedSessionContext(n)?resolveInitialTurnCallerStep({serializedContext:n}):Promise.resolve(void 0),[p,m,h]=await Promise.allSettled([u,d,f]);m.status===`fulfilled`&&m.value.kind===`owned`&&(l=m.value);let g=unwrapSettled(p),_=unwrapSettled(m);if(_.kind===`alias-conflict`){e.continuationConflictCommand!==void 0&&await settleContinuationConflictStep({command:e.continuationConflictCommand,continuationToken:s}),await disposeInitialBoot(i,l);return}return{inbox:i,session:{anchor:{kind:`self`},caller:unwrapSettled(h),capabilities:n[`eve.capabilities`],deploymentId:e.ownerDeploymentId,history:g.history,start:e.input.message===void 0?{kind:`first-message`}:{input:createInitialDelivery(e,n),kind:`turn`},initialTurnControl:_.turnControl,retention:e.retention,serializedContext:n,sessionId:t,sessionState:g.state,sessionTimeoutControl:_.timeoutControl,sessionTimeoutMs:a,sessionTimeoutDeadline:o,sessionWritable:r}}}catch(e){return await disposeInitialBoot(i,l),await failSession({error:e,serializedContext:n,sessionId:t,sessionState:void 0,sessionWritable:r})}}async function prepareSuccessor(e,t){let{sessionId:n}=e,r=migrateSessionCheckpoint(e.checkpoint),i=await prepareSuccessorStep({handoffVersion:e.handoffVersion,migration:r,sessionId:n,...t!==void 0&&{source:t}});if(r.kind===`incompatible`||i.kind===`incompatible`){await signalSessionOwnerActivationStep({activation:{kind:`incompatible`,payloads:[],reason:`checkpoint-version`,runId:getWorkflowMetadata().workflowRunId},token:e.activationToken});return}if(i.kind===`source-gone`){await signalFailedActivation(e,Error(`The session's previous owner no longer holds all of its addresses.`),{payloads:[]});return}let{checkpoint:a,childRunIdsToStop:o}=r;return{checkpoint:a,childRunIdsToStop:o,serializedContext:stampSessionIdentity(a.serializedContext,n)}}async function signalFailedActivation(e,t,n){await signalSessionOwnerActivationStep({activation:{error:normalizeSerializableError(t),kind:`failed`,runId:getWorkflowMetadata().workflowRunId,...n},token:e.activationToken})}async function bootForcedHandoffOwner(e){let{attemptKey:t,hookTokens:n,sessionId:r,sourceRunId:i}=e;if(t===void 0||n===void 0||i===void 0){await signalFailedActivation(e,Error(`Session handoff version 3 input is missing attemptKey, hookTokens, or sourceRunId.`),{payloads:[]});return}try{let e=createHook({experimental_minRetention:HANDOFF_LEASE_RETENTION,token:handoffLeaseToken(t)});await claimHookOwnership(e)}catch(t){if(isHookConflictError(t))return;await signalFailedActivation(e,t,{payloads:[]});return}let a;try{a=await prepareSuccessor(e,{hookTokens:n,runId:i})}catch(t){await signalFailedActivation(e,t,{payloads:[]});return}if(a===void 0)return;let o=createSessionInbox(r);try{await o.claimSessionHooks(n,{force:!0,handover:t})}catch(n){let a=o.claimedTokens.length>0;await signalFailedActivation(e,n,{claimedHooks:a,payloads:[]}),a&&await handBack(o,{attemptKey:t,sessionId:r,sourceRunId:i}),await o.dispose();return}return abandonHandoverIfRunEnds(o,{attemptKey:t,sourceRunId:i}),await startSuccessor(e,o,a)}async function handBack(e,t){let n=watchRunEnd(t.sourceRunId),r=new AbortController;try{if(await Promise.race([e.whenTakenOver(r.signal).then(()=>`taken-back`),n.ended.then(()=>`source-ended`)])===`source-ended`)return}finally{n.stop(),r.abort()}await sendSessionHandover({attemptKey:t.attemptKey,payloads:e.drain(),sessionId:t.sessionId})}async function bootReleasedHandoffOwner(e){let t=createSessionInbox(e.sessionId),n;try{if(n=await prepareSuccessor(e),n===void 0)return;await t.claimSessionHooks(sessionHookTokens({serializedContext:n.serializedContext,sessionState:n.checkpoint.sessionState})),await signalSessionOwnerActivationStep({activation:{kind:`active`},token:e.activationToken})}catch(n){await signalFailedActivation(e,n,{payloads:await t.release()});return}return await startSuccessor(e,t,n)}async function startSuccessor(e,t,n){let{sessionId:r}=e,{checkpoint:i,childRunIdsToStop:a,serializedContext:o}=n;a.length>0&&await stopUntrackedChildSessionsStep({runIds:a,sessionId:r});let s=e.reason===`compaction`?e.sessionTimeoutDeadline:sessionTimeoutDeadline(i.sessionTimeoutMs,Date.now()),c=s===void 0?void 0:createSessionTimeoutControl({deadline:s,sessionId:r});return{inbox:t,session:{anchor:{kind:`successor`},caller:e.delivery?.caller,capabilities:i.capabilities,deploymentId:e.ownerDeploymentId,history:i.history,start:e.delivery===void 0?{kind:`parked`}:{input:e.delivery,kind:`turn`},retention:i.retention,serializedContext:o,sessionId:r,sessionState:i.sessionState,sessionTimeoutControl:c,sessionTimeoutMs:i.sessionTimeoutMs,sessionTimeoutDeadline:s,sessionWritable:e.sessionWritable}}}function createInitialDelivery(e,t){if(e.input.message!==void 0)return{schedule:readSerializedSessionSchedule(t),deliveryMetadata:t[`eve.channelDelivery`]===void 0?void 0:[{...t[`eve.channelDelivery`],payloadIndex:0}],kind:`deliver`,payloads:[attachClientContext({message:e.input.message,context:e.input.context,outputSchema:e.input.outputSchema,state:e.input.state},readClientContext(e.input))],requestId:readChannelRequestId(t)}}workflowEntry.workflowId=`workflow//eve//workflowEntry`;export{bootInitialOwner,workflowEntry};
@@ -1,30 +1,68 @@
1
+ import type { SessionHandoverPayload } from "#execution/session-inbox/inbox.js";
1
2
  import type { SessionCheckpointMigration } from "#execution/session/checkpoint-migrations.js";
2
3
  import { type DurableSessionState } from "#execution/durable-session-store.js";
3
- import { type SessionOwnerActivation } from "#execution/session/handoff.js";
4
+ import { type SessionCheckpoint, type SessionOwnerActivation } from "#execution/session/handoff.js";
5
+ import { type SessionHandoffStart } from "#execution/session/entry-input.js";
4
6
  /** Parses retained work with this deployment's code before deciding whether it can move. */
5
7
  export declare function isSessionStateIdleForHandoff(input: {
6
8
  readonly sessionState: DurableSessionState;
7
9
  }): boolean;
8
- /** Reads durable work using the source deployment's handoff contract. */
9
- export declare function isSessionIdleForHandoffStep(input: {
10
- readonly sessionState: DurableSessionState;
10
+ export type SuccessorStartInput = SessionHandoffStart & {
11
+ readonly activationToken: string;
12
+ /** Original run whose stream stays the public session stream. */
13
+ readonly anchorRunId: string;
14
+ readonly attemptKey: string;
15
+ readonly checkpoint: SessionCheckpoint;
16
+ readonly hookTokens: readonly string[];
17
+ /** The owner starting the successor, which hands over what it accepts meanwhile. */
18
+ readonly sourceRunId: string;
19
+ readonly targetDeploymentId: string;
20
+ };
21
+ export type SuccessorStart = {
22
+ readonly kind: "not-idle";
23
+ } | {
24
+ readonly kind: "unsupported";
25
+ } | {
26
+ readonly kind: "started";
27
+ readonly runId: string;
28
+ };
29
+ /**
30
+ * Starts a successor owner bound to the original session stream, unless this
31
+ * deployment's code finds work in the checkpoint that cannot move yet, or the
32
+ * World cannot move session addresses between runs without releasing them.
33
+ */
34
+ export declare function startSuccessorStep(input: SuccessorStartInput): Promise<SuccessorStart>;
35
+ /** Whether `runId` has ended; a run not readable yet has not. */
36
+ export declare function hasRunEndedStep(input: {
37
+ readonly runId: string;
11
38
  }): Promise<boolean>;
12
39
  export type SessionCheckpointValidation = {
13
40
  readonly kind: "valid";
14
41
  } | {
15
42
  readonly kind: "incompatible";
16
43
  readonly reason: "checkpoint-version";
44
+ }
45
+ /** The source no longer holds every address it handed over, so none may be taken. */
46
+ | {
47
+ readonly kind: "source-gone";
17
48
  };
18
49
  /**
19
- * Validates an upgraded checkpoint and resolves the target deployment's
20
- * compiled bundle.
50
+ * Validates an upgraded checkpoint, resolves the target deployment's compiled
51
+ * bundle, and records this owner's identity before it claims any hook.
21
52
  *
22
53
  * A checkpoint this deployment cannot read is a settled answer, not a fault,
23
54
  * so it returns rather than throws: retrying the step can never change it.
24
55
  */
25
- export declare function validateSessionCheckpointStep(input: {
56
+ export declare function prepareSuccessorStep(input: {
57
+ /** Absent for version 1 sources, which predate the field. */
58
+ readonly handoffVersion: number | undefined;
26
59
  readonly migration: SessionCheckpointMigration;
27
60
  readonly sessionId: string;
61
+ /** Version 3: the run that still holds the session and the addresses it hands over. */
62
+ readonly source?: {
63
+ readonly runId: string;
64
+ readonly hookTokens: readonly string[];
65
+ };
28
66
  }): Promise<SessionCheckpointValidation>;
29
67
  /**
30
68
  * Stops idle subagent sessions an older owner kept resumable, after this owner
@@ -43,12 +81,12 @@ export declare function reportSessionHandoffRetainedStep(input: {
43
81
  readonly targetDeploymentId: string;
44
82
  }): Promise<void>;
45
83
  /**
46
- * Records the identity of an owner that this deployment's code did not start
47
- * (a handoff successor or a legacy importer) before it claims any hook:
48
- * ingress reads `$eve.version` to decide whether the owner can run, so a
49
- * missing value would strand a healthy session. The value comes from this
50
- * step's code, not the previous owner's. Unlike observability attributes, a
51
- * failed write fails the boot.
84
+ * Records the identity of a legacy importer before it claims any hook;
85
+ * handoff successors do the same inside `prepareSuccessorStep`. Ingress reads
86
+ * `$eve.version` to decide whether the owner can run, so a missing value would
87
+ * strand a healthy session. The value comes from this step's code, not the
88
+ * previous owner's. Unlike observability attributes, a failed write fails the
89
+ * boot.
52
90
  */
53
91
  export declare function recordSessionOwnerStep(input: {
54
92
  readonly sessionId: string;
@@ -57,6 +95,30 @@ export declare function signalSessionOwnerActivationStep(input: {
57
95
  readonly activation: SessionOwnerActivation;
58
96
  readonly token: string;
59
97
  }): Promise<void>;
98
+ /** Makes a takeover with no attempt open visible: every forced claim belongs to one. */
99
+ export declare function reportSessionTakenOverOutsideAttemptStep(input: {
100
+ readonly sessionId: string;
101
+ }): Promise<void>;
102
+ /**
103
+ * Makes a move whose handover part keeps failing visible; the owner keeps
104
+ * retrying, because dropping the part would lose an accepted message.
105
+ */
106
+ export declare function reportHandoverPartStuckStep(input: {
107
+ readonly attemptKey: string;
108
+ readonly error: string | undefined;
109
+ readonly failures: number;
110
+ readonly index: number;
111
+ readonly sessionId: string;
112
+ }): Promise<void>;
113
+ /**
114
+ * Sends one handover part through the stable session address, which reaches
115
+ * whichever run owns the session. A part that keeps failing fails the step,
116
+ * for the caller to retry until it is delivered.
117
+ */
118
+ export declare function sendSessionHandoverPartStep(input: {
119
+ readonly part: Omit<SessionHandoverPayload, "kind">;
120
+ readonly sessionId: string;
121
+ }): Promise<"delivered" | "session-ended">;
60
122
  export declare function signalSessionAnchorStep(input: {
61
123
  readonly result: {
62
124
  readonly output: unknown;
@@ -1 +1 @@
1
- import{openInputs,openSignIns}from"#protocol/session-projection.js";import{createLogger,logError}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{walkCauseChain}from"#shared/errors.js";import{getSandboxEnvironmentRuntime}from"#shared/sandbox-environment.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{queuedInput,storedProjection}from"#harness/session-machine/view.js";import{cancelRun,getWorld,resumeHook}from"#internal/workflow/runtime.js";import{getResolvedRuntimeAgentNode}from"#runtime/graph.js";import{readDurableSession}from"#execution/durable-session-store.js";import{getBlockingWorkflowToolRuns}from"#harness/workflow-tool-runs.js";import{IncompatibleStateLayoutError,deserializeContext}from"#context/serialize.js";import{holdsHitlRequests}from"#harness/hitl/index.js";import{EntityConflictError,RunExpiredError,WorkflowRunNotFoundError}from"#compiled/@workflow/errors/index.js";import{EVE_SESSION_ATTRIBUTE,EVE_VERSION_ATTRIBUTE}from"#execution/eve-workflow-attributes.js";import{SESSION_CHECKPOINT_VERSION}from"#execution/session/handoff.js";const log=createLogger(`execution.handoff`);function isSessionStateIdleForHandoff(e){let{state:t}=readDurableSession(e.sessionState),n=getBlockingWorkflowToolRuns(t);if(holdsHitlRequests(t)||[`eve.runtime.pendingInputBatch`,`eve.runtime.pendingCoordinationBatch`,`eve.runtime.deferredStepInput`,`eve.harness.pendingWorkflowInterrupt`].some(e=>t?.[e]!==void 0))return!1;let r=t?.[`eve.runtime.pendingInputBatches`];if(r!==void 0&&(!Array.isArray(r)||r.length>0))return!1;let i=storedProjection(t);return n.length===0&&i.activeTurnId===void 0&&openInputs(i).length===0&&openSignIns(i).length===0&&queuedInput(t)===void 0}async function isSessionIdleForHandoffStep(e){"use step";return isSessionStateIdleForHandoff(e)}async function validateSessionCheckpointStep(e){"use step";let{migration:t}=e,unreadable=t=>(log.warn(`session handoff refused: this deployment cannot read the checkpoint`,{detail:t,readableCheckpointVersion:SESSION_CHECKPOINT_VERSION,sessionId:e.sessionId}),{kind:`incompatible`,reason:`checkpoint-version`});if(t.kind===`incompatible`)return unreadable(t.detail);let{checkpoint:n}=t,r=n.sessionTimeoutMs;if(r!==!1&&(typeof r!=`number`||!Number.isFinite(r)||r<0))throw Error(`Session checkpoint contains an invalid timeout duration.`);let i;try{i=await deserializeContext(n.serializedContext)}catch(e){if(e instanceof IncompatibleStateLayoutError)return unreadable(e.message);throw e}let a=i.require(BundleKey),o=readDurableSession(n.sessionState).sandboxState?.session;if(o!=null&&o.providerName!==void 0){let e=getResolvedRuntimeAgentNode(a.graph,a.nodeId).sandboxRegistry.sandbox.inheritance?.definition??getResolvedRuntimeAgentNode(a.graph,a.nodeId).sandboxRegistry.sandbox.definition;if(e.kind!==`independent`)throw Error(`Session checkpoint sandbox has no resolved provider.`);let t=getSandboxEnvironmentRuntime(e.environment);if(o.providerName!==t.providerName||o.stateProtocolVersion!==t.stateProtocolVersion)throw Error(`Session checkpoint sandbox provider state is incompatible.`)}if(!isSessionStateIdleForHandoff(n))throw Error(`Session checkpoint contains pending work and cannot be handed off.`);return{kind:`valid`}}async function stopUntrackedChildSessionsStep(e){"use step";let t=await getWorld();for(let n of e.runIds)if(n!==e.sessionId)try{await cancelRun(t,n,{cancelReason:`Session upgraded`})}catch(t){[...walkCauseChain(t)].some(e=>EntityConflictError.is(e)||RunExpiredError.is(e)||WorkflowRunNotFoundError.is(e))||logError(log,`could not stop a subagent session after handoff`,t,{childRunId:n,sessionId:e.sessionId})}}async function reportSessionHandoffRetainedStep(e){"use step";log.warn(`session handoff failed; the current owner keeps the session`,{...e,checkpointVersion:SESSION_CHECKPOINT_VERSION})}async function recordSessionOwnerStep(e){"use step";let{setAttributes:t}=await import(`#compiled/@workflow/core/index.js`);await t({[EVE_SESSION_ATTRIBUTE]:e.sessionId,[EVE_VERSION_ATTRIBUTE]:resolveInstalledPackageInfo().version},{allowReservedAttributes:!0})}async function signalSessionOwnerActivationStep(e){"use step";await resumeHook(e.token,e.activation)}async function signalSessionAnchorStep(e){"use step";await resumeHook(e.token,e.result)}export{isSessionIdleForHandoffStep,isSessionStateIdleForHandoff,recordSessionOwnerStep,reportSessionHandoffRetainedStep,signalSessionAnchorStep,signalSessionOwnerActivationStep,stopUntrackedChildSessionsStep,validateSessionCheckpointStep};
1
+ import{openInputs,openSignIns}from"#protocol/session-projection.js";import{createLogger,logError}from"#internal/logging.js";import{sessionCommandHookToken,sessionInboxHookToken}from"#execution/session-inbox/address.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{walkCauseChain}from"#shared/errors.js";import{getSandboxEnvironmentRuntime}from"#shared/sandbox-environment.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{queuedInput,storedProjection}from"#harness/session-machine/view.js";import{cancelRun,getRun,getWorld,resumeHook}from"#internal/workflow/runtime.js";import{hasRunEnded,startWorkflowOnDeployment,workflowEntryReference}from"#execution/workflow-runtime.js";import{getResolvedRuntimeAgentNode}from"#runtime/graph.js";import{readDurableSession}from"#execution/durable-session-store.js";import{getBlockingWorkflowToolRuns}from"#harness/workflow-tool-runs.js";import{IncompatibleStateLayoutError,deserializeContext}from"#context/serialize.js";import{holdsHitlRequests}from"#harness/hitl/index.js";import{EntityConflictError,HookNotFoundError,RunExpiredError,WorkflowRunNotFoundError}from"#compiled/@workflow/errors/index.js";import{findSessionHookHolder}from"#execution/session-inbox/owner.js";import{EVE_SESSION_ATTRIBUTE,EVE_VERSION_ATTRIBUTE}from"#execution/eve-workflow-attributes.js";import{SESSION_CHECKPOINT_VERSION}from"#execution/session/handoff.js";import{mapWithConcurrency}from"#shared/map-with-concurrency.js";import{SESSION_HANDOFF_VERSION}from"#execution/session/entry-input.js";const log=createLogger(`execution.handoff`);function isSessionStateIdleForHandoff(e){let{state:t}=readDurableSession(e.sessionState),n=getBlockingWorkflowToolRuns(t);if(holdsHitlRequests(t)||[`eve.runtime.pendingInputBatch`,`eve.runtime.pendingCoordinationBatch`,`eve.runtime.deferredStepInput`,`eve.harness.pendingWorkflowInterrupt`].some(e=>t?.[e]!==void 0))return!1;let r=t?.[`eve.runtime.pendingInputBatches`];if(r!==void 0&&(!Array.isArray(r)||r.length>0))return!1;let i=storedProjection(t);return n.length===0&&i.activeTurnId===void 0&&openInputs(i).length===0&&openSignIns(i).length===0&&queuedInput(t)===void 0}async function startSuccessorStep(e){"use step";let{activationToken:t,anchorRunId:n,checkpoint:r,targetDeploymentId:i,...a}=e,{capabilities:o}=await getWorld();if(o?.hookForceClaim!==!0||o.hookRetention?.active!==!0)return log.warn(`session handoff unavailable: the Workflow world cannot force-claim hooks`,{sessionId:n}),{kind:`unsupported`};if(!isSessionStateIdleForHandoff(r))return{kind:`not-idle`};let s={...a,activationToken:t,checkpoint:r,handoffVersion:SESSION_HANDOFF_VERSION,kind:`handoff`,ownerDeploymentId:i,sessionWritable:getRun(n).getWritable(),sessionId:n};return{kind:`started`,runId:(await startWorkflowOnDeployment(workflowEntryReference,[s],i,r.retention===void 0?void 0:{experimental_retention:r.retention})).runId}}async function hasRunEndedStep(e){"use step";return await hasRunEnded(e.runId)}async function prepareSuccessorStep(e){"use step";let{migration:t}=e,unreadable=t=>(log.warn(`session handoff refused: this deployment cannot read the checkpoint`,{detail:t,readableCheckpointVersion:SESSION_CHECKPOINT_VERSION,sessionId:e.sessionId}),{kind:`incompatible`,reason:`checkpoint-version`});if(t.kind===`incompatible`)return unreadable(t.detail);let{checkpoint:n}=t,r=n.sessionTimeoutMs;if(r!==!1&&(typeof r!=`number`||!Number.isFinite(r)||r<0))throw Error(`Session checkpoint contains an invalid timeout duration.`);let i;try{i=await deserializeContext(n.serializedContext)}catch(e){if(e instanceof IncompatibleStateLayoutError)return unreadable(e.message);throw e}let a=i.require(BundleKey),o=readDurableSession(n.sessionState).sandboxState?.session;if(o!=null&&o.providerName!==void 0){let e=getResolvedRuntimeAgentNode(a.graph,a.nodeId).sandboxRegistry.sandbox.inheritance?.definition??getResolvedRuntimeAgentNode(a.graph,a.nodeId).sandboxRegistry.sandbox.definition;if(e.kind!==`independent`)throw Error(`Session checkpoint sandbox has no resolved provider.`);let t=getSandboxEnvironmentRuntime(e.environment);if(o.providerName!==t.providerName||o.stateProtocolVersion!==t.stateProtocolVersion)throw Error(`Session checkpoint sandbox provider state is incompatible.`)}if(!isSessionStateIdleForHandoff(n))throw Error(`Session checkpoint contains pending work and cannot be handed off.`);return e.source!==void 0&&!await holdsEveryAddress(e.source)?{kind:`source-gone`}:(await recordSessionOwner(e.sessionId),(e.handoffVersion??1)<SESSION_HANDOFF_VERSION&&log.info(`session moved from an owner that releases its addresses first`,{handoffVersion:e.handoffVersion??1,sessionId:e.sessionId}),{kind:`valid`})}async function holdsEveryAddress(e){return(await mapWithConcurrency(e.hookTokens,16,e=>findSessionHookHolder(sessionInboxHookToken(e)))).every(t=>t?.runId===e.runId)}async function stopUntrackedChildSessionsStep(e){"use step";let t=await getWorld();for(let n of e.runIds)if(n!==e.sessionId)try{await cancelRun(t,n,{cancelReason:`Session upgraded`})}catch(t){[...walkCauseChain(t)].some(e=>EntityConflictError.is(e)||RunExpiredError.is(e)||WorkflowRunNotFoundError.is(e))||logError(log,`could not stop a subagent session after handoff`,t,{childRunId:n,sessionId:e.sessionId})}}async function reportSessionHandoffRetainedStep(e){"use step";log.warn(`session handoff failed; the current owner keeps the session`,{...e,checkpointVersion:SESSION_CHECKPOINT_VERSION})}async function recordSessionOwnerStep(e){"use step";await recordSessionOwner(e.sessionId)}async function recordSessionOwner(e){let{setAttributes:t}=await import(`#compiled/@workflow/core/index.js`);await t({[EVE_SESSION_ATTRIBUTE]:e,[EVE_VERSION_ATTRIBUTE]:resolveInstalledPackageInfo().version},{allowReservedAttributes:!0})}async function signalSessionOwnerActivationStep(e){"use step";try{await resumeHook(e.token,e.activation)}catch(e){if(!HookNotFoundError.is(e))throw e}}async function reportSessionTakenOverOutsideAttemptStep(e){"use step";log.error(`session taken over with no handoff attempt open; this run leaves without finalizing`,{sessionId:e.sessionId})}async function reportHandoverPartStuckStep(e){"use step";log.warn(`session handover part keeps failing; the previous owner keeps retrying`,e)}async function sendSessionHandoverPartStep(e){"use step";try{return await resumeHook(sessionInboxHookToken(sessionCommandHookToken(e.sessionId)),{...e.part,kind:`handover`}),`delivered`}catch(e){if(HookNotFoundError.is(e))return`session-ended`;throw e}}async function signalSessionAnchorStep(e){"use step";await resumeHook(e.token,e.result)}export{hasRunEndedStep,isSessionStateIdleForHandoff,prepareSuccessorStep,recordSessionOwnerStep,reportHandoverPartStuckStep,reportSessionHandoffRetainedStep,reportSessionTakenOverOutsideAttemptStep,sendSessionHandoverPartStep,signalSessionAnchorStep,signalSessionOwnerActivationStep,startSuccessorStep,stopUntrackedChildSessionsStep};
@@ -1,4 +1,4 @@
1
- import type { DeliverHookPayload, SessionCapabilities } from "#channel/types.js";
1
+ import type { SessionCapabilities } from "#channel/types.js";
2
2
  import type { DurableSessionState } from "#execution/durable-session-store.js";
3
3
  import type { HarnessModelMessage } from "#harness/messages.js";
4
4
  import type { SessionInboxHandle, SessionInboxPayload } from "#execution/session-inbox/inbox.js";
@@ -29,11 +29,20 @@ export type SessionOwnerActivation = {
29
29
  } | {
30
30
  readonly kind: "incompatible";
31
31
  readonly reason: "checkpoint-version";
32
+ /** Version 3: the successor run that gave this verdict. */
33
+ readonly runId?: string;
32
34
  /** Commands accepted by partial successor claims before it rejected the checkpoint. */
33
35
  readonly payloads: readonly SessionInboxPayload[];
34
36
  } | {
35
37
  readonly kind: "failed";
38
+ /**
39
+ * Version 3: the successor took some of the session's hooks before it
40
+ * failed. The owner takes them back and waits for their handover.
41
+ */
42
+ readonly claimedHooks?: boolean;
36
43
  readonly error: unknown;
44
+ /** Version 3: the successor run that gave this verdict. */
45
+ readonly runId?: string;
37
46
  /** Commands accepted by partial successor claims before startup failed. */
38
47
  readonly payloads: readonly SessionInboxPayload[];
39
48
  };
@@ -45,65 +54,98 @@ interface SessionHandoffInput {
45
54
  readonly sessionId: string;
46
55
  }
47
56
  export declare function sessionAnchorToken(sessionId: string): string;
57
+ /** Only one run per attempt can hold this; a successor that loses it exits untouched. */
58
+ export declare function handoffLeaseToken(attemptKey: string): string;
59
+ /**
60
+ * Measured from the lease's creation and far beyond any queue delay, so a
61
+ * duplicate candidate that boots late still finds the token taken.
62
+ */
63
+ export declare const HANDOFF_LEASE_RETENTION = "7d";
48
64
  type SessionTransferOutcome = {
49
65
  readonly kind: "transferred";
50
66
  } | {
51
67
  readonly kind: "retained";
52
68
  readonly reason: SessionRetainedReason;
53
69
  };
54
- type SessionRetainedReason = "same-deployment" | "missing-deployment" | "not-idle" | "busy" | "accepted-during-release" | "activation-failed" | "checkpoint-incompatible" | "known-incompatible";
70
+ type SessionRetainedReason = "same-deployment" | "missing-deployment" | "not-idle" | "busy" | "activation-failed" | "checkpoint-incompatible" | "known-incompatible" | "unsupported-world";
55
71
  /** A compaction handoff the owner owes; the successor keeps this deadline. */
56
72
  export interface CompactionHandoff {
57
73
  readonly sessionTimeoutDeadline: Date | undefined;
58
74
  }
59
75
  type SessionTransferState = Pick<SessionCheckpoint, "history" | "serializedContext" | "sessionState">;
76
+ /**
77
+ * Sends `payloads` as handover parts, one payload per part: each was accepted
78
+ * on its own, so no part exceeds what the World already took. Every part is
79
+ * retried with backoff until it is delivered or the session has ended: the
80
+ * receiver reads nothing until the final part lands, and dropping a part
81
+ * would lose a message the session accepted.
82
+ */
83
+ export declare function sendSessionHandover(input: {
84
+ readonly attemptKey: string;
85
+ readonly payloads: readonly SessionInboxPayload[];
86
+ readonly sessionId: string;
87
+ }): Promise<void>;
60
88
  /**
61
89
  * The sole boundary for moving an idle session to a successor run: on another
62
90
  * exact deployment when newer code accepts a delivery, or on this deployment
63
91
  * after compaction so no single run's event log grows with the session.
64
92
  * Constructed once per owner; each `try*` method attempts one transfer.
65
- * When the upstream atomic hook-handoff primitive lands, only this class changes.
66
93
  *
67
- * Skipping known-incompatible targets requires this owner run to execute the
68
- * `incompatible` activation branch below. Owners still on an older eve build
69
- * treat that signal as a generic failure and attempt handoff on every turn;
70
- * they still benefit from successors returning incompatibility without step
71
- * retries.
94
+ * The owner never releases its hooks. The successor force-claims every one of
95
+ * them, and the takeover itself is the success signal: the owner then hands
96
+ * over whatever it accepted meanwhile and exits. Until the attempt settles
97
+ * the owner runs no turn.
72
98
  */
73
99
  export declare class SessionHandoff {
74
100
  private readonly input;
75
101
  private anchor;
76
102
  /** Deployments that rejected this session's checkpoint during this owner run. */
77
103
  private readonly incompatibleTargetDeploymentIds;
104
+ /** Counts this run's attempts; replay repeats the same sequence. */
105
+ private attempts;
106
+ /** The World cannot force-claim hooks, so no attempt can ever succeed. */
107
+ private worldUnsupported;
78
108
  constructor(input: SessionHandoffInput);
79
109
  /**
80
- * Attempts to move the session to the selected delivery's deployment. When a
81
- * compaction handoff is due and that deployment is this one or unknown, the
82
- * session moves to a fresh run here instead. The outcome states whether the
83
- * successor activated or this owner retained the session. Candidate failures
84
- * recover locally rather than escaping.
110
+ * Attempts to move the session to the selected delivery's deployment. The
111
+ * outcome states whether the successor took over or this owner retained the
112
+ * session. Candidate failures recover locally rather than escaping.
85
113
  */
86
- tryTransfer(selection: TurnSelection, state: SessionTransferState, options?: {
87
- readonly compaction?: CompactionHandoff;
88
- }): Promise<SessionTransferOutcome>;
114
+ tryTransfer(selection: TurnSelection, state: SessionTransferState): Promise<SessionTransferOutcome>;
89
115
  /**
90
116
  * Attempts to move a session that compacted to a fresh run on this
91
- * deployment. Without a delivery the caller guarantees no input is waiting,
92
- * and the successor parks until the next one arrives. The successor keeps
93
- * the session's deadline.
117
+ * deployment. The caller guarantees no input is waiting; the successor
118
+ * parks until the next one arrives and keeps the session's deadline.
94
119
  */
95
- tryCompactionTransfer(state: SessionTransferState, compaction: CompactionHandoff & {
96
- readonly delivery?: DeliverHookPayload;
97
- }): Promise<SessionTransferOutcome>;
120
+ tryCompactionTransfer(state: SessionTransferState, compaction: CompactionHandoff): Promise<SessionTransferOutcome>;
98
121
  private transfer;
122
+ /**
123
+ * The session was taken over while no attempt was open. This run leaves as
124
+ * after a transfer, without finalizing the stream the new owner now writes.
125
+ */
126
+ adoptTakeover(): Promise<{
127
+ readonly kind: "transferred";
128
+ }>;
129
+ /**
130
+ * Claims the attempt's lease. `gaveUpRunId` is the candidate whose outcome
131
+ * the owner is acting on: holding the lease, it can no longer take over.
132
+ */
133
+ private fenceAttempt;
134
+ /** Forwards what this run accepted but never read to the run that took the session over. */
135
+ private handOver;
136
+ /**
137
+ * Takes back every address a candidate that gave up may hold. Forced claims
138
+ * replace this run's own hooks, so this holds whether or not a pump has
139
+ * observed the candidate's claim yet.
140
+ */
141
+ private holdEveryToken;
142
+ private recoverFromSilentCandidate;
143
+ /** Keeps the session after a candidate's verdict, taking back any hook it claimed. */
144
+ private recover;
145
+ private ensureAnchor;
99
146
  /** After a transfer, the original run parks until the final owner reports the session result. */
100
147
  awaitAnchoredResult(): Promise<WorkflowEntryResult>;
101
148
  disposeAnchor(): Promise<void>;
102
- /** Starts the candidate and waits for it to activate or fail. */
103
- private startAndActivate;
104
- /** Reclaims the exact hook set and replays payloads accepted while it was released. */
105
- private recover;
106
149
  private reportRetained;
107
- private ensureAnchor;
108
150
  }
109
151
  export {};