@vercel/factory 0.0.15 → 0.0.16

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 (214) hide show
  1. package/CHANGELOG.md +356 -0
  2. package/README.md +49 -261
  3. package/dist/agent-routes.d.mts +47 -3
  4. package/dist/agent-routes.mjs +28 -1
  5. package/dist/agent-routes.mjs.map +1 -1
  6. package/dist/api-contracts.d.mts +20 -1
  7. package/dist/api-contracts.mjs +2 -1
  8. package/dist/api-contracts.mjs.map +1 -1
  9. package/dist/api.d.mts +45 -2
  10. package/dist/api.mjs +199 -10
  11. package/dist/api.mjs.map +1 -1
  12. package/dist/approval-contracts.d.mts +6 -0
  13. package/dist/blob/index.d.mts +51 -14
  14. package/dist/blob/index.mjs +26 -10
  15. package/dist/blob/index.mjs.map +1 -1
  16. package/dist/budget.d.mts +7 -0
  17. package/dist/budget.mjs +6 -0
  18. package/dist/budget.mjs.map +1 -1
  19. package/dist/build-factory.d.mts +1 -0
  20. package/dist/change-verification/dispatch.d.mts +16 -3
  21. package/dist/change-verification/dispatch.mjs +45 -7
  22. package/dist/change-verification/dispatch.mjs.map +1 -1
  23. package/dist/change-verification/eve-tool.d.mts +2 -1
  24. package/dist/change-verification/eve-tool.mjs +35 -47
  25. package/dist/change-verification/eve-tool.mjs.map +1 -1
  26. package/dist/change-verification/result.mjs +130 -0
  27. package/dist/change-verification/result.mjs.map +1 -0
  28. package/dist/changes/eve-record-change.d.mts +2 -2
  29. package/dist/changes/eve-record-change.mjs +43 -9
  30. package/dist/changes/eve-record-change.mjs.map +1 -1
  31. package/dist/changes.d.mts +4 -3
  32. package/dist/changes.mjs +2 -2
  33. package/dist/changes.mjs.map +1 -1
  34. package/dist/client-events.d.mts +10 -3
  35. package/dist/client-events.mjs +6 -2
  36. package/dist/client-events.mjs.map +1 -1
  37. package/dist/client-stream.mjs +8 -2
  38. package/dist/client-stream.mjs.map +1 -1
  39. package/dist/client-transcript.mjs +5 -1
  40. package/dist/client-transcript.mjs.map +1 -1
  41. package/dist/client.d.mts +121 -12
  42. package/dist/client.mjs +117 -9
  43. package/dist/client.mjs.map +1 -1
  44. package/dist/code-review/contracts.d.mts +1 -0
  45. package/dist/code-review/eve-post-review.d.mts +4 -4
  46. package/dist/code-review/eve-post-review.mjs +29 -17
  47. package/dist/code-review/eve-post-review.mjs.map +1 -1
  48. package/dist/code-review/eve-review-comments.d.mts +13 -2
  49. package/dist/code-review/eve-review-comments.mjs +45 -11
  50. package/dist/code-review/eve-review-comments.mjs.map +1 -1
  51. package/dist/code-review/github-reporter.d.mts +2 -0
  52. package/dist/code-review/github-reporter.mjs +7 -5
  53. package/dist/code-review/github-reporter.mjs.map +1 -1
  54. package/dist/code-review.d.mts +3 -2
  55. package/dist/deepsec/eve-tool.mjs +3 -1
  56. package/dist/deepsec/eve-tool.mjs.map +1 -1
  57. package/dist/dispatch.d.mts +79 -8
  58. package/dist/dispatch.mjs +68 -9
  59. package/dist/dispatch.mjs.map +1 -1
  60. package/dist/eve/index.d.mts +70 -10
  61. package/dist/eve/index.mjs +93 -22
  62. package/dist/eve/index.mjs.map +1 -1
  63. package/dist/eve/invoke.mjs +24 -11
  64. package/dist/eve/invoke.mjs.map +1 -1
  65. package/dist/eve/session-client.d.mts +122 -4
  66. package/dist/eve/session-client.mjs +127 -13
  67. package/dist/eve/session-client.mjs.map +1 -1
  68. package/dist/eve/task-execution.d.mts +380 -0
  69. package/dist/eve/task-execution.mjs +57 -2
  70. package/dist/eve/task-execution.mjs.map +1 -1
  71. package/dist/eve/task-session.d.mts +44 -2
  72. package/dist/eve/task-session.mjs +44 -2
  73. package/dist/eve/task-session.mjs.map +1 -1
  74. package/dist/eve/transcript.mjs +5 -1
  75. package/dist/eve/transcript.mjs.map +1 -1
  76. package/dist/execution.d.mts +117 -9
  77. package/dist/execution.mjs +76 -6
  78. package/dist/execution.mjs.map +1 -1
  79. package/dist/finding-remediation/admission.d.mts +2 -0
  80. package/dist/finding-remediation/admission.mjs +4 -1
  81. package/dist/finding-remediation/admission.mjs.map +1 -1
  82. package/dist/findings.d.mts +1 -0
  83. package/dist/github-publication.d.mts +1 -0
  84. package/dist/github-publication.mjs +97 -84
  85. package/dist/github-publication.mjs.map +1 -1
  86. package/dist/github-transfer.d.mts +15 -6
  87. package/dist/github-transfer.mjs +214 -66
  88. package/dist/github-transfer.mjs.map +1 -1
  89. package/dist/github.d.mts +51 -11
  90. package/dist/github.mjs +126 -24
  91. package/dist/github.mjs.map +1 -1
  92. package/dist/inbox-activity.d.mts +53 -0
  93. package/dist/inbox-activity.mjs +41 -0
  94. package/dist/inbox-activity.mjs.map +1 -0
  95. package/dist/index.d.mts +3 -1
  96. package/dist/index.mjs +3 -2
  97. package/dist/intake-contracts.d.mts +0 -1
  98. package/dist/integrations/deepsec.d.mts +1 -0
  99. package/dist/integrations/github.d.mts +2 -2
  100. package/dist/integrations/github.mjs +2 -2
  101. package/dist/integrations/slack.d.mts +3 -1
  102. package/dist/integrations/slack.mjs +3 -1
  103. package/dist/integrations/vercel.d.mts +4 -2
  104. package/dist/integrations/vercel.mjs +3 -2
  105. package/dist/merge-resolution/eve-tools.d.mts +1 -0
  106. package/dist/merge-resolution/eve-tools.mjs +7 -2
  107. package/dist/merge-resolution/eve-tools.mjs.map +1 -1
  108. package/dist/model-settings.d.mts +41 -0
  109. package/dist/model-settings.mjs +35 -0
  110. package/dist/model-settings.mjs.map +1 -0
  111. package/dist/planning/reconcile.mjs +6 -0
  112. package/dist/planning/reconcile.mjs.map +1 -1
  113. package/dist/postgres/index.d.mts +43 -2
  114. package/dist/postgres/index.mjs +40 -2
  115. package/dist/postgres/index.mjs.map +1 -1
  116. package/dist/presets/software-development/dispatch.d.mts +4 -1
  117. package/dist/presets/software-development/dispatch.mjs +2 -1
  118. package/dist/presets/software-development/dispatch.mjs.map +1 -1
  119. package/dist/presets/software-development/task-communication.d.mts +1 -0
  120. package/dist/presets/software-development/task-communication.mjs +48 -11
  121. package/dist/presets/software-development/task-communication.mjs.map +1 -1
  122. package/dist/presets/software-development.d.mts +1 -0
  123. package/dist/pull-requests/github-publisher.d.mts +15 -1
  124. package/dist/pull-requests/github-publisher.mjs +61 -1
  125. package/dist/pull-requests/github-publisher.mjs.map +1 -1
  126. package/dist/pull-requests.d.mts +1 -0
  127. package/dist/sandbox/index.d.mts +1 -0
  128. package/dist/schema/agent-route.d.mts +19 -1
  129. package/dist/schema/agent-route.mjs +19 -1
  130. package/dist/schema/agent-route.mjs.map +1 -1
  131. package/dist/schema/factory-config.d.mts +27 -0
  132. package/dist/schema/factory-config.mjs +33 -3
  133. package/dist/schema/factory-config.mjs.map +1 -1
  134. package/dist/schema/repository.d.mts +4 -0
  135. package/dist/schema/repository.mjs +5 -1
  136. package/dist/schema/repository.mjs.map +1 -1
  137. package/dist/schema/session.d.mts +1 -0
  138. package/dist/schema/session.mjs +1 -0
  139. package/dist/schema/session.mjs.map +1 -1
  140. package/dist/schema/slack-pr-notifications.d.mts +12 -0
  141. package/dist/schema/slack-pr-notifications.mjs +11 -0
  142. package/dist/schema/slack-pr-notifications.mjs.map +1 -0
  143. package/dist/schema/task-graph.d.mts +39 -0
  144. package/dist/schema/task.d.mts +1 -0
  145. package/dist/schema/task.mjs +2 -1
  146. package/dist/schema/task.mjs.map +1 -1
  147. package/dist/schema/transcript.d.mts +6 -0
  148. package/dist/schema/transcript.mjs +2 -1
  149. package/dist/schema/transcript.mjs.map +1 -1
  150. package/dist/schema/work.d.mts +52 -3
  151. package/dist/schema/work.mjs.map +1 -1
  152. package/dist/session-previews.d.mts +76 -0
  153. package/dist/session-previews.mjs +55 -0
  154. package/dist/session-previews.mjs.map +1 -0
  155. package/dist/session-review.d.mts +120 -0
  156. package/dist/session-review.mjs +79 -0
  157. package/dist/session-review.mjs.map +1 -0
  158. package/dist/signal-triage.mjs +1 -1
  159. package/dist/signals.d.mts +1 -0
  160. package/dist/stall.d.mts +4 -1
  161. package/dist/stall.mjs +6 -2
  162. package/dist/stall.mjs.map +1 -1
  163. package/dist/store/driver.d.mts +1 -1
  164. package/dist/store/driver.mjs.map +1 -1
  165. package/dist/store/engine.d.mts +206 -8
  166. package/dist/store/engine.mjs +147 -13
  167. package/dist/store/engine.mjs.map +1 -1
  168. package/dist/store/memory.d.mts +18 -1
  169. package/dist/store/memory.mjs +18 -1
  170. package/dist/store/memory.mjs.map +1 -1
  171. package/dist/store/slack-pr-notifications.d.mts +44 -0
  172. package/dist/store/slack-pr-notifications.mjs +121 -0
  173. package/dist/store/slack-pr-notifications.mjs.map +1 -0
  174. package/dist/store/task-work.d.mts +121 -6
  175. package/dist/store/task-work.mjs +7 -4
  176. package/dist/store/task-work.mjs.map +1 -1
  177. package/dist/sweep.d.mts +28 -6
  178. package/dist/sweep.mjs +34 -6
  179. package/dist/sweep.mjs.map +1 -1
  180. package/dist/task-graph-view.d.mts +3 -0
  181. package/dist/tasks.d.mts +3 -3
  182. package/dist/tasks.mjs +3 -3
  183. package/dist/vercel-git.d.mts +35 -3
  184. package/dist/vercel-git.mjs +265 -33
  185. package/dist/vercel-git.mjs.map +1 -1
  186. package/dist/vercel-github-api.d.mts +103 -0
  187. package/dist/vercel-github-api.mjs +363 -0
  188. package/dist/vercel-github-api.mjs.map +1 -0
  189. package/dist/vercel.d.mts +3 -2
  190. package/dist/vercel.mjs +3 -2
  191. package/dist/vercel.mjs.map +1 -1
  192. package/dist/work-triage.d.mts +1 -0
  193. package/dist/workflows.d.mts +102 -4
  194. package/dist/workflows.mjs +55 -2
  195. package/dist/workflows.mjs.map +1 -1
  196. package/dist/workspace-files-git.d.mts +15 -0
  197. package/dist/workspace-files-git.mjs +61 -0
  198. package/dist/workspace-files-git.mjs.map +1 -0
  199. package/dist/workspace-files.d.mts +107 -0
  200. package/dist/workspace-files.mjs +74 -0
  201. package/dist/workspace-files.mjs.map +1 -0
  202. package/docs/getting-started.md +104 -0
  203. package/docs/index.md +100 -0
  204. package/docs/recipes/cancellation.md +215 -0
  205. package/docs/recipes/custom-workflow.md +153 -0
  206. package/docs/recipes/dependent-tasks.md +207 -0
  207. package/docs/recipes/eve-agent.md +277 -0
  208. package/docs/recipes/human-input.md +204 -0
  209. package/docs/recipes/persistence-recovery.md +268 -0
  210. package/docs/recipes/retry-recovery.md +241 -0
  211. package/docs/recipes/task-messaging.md +215 -0
  212. package/docs/recipes/typed-eve-result.md +161 -0
  213. package/docs/runtime-integration.md +137 -0
  214. package/package.json +17 -6
@@ -1,7 +1,43 @@
1
+ import { readEvents } from "../client-events.mjs";
1
2
  import { ExecutionLaunchPendingError } from "../dispatch.mjs";
2
3
  import { Client } from "eve/client";
3
4
  //#region src/eve/session-client.ts
4
- /** Start or reconnect to an Eve session through its idempotent HTTP boundary. */
5
+ /**
6
+ * Starts or reconnects to one Eve session through its idempotent HTTP boundary.
7
+ *
8
+ * @remarks
9
+ * This call sends one POST to the selected agent's `/eve/v1/session` endpoint. Reusing
10
+ * `operationId` asks Eve to return the same logical session rather than create duplicate work.
11
+ * The returned execution reference identifies provider acceptance only; Factory dispatch must
12
+ * still persist it, and the agent must later complete or otherwise advance its Task.
13
+ *
14
+ * An HTTP refusal throws a regular error with the status. A successful response without a usable
15
+ * session ID throws `ExecutionLaunchPendingError`, because the provider may have accepted work and
16
+ * recovery must use the same operation ID. Request timeouts and transport failures propagate
17
+ * unchanged because this transport helper does not own a durable retry loop. A launcher that can
18
+ * recover the same running attempt must translate an ambiguous transport failure to
19
+ * `ExecutionLaunchPendingError` at that boundary and retry with the same operation ID.
20
+ *
21
+ * @param options - Agent URL, initial message, stable operation ID, and optional request controls.
22
+ * @returns An Eve execution reference containing the accepted session ID.
23
+ * @throws `ExecutionLaunchPendingError` after an accepted response without a session ID, and a
24
+ * regular error on timeout, transport failure, HTTP refusal, or invalid input.
25
+ * @see The shipped `docs/recipes/eve-agent.md` recipe for the complete dispatch boundary.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { startEveSession, taskHeaders } from "@vercel/factory";
30
+ * import type { TaskAttemptRef } from "@vercel/factory/execution";
31
+ *
32
+ * declare const task: TaskAttemptRef;
33
+ * const execution = await startEveSession({
34
+ * agentUrl: "https://factory.example/eve/agents/worker",
35
+ * message: `Work Task ${task.taskId}`,
36
+ * operationId: `factory-task:${task.taskId}:${task.attempt}`,
37
+ * headers: taskHeaders(task),
38
+ * });
39
+ * ```
40
+ */
5
41
  async function startEveSession(options) {
6
42
  const response = await (options.fetch ?? globalThis.fetch)(`${options.agentUrl.replace(/\/+$/u, "")}/eve/v1/session`, {
7
43
  method: "POST",
@@ -24,10 +60,28 @@ async function startEveSession(options) {
24
60
  };
25
61
  throw new ExecutionLaunchPendingError(`${options.agentName ?? "Eve agent"} accepted the request but returned no session ID`);
26
62
  }
27
- /** Forward one authorized Factory API request to its exact Eve session. */
63
+ /**
64
+ * Forwards one authorized Factory API request to its exact Eve session.
65
+ *
66
+ * @remarks
67
+ * The caller must authenticate and authorize the source request before invoking this helper. It
68
+ * forwards the source method, abort signal, and POST body using the supplied binding headers, then
69
+ * returns Eve's response unchanged. It does not interpret non-success status codes, retry the
70
+ * request, or update Factory Task state, so callers retain operation-specific response and replay
71
+ * handling.
72
+ *
73
+ * @param options - Exact Eve session, path, authorized source request, and transport controls.
74
+ * @returns The unconsumed response from the Eve session endpoint.
75
+ * @throws When a nonempty path is not absolute, resolves outside the session, contains a fragment,
76
+ * or the request transport fails.
77
+ */
28
78
  async function forwardEveSessionRequest(options) {
29
- if (!options.path.startsWith("/") || options.path.startsWith("//")) throw new Error("Eve session forwarding requires an absolute path suffix.");
30
- return (options.fetch ?? globalThis.fetch)(`${options.agentUrl.replace(/\/+$/u, "")}/eve/v1/session/${encodeURIComponent(options.sessionId)}${options.path}`, {
79
+ if (options.path !== "" && (!options.path.startsWith("/") || options.path.startsWith("//"))) throw new Error("Eve session forwarding requires an absolute path suffix.");
80
+ const sessionUrl = `${options.agentUrl.replace(/\/+$/u, "")}/eve/v1/session/${encodeURIComponent(options.sessionId)}`;
81
+ const target = new URL(`${sessionUrl}${options.path}`);
82
+ const session = new URL(sessionUrl);
83
+ if (target.origin !== session.origin || target.hash !== "" || target.pathname !== session.pathname && !target.pathname.startsWith(`${session.pathname}/`)) throw new Error("Eve session forwarding path must stay within the selected session.");
84
+ return (options.fetch ?? globalThis.fetch)(target.href, {
31
85
  method: options.source.method,
32
86
  headers: jsonHeaders(await options.headers),
33
87
  ...options.source.method === "POST" ? { body: await options.source.text() } : {},
@@ -35,7 +89,23 @@ async function forwardEveSessionRequest(options) {
35
89
  redirect: "error"
36
90
  });
37
91
  }
38
- /** Request cancellation and confirm the exact Eve session has stopped running. */
92
+ /**
93
+ * Requests cancellation and confirms that the exact Eve session has stopped running.
94
+ *
95
+ * @remarks
96
+ * A successful cancellation response is not sufficient by itself. Unless Eve reports
97
+ * `no_active_turn`, this helper follows from the latest provider event and returns only after
98
+ * observing `session.waiting`, `session.completed`, or `session.failed`. A refusal, timeout, stream
99
+ * failure, or non-confirming event throws; the caller should keep canonical Task cancellation and
100
+ * retry or inspect this same session rather than assume it stopped.
101
+ *
102
+ * This helper does not transition or settle a Factory Task. Use the higher-level execution
103
+ * cancellation flow when Task lifecycle and usage must be persisted together.
104
+ *
105
+ * @param options - Exact Eve session plus request and confirmation bounds.
106
+ * @returns After the provider confirms that the session has no active turn.
107
+ * @throws On HTTP refusal, request or confirmation timeout, stream failure, or missing confirmation.
108
+ */
39
109
  async function cancelEveSession(options) {
40
110
  const headers = await options.headers;
41
111
  const response = await (options.fetch ?? globalThis.fetch)(`${options.agentUrl.replace(/\/+$/u, "")}/eve/v1/session/${encodeURIComponent(options.sessionId)}/cancel`, {
@@ -69,21 +139,63 @@ async function cancelEveSession(options) {
69
139
  }
70
140
  throw new Error("Eve execution cancellation is not yet confirmed by the provider stream");
71
141
  }
72
- /** Read the response from the latest fully completed Eve turn, if one exists. */
142
+ /**
143
+ * Reads the response from the latest fully completed Eve turn, if one exists.
144
+ *
145
+ * @remarks
146
+ * This performs a bounded, non-following scan from the start of the session. It returns a nonempty
147
+ * completed message only when the matching latest turn reaches `turn.completed`; failed,
148
+ * cancelled, incomplete, over-limit, or message-less turns return `null`. The operation is
149
+ * read-only and safe to repeat. The default reader verifies that it reached Eve's advertised
150
+ * durable tail before accepting output. Transport and timeout failures are propagated so callers
151
+ * can distinguish unavailable or truncated history from a confirmed lack of completed output.
152
+ *
153
+ * @param options - Exact Eve session plus stream deadline and event bound.
154
+ * @returns The latest completed turn response, or `null` when no bounded complete response exists.
155
+ * @throws When the event stream cannot be read or its deadline expires.
156
+ */
73
157
  async function readCompletedEveTurn(options) {
74
158
  const headers = await options.headers ?? {};
75
- let response = null;
76
- let turnId;
77
- let completed = false;
78
- let count = 0;
79
- for await (const event of (options.stream ?? streamEveSession)({
159
+ const signal = AbortSignal.timeout(options.timeoutMs ?? 1e4);
160
+ let expectedEventCount;
161
+ let events;
162
+ if (options.stream !== void 0) events = options.stream({
80
163
  agentUrl: options.agentUrl,
81
164
  sessionId: options.sessionId,
82
165
  headers,
83
- signal: AbortSignal.timeout(options.timeoutMs ?? 1e4),
166
+ signal,
84
167
  startIndex: 0,
85
168
  follow: false
86
- })) {
169
+ });
170
+ else {
171
+ const query = new URLSearchParams({
172
+ startIndex: "0",
173
+ includeTailIndex: "1"
174
+ });
175
+ const response = await (options.fetch ?? globalThis.fetch)(`${options.agentUrl.replace(/\/+$/u, "")}/eve/v1/session/${encodeURIComponent(options.sessionId)}/stream?${query}`, {
176
+ cache: "no-store",
177
+ headers,
178
+ redirect: "error",
179
+ signal
180
+ });
181
+ if (!response.ok || response.body === null) {
182
+ await response.body?.cancel();
183
+ throw new Error(`Eve session history refused: ${response.status}`);
184
+ }
185
+ const tailValue = response.headers.get("x-eve-stream-tail-index");
186
+ const tail = tailValue === null ? NaN : Number(tailValue);
187
+ if (!Number.isSafeInteger(tail) || tail < -1) {
188
+ await response.body.cancel();
189
+ throw new Error("Eve session history did not include a valid durable cursor");
190
+ }
191
+ expectedEventCount = tail + 1;
192
+ events = readEvents(response.body);
193
+ }
194
+ let response = null;
195
+ let turnId;
196
+ let completed = false;
197
+ let count = 0;
198
+ for await (const event of events) {
87
199
  if (++count > (options.maxEvents ?? 1e4)) return null;
88
200
  const data = eventData(event);
89
201
  if (event.type === "turn.started") {
@@ -99,6 +211,8 @@ async function readCompletedEveTurn(options) {
99
211
  "session.failed"
100
212
  ].includes(event.type)) completed = false;
101
213
  }
214
+ signal.throwIfAborted();
215
+ if (expectedEventCount !== void 0 && count !== expectedEventCount) throw new Error("Eve session history closed before its durable cursor");
102
216
  return completed && response?.trim() ? response : null;
103
217
  }
104
218
  async function* streamEveSession(input) {
@@ -1 +1 @@
1
- {"version":3,"file":"session-client.mjs","names":[],"sources":["../../src/eve/session-client.ts"],"sourcesContent":["import { Client } from \"eve/client\";\nimport { ExecutionLaunchPendingError } from \"../dispatch\";\nimport type { MaybePromise } from \"../schema/common\";\nimport type { ExecutionReference } from \"../schema/task\";\n\n/** The minimal event shape consumed from an Eve session stream. */\nexport interface EveSessionEvent {\n type: string;\n data?: unknown;\n}\n\n/** Identifies and bounds a direct read of one Eve session event stream. */\nexport interface EveSessionStreamInput {\n agentUrl: string;\n sessionId: string;\n headers: HeadersInit;\n signal: AbortSignal;\n startIndex: number;\n follow: boolean;\n}\n\n/** Injectable reader for an Eve session's ordered event stream. */\nexport type EveSessionStream = (input: EveSessionStreamInput) => AsyncIterable<EveSessionEvent>;\n\n/** Configures an idempotent request to start or reconnect to an Eve session. */\nexport interface StartEveSessionOptions {\n agentUrl: string;\n message: unknown;\n operationId: string;\n headers?: MaybePromise<HeadersInit>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n agentName?: string;\n}\n\n/** Start or reconnect to an Eve session through its idempotent HTTP boundary. */\nexport async function startEveSession(\n options: StartEveSessionOptions,\n): Promise<ExecutionReference> {\n const response = await (options.fetch ?? globalThis.fetch)(\n `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session`,\n {\n method: \"POST\",\n headers: jsonHeaders(await options.headers),\n body: JSON.stringify({ message: options.message, operationId: options.operationId }),\n signal: AbortSignal.timeout(options.timeoutMs ?? 20_000),\n redirect: \"error\",\n },\n );\n if (!response.ok) {\n await response.body?.cancel();\n throw new Error(`${options.agentName ?? \"Eve agent\"} refused the session: ${response.status}`);\n }\n const body: unknown = await response.json().catch(() => null);\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"sessionId\" in body &&\n typeof body.sessionId === \"string\" &&\n body.sessionId !== \"\"\n ) {\n return { provider: \"eve\", sessionId: body.sessionId };\n }\n throw new ExecutionLaunchPendingError(\n `${options.agentName ?? \"Eve agent\"} accepted the request but returned no session ID`,\n );\n}\n\n/** Configures forwarding an authorized HTTP request to one exact Eve session. */\nexport interface ForwardEveSessionRequestOptions {\n agentUrl: string;\n sessionId: string;\n path: string;\n source: Request;\n headers?: MaybePromise<HeadersInit>;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Forward one authorized Factory API request to its exact Eve session. */\nexport async function forwardEveSessionRequest(\n options: ForwardEveSessionRequestOptions,\n): Promise<Response> {\n if (!options.path.startsWith(\"/\") || options.path.startsWith(\"//\")) {\n throw new Error(\"Eve session forwarding requires an absolute path suffix.\");\n }\n return (options.fetch ?? globalThis.fetch)(\n `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session/${encodeURIComponent(options.sessionId)}${options.path}`,\n {\n method: options.source.method,\n headers: jsonHeaders(await options.headers),\n ...(options.source.method === \"POST\" ? { body: await options.source.text() } : {}),\n signal: options.source.signal,\n redirect: \"error\",\n },\n );\n}\n\n/** Configures cancellation and provider-stream confirmation for one exact Eve session. */\nexport interface CancelEveSessionOptions {\n agentUrl: string;\n sessionId: string;\n headers?: MaybePromise<HeadersInit>;\n fetch?: typeof globalThis.fetch;\n requestTimeoutMs?: number;\n confirmationTimeoutMs?: number;\n stream?: EveSessionStream;\n}\n\n/** Request cancellation and confirm the exact Eve session has stopped running. */\nexport async function cancelEveSession(options: CancelEveSessionOptions): Promise<void> {\n const headers = await options.headers;\n const response = await (options.fetch ?? globalThis.fetch)(\n `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session/${encodeURIComponent(options.sessionId)}/cancel`,\n {\n method: \"POST\",\n headers: jsonHeaders(headers),\n body: \"{}\",\n redirect: \"error\",\n signal: AbortSignal.timeout(options.requestTimeoutMs ?? 10_000),\n },\n );\n if (!response.ok) {\n await response.body?.cancel();\n throw new Error(`Eve execution cancellation refused: ${response.status}`);\n }\n const result: unknown = await response.json().catch(() => null);\n if (\n typeof result === \"object\" &&\n result !== null &&\n \"status\" in result &&\n result.status === \"no_active_turn\"\n ) {\n return;\n }\n const signal = AbortSignal.timeout(options.confirmationTimeoutMs ?? 5_000);\n for await (const event of (options.stream ?? streamEveSession)({\n agentUrl: options.agentUrl,\n sessionId: options.sessionId,\n headers: headers ?? {},\n signal,\n startIndex: -1,\n follow: true,\n })) {\n if ([\"session.waiting\", \"session.completed\", \"session.failed\"].includes(event.type)) return;\n break;\n }\n throw new Error(\"Eve execution cancellation is not yet confirmed by the provider stream\");\n}\n\n/** Configures a bounded scan for the response from the latest completed Eve turn. */\nexport interface ReadCompletedEveTurnOptions {\n agentUrl: string;\n sessionId: string;\n headers?: MaybePromise<HeadersInit>;\n timeoutMs?: number;\n maxEvents?: number;\n stream?: EveSessionStream;\n}\n\n/** Read the response from the latest fully completed Eve turn, if one exists. */\nexport async function readCompletedEveTurn(\n options: ReadCompletedEveTurnOptions,\n): Promise<string | null> {\n const headers = (await options.headers) ?? {};\n let response: string | null = null;\n let turnId: string | undefined;\n let completed = false;\n let count = 0;\n for await (const event of (options.stream ?? streamEveSession)({\n agentUrl: options.agentUrl,\n sessionId: options.sessionId,\n headers,\n signal: AbortSignal.timeout(options.timeoutMs ?? 10_000),\n startIndex: 0,\n follow: false,\n })) {\n if (++count > (options.maxEvents ?? 10_000)) return null;\n const data = eventData(event);\n if (event.type === \"turn.started\") {\n turnId = typeof data.turnId === \"string\" ? data.turnId : undefined;\n response = null;\n completed = false;\n }\n if (\n event.type === \"message.completed\" &&\n data.turnId === turnId &&\n typeof data.message === \"string\"\n ) {\n response = data.message;\n }\n if (event.type === \"turn.completed\" && data.turnId === turnId) completed = true;\n if ([\"turn.failed\", \"turn.cancelled\", \"session.failed\"].includes(event.type)) {\n completed = false;\n }\n }\n return completed && response?.trim() ? response : null;\n}\n\nasync function* streamEveSession(input: EveSessionStreamInput): AsyncIterable<EveSessionEvent> {\n const client = new Client({\n host: input.agentUrl.replace(/\\/+$/u, \"\"),\n headers: Object.fromEntries(new Headers(input.headers).entries()),\n redirect: \"error\",\n });\n for await (const event of client.sessions.attach(input.sessionId).stream({\n startIndex: input.startIndex,\n follow: input.follow,\n streamReconnectPolicy: { reconnect: false },\n signal: input.signal,\n })) {\n yield { type: event.type, ...(\"data\" in event ? { data: event.data } : {}) };\n }\n}\n\nfunction eventData(event: EveSessionEvent): Record<string, unknown> {\n return typeof event.data === \"object\" && event.data !== null && !Array.isArray(event.data)\n ? (event.data as Record<string, unknown>)\n : {};\n}\n\nfunction jsonHeaders(input?: HeadersInit): Record<string, string> {\n const headers = new Headers(input);\n if (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n return Object.fromEntries(headers.entries());\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-client.mjs","names":[],"sources":["../../src/eve/session-client.ts"],"sourcesContent":["import { Client } from \"eve/client\";\nimport { readEvents } from \"../client-events\";\nimport { ExecutionLaunchPendingError } from \"../dispatch\";\nimport type { MaybePromise } from \"../schema/common\";\nimport type { ExecutionReference } from \"../schema/task\";\n\n/** The minimal event shape consumed from an Eve session stream. */\nexport interface EveSessionEvent {\n /** Eve protocol event discriminator. */\n type: string;\n /** Provider payload retained as unknown until the consuming operation validates required fields. */\n data?: unknown;\n}\n\n/** Identifies and bounds a direct read of one Eve session event stream. */\nexport interface EveSessionStreamInput {\n /** Base URL of the selected Eve agent. */\n agentUrl: string;\n /** Exact provider session whose ordered event log is read. */\n sessionId: string;\n /** Authentication and Factory binding headers for the stream request. */\n headers: HeadersInit;\n /** Cancellation or deadline signal that bounds the stream read. */\n signal: AbortSignal;\n /** Inclusive provider event index, or `-1` for the latest event. */\n startIndex: number;\n /** Whether the stream waits for events appended after the current end. */\n follow: boolean;\n}\n\n/** Injectable reader for an Eve session's ordered event stream. */\nexport type EveSessionStream = (input: EveSessionStreamInput) => AsyncIterable<EveSessionEvent>;\n\n/** Configures an idempotent request to start or reconnect to an Eve session. */\nexport interface StartEveSessionOptions {\n /** Base URL of the selected Eve agent; trailing slashes are ignored. */\n agentUrl: string;\n /** Initial user message serialized into Eve's session-create request. */\n message: unknown;\n /** Stable application idempotency key reused when reconnecting the same logical launch. */\n operationId: string;\n /** Authentication and Factory binding headers, resolved immediately before the request. */\n headers?: MaybePromise<HeadersInit>;\n /**\n * HTTP implementation. Defaults to `globalThis.fetch`. Transport rejections propagate; a launch\n * adapter that owns durable recovery must translate ambiguous acceptance at its boundary.\n */\n fetch?: typeof globalThis.fetch;\n /** Request deadline in milliseconds. Defaults to 20,000. */\n timeoutMs?: number;\n /** Human-readable agent name included in refusal diagnostics. Defaults to `Eve agent`. */\n agentName?: string;\n}\n\n/**\n * Starts or reconnects to one Eve session through its idempotent HTTP boundary.\n *\n * @remarks\n * This call sends one POST to the selected agent's `/eve/v1/session` endpoint. Reusing\n * `operationId` asks Eve to return the same logical session rather than create duplicate work.\n * The returned execution reference identifies provider acceptance only; Factory dispatch must\n * still persist it, and the agent must later complete or otherwise advance its Task.\n *\n * An HTTP refusal throws a regular error with the status. A successful response without a usable\n * session ID throws `ExecutionLaunchPendingError`, because the provider may have accepted work and\n * recovery must use the same operation ID. Request timeouts and transport failures propagate\n * unchanged because this transport helper does not own a durable retry loop. A launcher that can\n * recover the same running attempt must translate an ambiguous transport failure to\n * `ExecutionLaunchPendingError` at that boundary and retry with the same operation ID.\n *\n * @param options - Agent URL, initial message, stable operation ID, and optional request controls.\n * @returns An Eve execution reference containing the accepted session ID.\n * @throws `ExecutionLaunchPendingError` after an accepted response without a session ID, and a\n * regular error on timeout, transport failure, HTTP refusal, or invalid input.\n * @see The shipped `docs/recipes/eve-agent.md` recipe for the complete dispatch boundary.\n *\n * @example\n * ```ts\n * import { startEveSession, taskHeaders } from \"@vercel/factory\";\n * import type { TaskAttemptRef } from \"@vercel/factory/execution\";\n *\n * declare const task: TaskAttemptRef;\n * const execution = await startEveSession({\n * agentUrl: \"https://factory.example/eve/agents/worker\",\n * message: `Work Task ${task.taskId}`,\n * operationId: `factory-task:${task.taskId}:${task.attempt}`,\n * headers: taskHeaders(task),\n * });\n * ```\n */\nexport async function startEveSession(\n options: StartEveSessionOptions,\n): Promise<ExecutionReference> {\n const response = await (options.fetch ?? globalThis.fetch)(\n `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session`,\n {\n method: \"POST\",\n headers: jsonHeaders(await options.headers),\n body: JSON.stringify({ message: options.message, operationId: options.operationId }),\n signal: AbortSignal.timeout(options.timeoutMs ?? 20_000),\n redirect: \"error\",\n },\n );\n if (!response.ok) {\n await response.body?.cancel();\n throw new Error(`${options.agentName ?? \"Eve agent\"} refused the session: ${response.status}`);\n }\n const body: unknown = await response.json().catch(() => null);\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"sessionId\" in body &&\n typeof body.sessionId === \"string\" &&\n body.sessionId !== \"\"\n ) {\n return { provider: \"eve\", sessionId: body.sessionId };\n }\n throw new ExecutionLaunchPendingError(\n `${options.agentName ?? \"Eve agent\"} accepted the request but returned no session ID`,\n );\n}\n\n/** Configures forwarding an authorized HTTP request to one exact Eve session. */\nexport interface ForwardEveSessionRequestOptions {\n /** Base URL of the selected Eve agent; trailing slashes are ignored. */\n agentUrl: string;\n /** Exact provider session that owns the forwarded Factory API request. */\n sessionId: string;\n /** Empty for the session endpoint, otherwise an absolute path/query suffix within it; fragments are refused. */\n path: string;\n /** Incoming request supplying method, abort signal, and POST body. */\n source: Request;\n /** Authentication and Factory binding headers, resolved immediately before forwarding. */\n headers?: MaybePromise<HeadersInit>;\n /** HTTP implementation. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * Forwards one authorized Factory API request to its exact Eve session.\n *\n * @remarks\n * The caller must authenticate and authorize the source request before invoking this helper. It\n * forwards the source method, abort signal, and POST body using the supplied binding headers, then\n * returns Eve's response unchanged. It does not interpret non-success status codes, retry the\n * request, or update Factory Task state, so callers retain operation-specific response and replay\n * handling.\n *\n * @param options - Exact Eve session, path, authorized source request, and transport controls.\n * @returns The unconsumed response from the Eve session endpoint.\n * @throws When a nonempty path is not absolute, resolves outside the session, contains a fragment,\n * or the request transport fails.\n */\nexport async function forwardEveSessionRequest(\n options: ForwardEveSessionRequestOptions,\n): Promise<Response> {\n if (options.path !== \"\" && (!options.path.startsWith(\"/\") || options.path.startsWith(\"//\"))) {\n throw new Error(\"Eve session forwarding requires an absolute path suffix.\");\n }\n const sessionUrl = `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session/${encodeURIComponent(options.sessionId)}`;\n const target = new URL(`${sessionUrl}${options.path}`);\n const session = new URL(sessionUrl);\n if (\n target.origin !== session.origin ||\n target.hash !== \"\" ||\n (target.pathname !== session.pathname && !target.pathname.startsWith(`${session.pathname}/`))\n ) {\n throw new Error(\"Eve session forwarding path must stay within the selected session.\");\n }\n return (options.fetch ?? globalThis.fetch)(target.href, {\n method: options.source.method,\n headers: jsonHeaders(await options.headers),\n ...(options.source.method === \"POST\" ? { body: await options.source.text() } : {}),\n signal: options.source.signal,\n redirect: \"error\",\n });\n}\n\n/** Configures cancellation and provider-stream confirmation for one exact Eve session. */\nexport interface CancelEveSessionOptions {\n /** Base URL of the selected Eve agent; trailing slashes are ignored. */\n agentUrl: string;\n /** Exact provider session to cancel and observe. */\n sessionId: string;\n /** Authentication and Factory binding headers used for cancellation and confirmation. */\n headers?: MaybePromise<HeadersInit>;\n /** HTTP implementation. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /** Cancellation request deadline in milliseconds. Defaults to 10,000. */\n requestTimeoutMs?: number;\n /** Provider-stream confirmation deadline in milliseconds. Defaults to 5,000. */\n confirmationTimeoutMs?: number;\n /** Injectable ordered event reader. Defaults to the Eve client stream. */\n stream?: EveSessionStream;\n}\n\n/**\n * Requests cancellation and confirms that the exact Eve session has stopped running.\n *\n * @remarks\n * A successful cancellation response is not sufficient by itself. Unless Eve reports\n * `no_active_turn`, this helper follows from the latest provider event and returns only after\n * observing `session.waiting`, `session.completed`, or `session.failed`. A refusal, timeout, stream\n * failure, or non-confirming event throws; the caller should keep canonical Task cancellation and\n * retry or inspect this same session rather than assume it stopped.\n *\n * This helper does not transition or settle a Factory Task. Use the higher-level execution\n * cancellation flow when Task lifecycle and usage must be persisted together.\n *\n * @param options - Exact Eve session plus request and confirmation bounds.\n * @returns After the provider confirms that the session has no active turn.\n * @throws On HTTP refusal, request or confirmation timeout, stream failure, or missing confirmation.\n */\nexport async function cancelEveSession(options: CancelEveSessionOptions): Promise<void> {\n const headers = await options.headers;\n const response = await (options.fetch ?? globalThis.fetch)(\n `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session/${encodeURIComponent(options.sessionId)}/cancel`,\n {\n method: \"POST\",\n headers: jsonHeaders(headers),\n body: \"{}\",\n redirect: \"error\",\n signal: AbortSignal.timeout(options.requestTimeoutMs ?? 10_000),\n },\n );\n if (!response.ok) {\n await response.body?.cancel();\n throw new Error(`Eve execution cancellation refused: ${response.status}`);\n }\n const result: unknown = await response.json().catch(() => null);\n if (\n typeof result === \"object\" &&\n result !== null &&\n \"status\" in result &&\n result.status === \"no_active_turn\"\n ) {\n return;\n }\n const signal = AbortSignal.timeout(options.confirmationTimeoutMs ?? 5_000);\n for await (const event of (options.stream ?? streamEveSession)({\n agentUrl: options.agentUrl,\n sessionId: options.sessionId,\n headers: headers ?? {},\n signal,\n startIndex: -1,\n follow: true,\n })) {\n if ([\"session.waiting\", \"session.completed\", \"session.failed\"].includes(event.type)) return;\n break;\n }\n throw new Error(\"Eve execution cancellation is not yet confirmed by the provider stream\");\n}\n\n/** Configures a bounded scan for the response from the latest completed Eve turn. */\nexport interface ReadCompletedEveTurnOptions {\n /** Base URL of the selected Eve agent; trailing slashes are ignored. */\n agentUrl: string;\n /** Exact provider session whose finite event history is scanned. */\n sessionId: string;\n /** Authentication and Factory binding headers for the stream request. */\n headers?: MaybePromise<HeadersInit>;\n /** Event-stream read deadline in milliseconds. Defaults to 10,000. */\n timeoutMs?: number;\n /** Maximum events inspected before returning `null`. Defaults to 10,000. */\n maxEvents?: number;\n /** HTTP implementation used by the default bounded stream reader. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch;\n /** Injectable ordered event reader; it must propagate transport failures to preserve certainty. */\n stream?: EveSessionStream;\n}\n\n/**\n * Reads the response from the latest fully completed Eve turn, if one exists.\n *\n * @remarks\n * This performs a bounded, non-following scan from the start of the session. It returns a nonempty\n * completed message only when the matching latest turn reaches `turn.completed`; failed,\n * cancelled, incomplete, over-limit, or message-less turns return `null`. The operation is\n * read-only and safe to repeat. The default reader verifies that it reached Eve's advertised\n * durable tail before accepting output. Transport and timeout failures are propagated so callers\n * can distinguish unavailable or truncated history from a confirmed lack of completed output.\n *\n * @param options - Exact Eve session plus stream deadline and event bound.\n * @returns The latest completed turn response, or `null` when no bounded complete response exists.\n * @throws When the event stream cannot be read or its deadline expires.\n */\nexport async function readCompletedEveTurn(\n options: ReadCompletedEveTurnOptions,\n): Promise<string | null> {\n const headers = (await options.headers) ?? {};\n const signal = AbortSignal.timeout(options.timeoutMs ?? 10_000);\n let expectedEventCount: number | undefined;\n let events: AsyncIterable<EveSessionEvent>;\n if (options.stream !== undefined) {\n events = options.stream({\n agentUrl: options.agentUrl,\n sessionId: options.sessionId,\n headers,\n signal,\n startIndex: 0,\n follow: false,\n });\n } else {\n const query = new URLSearchParams({ startIndex: \"0\", includeTailIndex: \"1\" });\n const response = await (options.fetch ?? globalThis.fetch)(\n `${options.agentUrl.replace(/\\/+$/u, \"\")}/eve/v1/session/${encodeURIComponent(options.sessionId)}/stream?${query}`,\n { cache: \"no-store\", headers, redirect: \"error\", signal },\n );\n if (!response.ok || response.body === null) {\n await response.body?.cancel();\n throw new Error(`Eve session history refused: ${response.status}`);\n }\n const tailValue = response.headers.get(\"x-eve-stream-tail-index\");\n const tail = tailValue === null ? Number.NaN : Number(tailValue);\n if (!Number.isSafeInteger(tail) || tail < -1) {\n await response.body.cancel();\n throw new Error(\"Eve session history did not include a valid durable cursor\");\n }\n expectedEventCount = tail + 1;\n events = readEvents(response.body);\n }\n let response: string | null = null;\n let turnId: string | undefined;\n let completed = false;\n let count = 0;\n for await (const event of events) {\n if (++count > (options.maxEvents ?? 10_000)) return null;\n const data = eventData(event);\n if (event.type === \"turn.started\") {\n turnId = typeof data.turnId === \"string\" ? data.turnId : undefined;\n response = null;\n completed = false;\n }\n if (\n event.type === \"message.completed\" &&\n data.turnId === turnId &&\n typeof data.message === \"string\"\n ) {\n response = data.message;\n }\n if (event.type === \"turn.completed\" && data.turnId === turnId) completed = true;\n if ([\"turn.failed\", \"turn.cancelled\", \"session.failed\"].includes(event.type)) {\n completed = false;\n }\n }\n signal.throwIfAborted();\n if (expectedEventCount !== undefined && count !== expectedEventCount) {\n throw new Error(\"Eve session history closed before its durable cursor\");\n }\n return completed && response?.trim() ? response : null;\n}\n\nasync function* streamEveSession(input: EveSessionStreamInput): AsyncIterable<EveSessionEvent> {\n const client = new Client({\n host: input.agentUrl.replace(/\\/+$/u, \"\"),\n headers: Object.fromEntries(new Headers(input.headers).entries()),\n redirect: \"error\",\n });\n for await (const event of client.sessions.attach(input.sessionId).stream({\n startIndex: input.startIndex,\n follow: input.follow,\n streamReconnectPolicy: { reconnect: false },\n signal: input.signal,\n })) {\n yield { type: event.type, ...(\"data\" in event ? { data: event.data } : {}) };\n }\n}\n\nfunction eventData(event: EveSessionEvent): Record<string, unknown> {\n return typeof event.data === \"object\" && event.data !== null && !Array.isArray(event.data)\n ? (event.data as Record<string, unknown>)\n : {};\n}\n\nfunction jsonHeaders(input?: HeadersInit): Record<string, string> {\n const headers = new Headers(input);\n if (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n return Object.fromEntries(headers.entries());\n}\n"],"mappings":"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"}
@@ -0,0 +1,380 @@
1
+ import { TaskId } from "../schema/id.mjs";
2
+ import { JsonValue } from "../schema/work.mjs";
3
+ import { TaskKind } from "../schema/task.mjs";
4
+ import "../workflows.mjs";
5
+ import { FactoryStores } from "../store/engine.mjs";
6
+ import "../tasks.mjs";
7
+ import { SessionContext } from "eve/context";
8
+ //#region src/eve/task-execution.d.ts
9
+ /**
10
+ * Requires the authenticated session's current running Eve Task execution and attempt.
11
+ *
12
+ * @remarks
13
+ * `requestedTaskId` is a consistency check, not an authority source. This function reads the Task
14
+ * and attempt established by `withFactoryTask`, rejects conflicting current-auth attributes, and
15
+ * verifies that durable state still points to the same Eve session and attempt. Use it immediately
16
+ * before a mutating tool applies effects or completes workflow output. A cancellation, retry, or
17
+ * replacement session fences the stale caller out.
18
+ *
19
+ * The check does not complete the Task and does not make a following external side effect atomic
20
+ * with Factory state. Pass the returned attempt and execution as the completion fence when writing
21
+ * output.
22
+ *
23
+ * @param stores - Task store used to reread canonical execution state.
24
+ * @param ctx - Eve tool or hook context containing initiator and current authentication.
25
+ * @param requestedTaskId - Task ID from the tool input, required to match the authenticated binding.
26
+ * @returns The currently running Task bound to this exact Eve session and attempt.
27
+ * @throws When authentication is missing or malformed, the requested Task differs, or durable execution was replaced or stopped.
28
+ * @see The shipped `docs/recipes/typed-eve-result.md` recipe for a complete typed tool.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { requireTaskExecution } from "@vercel/factory";
33
+ * import type { FactoryStores } from "@vercel/factory/storage";
34
+ * import type { TaskId } from "@vercel/factory/tasks";
35
+ * import { defineWorkflow } from "@vercel/factory/workflows";
36
+ * import type { ToolContext } from "eve/tools";
37
+ * import { z } from "zod";
38
+ *
39
+ * declare const stores: FactoryStores;
40
+ * declare const ctx: ToolContext;
41
+ * declare const taskId: TaskId;
42
+ * const workflow = defineWorkflow({
43
+ * id: "greeting",
44
+ * version: 1,
45
+ * input: z.object({ name: z.string() }),
46
+ * output: z.object({ message: z.string() }),
47
+ * });
48
+ *
49
+ * const task = await requireTaskExecution(stores, ctx, taskId);
50
+ * if (task.execution?.provider !== "eve") throw new Error("Expected Eve execution");
51
+ * await stores.work.completeWorkflow({
52
+ * task: { taskId: task.id, attempt: task.attempt },
53
+ * workflow: workflow.binding,
54
+ * output: { message: "Hello" },
55
+ * fence: { from: "running", execution: task.execution },
56
+ * });
57
+ * ```
58
+ */
59
+ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx: {
60
+ session: Pick<SessionContext["session"], "id" | "auth">;
61
+ }, requestedTaskId: TaskId): Promise<({
62
+ id: `task_${string}`;
63
+ schemaVersion: 1;
64
+ replyTo: {
65
+ channel: string;
66
+ address: string;
67
+ };
68
+ attempt: number;
69
+ updatedAt: string;
70
+ repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
71
+ kind: TaskKind;
72
+ model?: string | undefined;
73
+ area?: string | undefined;
74
+ approval?: "required" | "granted" | "denied" | undefined;
75
+ origin: {
76
+ signalId?: `sig_${string}` | undefined;
77
+ operator?: string | undefined;
78
+ };
79
+ work: {
80
+ title: string;
81
+ input: JsonValue;
82
+ completionCriteria: readonly string[];
83
+ workflow: {
84
+ id: string;
85
+ version: number;
86
+ } & import("zod").$brand<"WorkflowBinding">;
87
+ route?: ({
88
+ id: string;
89
+ version: number;
90
+ } & import("zod").$brand<"AgentRouteBinding">) | undefined;
91
+ };
92
+ workflowPhase?: string | undefined;
93
+ rootTaskId: `task_${string}`;
94
+ pendingCommunicationReceiptIds?: readonly `rcpt_${string}`[] | undefined;
95
+ execution?: {
96
+ provider: string;
97
+ sessionId: string;
98
+ environmentId?: string | undefined;
99
+ harness?: string | undefined;
100
+ traceId?: string | undefined;
101
+ } | undefined;
102
+ lastProgressAt?: string | undefined;
103
+ reservedUsd?: number | undefined;
104
+ reservedAt?: string | undefined;
105
+ usage?: {
106
+ costUsd: number;
107
+ inputTokens: number;
108
+ outputTokens: number;
109
+ cacheReadTokens: number;
110
+ cacheWriteTokens: number;
111
+ steps: Record<string, number>;
112
+ } | undefined;
113
+ settledUsd?: number | undefined;
114
+ answerEventIds?: readonly `ans_${string}`[] | undefined;
115
+ changeId?: `chg_${string}` | undefined;
116
+ reportRef?: string | undefined;
117
+ createdAt: string;
118
+ } & {
119
+ stalledAt?: never;
120
+ stalledReason?: never;
121
+ } & {
122
+ failureReceiptId?: never;
123
+ } & {
124
+ state: "running";
125
+ waitingForMessages?: boolean;
126
+ needsHumanAction?: never;
127
+ needsHumanNotifiedAt?: never;
128
+ workResult?: {
129
+ output: JsonValue;
130
+ completedAt: string;
131
+ };
132
+ } & {
133
+ question?: never;
134
+ questionTaskId?: never;
135
+ parentTaskAttempt?: never;
136
+ parentTaskId?: `task_${string}`;
137
+ }) | ({
138
+ id: `task_${string}`;
139
+ schemaVersion: 1;
140
+ replyTo: {
141
+ channel: string;
142
+ address: string;
143
+ };
144
+ attempt: number;
145
+ updatedAt: string;
146
+ repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
147
+ kind: TaskKind;
148
+ model?: string | undefined;
149
+ area?: string | undefined;
150
+ approval?: "required" | "granted" | "denied" | undefined;
151
+ origin: {
152
+ signalId?: `sig_${string}` | undefined;
153
+ operator?: string | undefined;
154
+ };
155
+ work: {
156
+ title: string;
157
+ input: JsonValue;
158
+ completionCriteria: readonly string[];
159
+ workflow: {
160
+ id: string;
161
+ version: number;
162
+ } & import("zod").$brand<"WorkflowBinding">;
163
+ route?: ({
164
+ id: string;
165
+ version: number;
166
+ } & import("zod").$brand<"AgentRouteBinding">) | undefined;
167
+ };
168
+ workflowPhase?: string | undefined;
169
+ rootTaskId: `task_${string}`;
170
+ pendingCommunicationReceiptIds?: readonly `rcpt_${string}`[] | undefined;
171
+ execution?: {
172
+ provider: string;
173
+ sessionId: string;
174
+ environmentId?: string | undefined;
175
+ harness?: string | undefined;
176
+ traceId?: string | undefined;
177
+ } | undefined;
178
+ lastProgressAt?: string | undefined;
179
+ reservedUsd?: number | undefined;
180
+ reservedAt?: string | undefined;
181
+ usage?: {
182
+ costUsd: number;
183
+ inputTokens: number;
184
+ outputTokens: number;
185
+ cacheReadTokens: number;
186
+ cacheWriteTokens: number;
187
+ steps: Record<string, number>;
188
+ } | undefined;
189
+ settledUsd?: number | undefined;
190
+ answerEventIds?: readonly `ans_${string}`[] | undefined;
191
+ changeId?: `chg_${string}` | undefined;
192
+ reportRef?: string | undefined;
193
+ createdAt: string;
194
+ } & {
195
+ stalledAt?: never;
196
+ stalledReason?: never;
197
+ } & {
198
+ failureReceiptId?: never;
199
+ } & {
200
+ state: "running";
201
+ waitingForMessages?: boolean;
202
+ needsHumanAction?: never;
203
+ needsHumanNotifiedAt?: never;
204
+ workResult?: {
205
+ output: JsonValue;
206
+ completedAt: string;
207
+ };
208
+ } & {
209
+ question: {
210
+ text: string;
211
+ options: readonly string[];
212
+ closingOptions?: readonly string[] | undefined;
213
+ answer?: string | undefined;
214
+ answeredBy?: string | undefined;
215
+ };
216
+ questionTaskId?: never;
217
+ parentTaskAttempt?: never;
218
+ parentTaskId?: `task_${string}`;
219
+ }) | ({
220
+ id: `task_${string}`;
221
+ schemaVersion: 1;
222
+ replyTo: {
223
+ channel: string;
224
+ address: string;
225
+ };
226
+ attempt: number;
227
+ updatedAt: string;
228
+ repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
229
+ kind: TaskKind;
230
+ model?: string | undefined;
231
+ area?: string | undefined;
232
+ approval?: "required" | "granted" | "denied" | undefined;
233
+ origin: {
234
+ signalId?: `sig_${string}` | undefined;
235
+ operator?: string | undefined;
236
+ };
237
+ work: {
238
+ title: string;
239
+ input: JsonValue;
240
+ completionCriteria: readonly string[];
241
+ workflow: {
242
+ id: string;
243
+ version: number;
244
+ } & import("zod").$brand<"WorkflowBinding">;
245
+ route?: ({
246
+ id: string;
247
+ version: number;
248
+ } & import("zod").$brand<"AgentRouteBinding">) | undefined;
249
+ };
250
+ workflowPhase?: string | undefined;
251
+ rootTaskId: `task_${string}`;
252
+ pendingCommunicationReceiptIds?: readonly `rcpt_${string}`[] | undefined;
253
+ execution?: {
254
+ provider: string;
255
+ sessionId: string;
256
+ environmentId?: string | undefined;
257
+ harness?: string | undefined;
258
+ traceId?: string | undefined;
259
+ } | undefined;
260
+ lastProgressAt?: string | undefined;
261
+ reservedUsd?: number | undefined;
262
+ reservedAt?: string | undefined;
263
+ usage?: {
264
+ costUsd: number;
265
+ inputTokens: number;
266
+ outputTokens: number;
267
+ cacheReadTokens: number;
268
+ cacheWriteTokens: number;
269
+ steps: Record<string, number>;
270
+ } | undefined;
271
+ settledUsd?: number | undefined;
272
+ answerEventIds?: readonly `ans_${string}`[] | undefined;
273
+ changeId?: `chg_${string}` | undefined;
274
+ reportRef?: string | undefined;
275
+ createdAt: string;
276
+ } & {
277
+ stalledAt?: never;
278
+ stalledReason?: never;
279
+ } & {
280
+ failureReceiptId?: never;
281
+ } & {
282
+ state: "running";
283
+ waitingForMessages?: boolean;
284
+ needsHumanAction?: never;
285
+ needsHumanNotifiedAt?: never;
286
+ workResult?: {
287
+ output: JsonValue;
288
+ completedAt: string;
289
+ };
290
+ } & {
291
+ question: {
292
+ text: string;
293
+ options: readonly string[];
294
+ closingOptions?: readonly string[] | undefined;
295
+ answer?: string | undefined;
296
+ answeredBy?: string | undefined;
297
+ };
298
+ questionTaskId?: never;
299
+ parentTaskAttempt: number;
300
+ parentTaskId: `task_${string}`;
301
+ }) | ({
302
+ id: `task_${string}`;
303
+ schemaVersion: 1;
304
+ replyTo: {
305
+ channel: string;
306
+ address: string;
307
+ };
308
+ attempt: number;
309
+ updatedAt: string;
310
+ repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
311
+ kind: TaskKind;
312
+ model?: string | undefined;
313
+ area?: string | undefined;
314
+ approval?: "required" | "granted" | "denied" | undefined;
315
+ origin: {
316
+ signalId?: `sig_${string}` | undefined;
317
+ operator?: string | undefined;
318
+ };
319
+ work: {
320
+ title: string;
321
+ input: JsonValue;
322
+ completionCriteria: readonly string[];
323
+ workflow: {
324
+ id: string;
325
+ version: number;
326
+ } & import("zod").$brand<"WorkflowBinding">;
327
+ route?: ({
328
+ id: string;
329
+ version: number;
330
+ } & import("zod").$brand<"AgentRouteBinding">) | undefined;
331
+ };
332
+ workflowPhase?: string | undefined;
333
+ rootTaskId: `task_${string}`;
334
+ pendingCommunicationReceiptIds?: readonly `rcpt_${string}`[] | undefined;
335
+ execution?: {
336
+ provider: string;
337
+ sessionId: string;
338
+ environmentId?: string | undefined;
339
+ harness?: string | undefined;
340
+ traceId?: string | undefined;
341
+ } | undefined;
342
+ lastProgressAt?: string | undefined;
343
+ reservedUsd?: number | undefined;
344
+ reservedAt?: string | undefined;
345
+ usage?: {
346
+ costUsd: number;
347
+ inputTokens: number;
348
+ outputTokens: number;
349
+ cacheReadTokens: number;
350
+ cacheWriteTokens: number;
351
+ steps: Record<string, number>;
352
+ } | undefined;
353
+ settledUsd?: number | undefined;
354
+ answerEventIds?: readonly `ans_${string}`[] | undefined;
355
+ changeId?: `chg_${string}` | undefined;
356
+ reportRef?: string | undefined;
357
+ createdAt: string;
358
+ } & {
359
+ stalledAt?: never;
360
+ stalledReason?: never;
361
+ } & {
362
+ failureReceiptId?: never;
363
+ } & {
364
+ state: "running";
365
+ waitingForMessages?: boolean;
366
+ needsHumanAction?: never;
367
+ needsHumanNotifiedAt?: never;
368
+ workResult?: {
369
+ output: JsonValue;
370
+ completedAt: string;
371
+ };
372
+ } & {
373
+ question?: never;
374
+ questionTaskId: `task_${string}`;
375
+ parentTaskAttempt?: never;
376
+ parentTaskId?: `task_${string}`;
377
+ })>;
378
+ //#endregion
379
+ export { requireTaskExecution };
380
+ //# sourceMappingURL=task-execution.d.mts.map