agents 0.22.0 → 0.23.0

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 (186) hide show
  1. package/README.md +20 -1
  2. package/dist/{agent-routing-CnkaHb-v.d.ts → agent-routing-DE5zmCQ8.d.ts} +1038 -394
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +26 -26
  5. package/dist/{agent-tools-C0XJqWzB.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
  6. package/dist/agent-tools.d.ts +21 -21
  7. package/dist/browser/ai.js +4 -1
  8. package/dist/browser/ai.js.map +1 -1
  9. package/dist/browser/tanstack-ai.js.map +1 -1
  10. package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
  11. package/dist/callable-decorator.d.ts +18 -0
  12. package/dist/callable-decorator.js +71 -0
  13. package/dist/callable-decorator.js.map +1 -0
  14. package/dist/capability-BjSKYpzg.js.map +1 -1
  15. package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
  16. package/dist/channel-Bnm4S7T2.d.ts +491 -0
  17. package/dist/channels/ai-sdk.d.ts +46 -0
  18. package/dist/channels/ai-sdk.js +120 -0
  19. package/dist/channels/ai-sdk.js.map +1 -0
  20. package/dist/channels/email.d.ts +95 -0
  21. package/dist/channels/email.js +323 -0
  22. package/dist/channels/email.js.map +1 -0
  23. package/dist/channels/index.d.ts +233 -0
  24. package/dist/channels/index.js +608 -0
  25. package/dist/channels/index.js.map +1 -0
  26. package/dist/channels/slack.d.ts +140 -0
  27. package/dist/channels/slack.js +614 -0
  28. package/dist/channels/slack.js.map +1 -0
  29. package/dist/channels/tanstack-ai.d.ts +39 -0
  30. package/dist/channels/tanstack-ai.js +17 -0
  31. package/dist/channels/tanstack-ai.js.map +1 -0
  32. package/dist/channels/telegram.d.ts +106 -0
  33. package/dist/channels/telegram.js +427 -0
  34. package/dist/channels/telegram.js.map +1 -0
  35. package/dist/channels/voice.d.ts +45 -0
  36. package/dist/channels/voice.js +122 -0
  37. package/dist/channels/voice.js.map +1 -0
  38. package/dist/chat/index.d.ts +2325 -2019
  39. package/dist/chat/index.js +888 -518
  40. package/dist/chat/index.js.map +1 -1
  41. package/dist/chat-sdk/index.d.ts +7 -7
  42. package/dist/chat-sdk/index.js +1 -1
  43. package/dist/client.d.ts +1 -1
  44. package/dist/context/index.d.ts +216 -0
  45. package/dist/context/index.js +454 -0
  46. package/dist/context/index.js.map +1 -0
  47. package/dist/{current-agent-CuMErtly.d.ts → current-agent-Da_C9a3b.d.ts} +90 -107
  48. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  49. package/dist/{diagnostics-CaBjfz4J.js → diagnostics-BzvaX2UT.js} +5 -1
  50. package/dist/diagnostics-BzvaX2UT.js.map +1 -0
  51. package/dist/diagnostics-C4jcz3VK.js +360 -0
  52. package/dist/diagnostics-C4jcz3VK.js.map +1 -0
  53. package/dist/index-BB0kqhIz.d.ts +101 -0
  54. package/dist/index-BVVgDSdq.d.ts +1 -0
  55. package/dist/index-XDkuQ7zm.d.ts +89 -0
  56. package/dist/{index-DcSAZKsB.d.ts → index-YSKgfgg9.d.ts} +3 -1
  57. package/dist/index.d.ts +91 -82
  58. package/dist/index.js +3 -2
  59. package/dist/ingress-BfetZbMO.js +83 -0
  60. package/dist/ingress-BfetZbMO.js.map +1 -0
  61. package/dist/internal-CYlgHl1l.js +59 -0
  62. package/dist/internal-CYlgHl1l.js.map +1 -0
  63. package/dist/internal_context.d.ts +1 -1
  64. package/dist/lifecycle/index.d.ts +35 -17
  65. package/dist/lifecycle/index.js +1 -1
  66. package/dist/lifecycle-CMRGjZdw.js +1299 -0
  67. package/dist/lifecycle-CMRGjZdw.js.map +1 -0
  68. package/dist/mcp/client/index.d.ts +20 -20
  69. package/dist/mcp/index.d.ts +35 -35
  70. package/dist/mcp/index.js +1 -1
  71. package/dist/observability/index.d.ts +1 -1
  72. package/dist/observability/index.js +1 -1
  73. package/dist/react.d.ts +4 -4
  74. package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
  75. package/dist/retries.d.ts +8 -6
  76. package/dist/retries.js +13 -1
  77. package/dist/retries.js.map +1 -1
  78. package/dist/routing/index.d.ts +137 -0
  79. package/dist/routing/index.js +244 -0
  80. package/dist/routing/index.js.map +1 -0
  81. package/dist/sanitize-D9TujEK8.js +79 -0
  82. package/dist/sanitize-D9TujEK8.js.map +1 -0
  83. package/dist/scheduler-DD9NdYbF.js +665 -0
  84. package/dist/scheduler-DD9NdYbF.js.map +1 -0
  85. package/dist/{scheduler-DQoTGoAW.d.ts → scheduler-Dwh85ZGl.d.ts} +21 -22
  86. package/dist/schedules/index.d.ts +1 -1
  87. package/dist/schedules/index.js +1 -1
  88. package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
  89. package/dist/serializable.d.ts +1 -1
  90. package/dist/sessions/index.d.ts +441 -0
  91. package/dist/sessions/index.js +2063 -0
  92. package/dist/sessions/index.js.map +1 -0
  93. package/dist/skills/index.d.ts +99 -0
  94. package/dist/skills/index.js +254 -5
  95. package/dist/skills/index.js.map +1 -1
  96. package/dist/{src-5W6JNKVb.js → src-DlSHshb2.js} +1460 -1110
  97. package/dist/src-DlSHshb2.js.map +1 -0
  98. package/dist/streams/index.d.ts +120 -0
  99. package/dist/streams/index.js +107 -0
  100. package/dist/streams/index.js.map +1 -0
  101. package/dist/streams-D6tJ0NN9.d.ts +370 -0
  102. package/dist/streams-DZKgAj9b.js +709 -0
  103. package/dist/streams-DZKgAj9b.js.map +1 -0
  104. package/dist/sub-routing.d.ts +12 -12
  105. package/dist/surface-bZZJqBka.js +17 -0
  106. package/dist/surface-bZZJqBka.js.map +1 -0
  107. package/dist/tasks/index.d.ts +64 -0
  108. package/dist/tasks/index.js +2 -0
  109. package/dist/tasks-BRJ5zgya.d.ts +517 -0
  110. package/dist/tasks-ylZgBjhj.js +1656 -0
  111. package/dist/tasks-ylZgBjhj.js.map +1 -0
  112. package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
  113. package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
  114. package/dist/text-stream-CpdiKrJB.js +272 -0
  115. package/dist/text-stream-CpdiKrJB.js.map +1 -0
  116. package/dist/tokens-nHAKcN6M.js +52 -0
  117. package/dist/tokens-nHAKcN6M.js.map +1 -0
  118. package/dist/tool-schema-CBjGPrsQ.js +31 -0
  119. package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
  120. package/dist/types-B7LojTe4.d.ts +202 -0
  121. package/dist/types-_Faxb570.d.ts +439 -0
  122. package/dist/voice/client.d.ts +226 -0
  123. package/dist/voice/client.js +932 -0
  124. package/dist/voice/client.js.map +1 -0
  125. package/dist/voice/errors.d.ts +43 -0
  126. package/dist/voice/errors.js +41 -0
  127. package/dist/voice/errors.js.map +1 -0
  128. package/dist/voice/index.d.ts +271 -0
  129. package/dist/voice/index.js +1812 -0
  130. package/dist/voice/index.js.map +1 -0
  131. package/dist/voice/react.d.ts +167 -0
  132. package/dist/voice/react.js +234 -0
  133. package/dist/voice/react.js.map +1 -0
  134. package/dist/voice/sfu.d.ts +71 -0
  135. package/dist/voice/sfu.js +157 -0
  136. package/dist/voice/sfu.js.map +1 -0
  137. package/dist/voice/text.d.ts +6 -0
  138. package/dist/voice/text.js +2 -0
  139. package/dist/voice/types.d.ts +58 -0
  140. package/dist/voice/types.js +18 -0
  141. package/dist/voice/types.js.map +1 -0
  142. package/dist/voice/workers-ai.d.ts +136 -0
  143. package/dist/voice/workers-ai.js +568 -0
  144. package/dist/voice/workers-ai.js.map +1 -0
  145. package/dist/websockets/index.d.ts +192 -0
  146. package/dist/websockets/index.js +2 -0
  147. package/dist/websockets-DUfRHPRq.js +502 -0
  148. package/dist/websockets-DUfRHPRq.js.map +1 -0
  149. package/dist/workflow-types.d.ts +25 -25
  150. package/dist/workflows.d.ts +21 -21
  151. package/dist/workflows.js +1 -1
  152. package/docs/agent-class.md +2 -2
  153. package/docs/agent-tools.md +2 -1
  154. package/docs/channels.md +323 -0
  155. package/docs/chat-agents.md +6 -13
  156. package/docs/context.md +131 -0
  157. package/docs/index.md +15 -12
  158. package/docs/lifecycle.md +102 -55
  159. package/docs/long-running-agents.md +2 -2
  160. package/docs/mcp-servers.md +5 -1
  161. package/docs/resumable-streaming.md +1 -1
  162. package/docs/routing.md +105 -0
  163. package/docs/sessions.md +237 -871
  164. package/docs/streams.md +213 -0
  165. package/docs/sub-agents.md +184 -124
  166. package/docs/tasks.md +246 -0
  167. package/docs/voice.md +745 -0
  168. package/package.json +115 -13
  169. package/dist/capability-runner-CvHGZqUu.d.ts +0 -150
  170. package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
  171. package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
  172. package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
  173. package/dist/diagnostics-CaBjfz4J.js.map +0 -1
  174. package/dist/durable-object-lifecycle-D6nNQJJd.js +0 -862
  175. package/dist/durable-object-lifecycle-D6nNQJJd.js.map +0 -1
  176. package/dist/experimental/memory/session/index.d.ts +0 -671
  177. package/dist/experimental/memory/session/index.js +0 -2379
  178. package/dist/experimental/memory/session/index.js.map +0 -1
  179. package/dist/experimental/memory/utils/index.d.ts +0 -96
  180. package/dist/experimental/memory/utils/index.js +0 -79
  181. package/dist/experimental/memory/utils/index.js.map +0 -1
  182. package/dist/scheduler-CR9RHGos.js +0 -857
  183. package/dist/scheduler-CR9RHGos.js.map +0 -1
  184. package/dist/src-5W6JNKVb.js.map +0 -1
  185. package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
  186. package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
@@ -0,0 +1,1656 @@
1
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
2
+ import { t as LifecycleCapability } from "./capability-BjSKYpzg.js";
3
+ import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
4
+ import { isDurableObjectCodeUpdateReset, isDurableObjectMemoryLimitReset, isPlatformFailure } from "./retries.js";
5
+ import { SqlError } from "./sql-error.js";
6
+ import { nanoid } from "nanoid";
7
+ //#region src/tasks/errors.ts
8
+ /**
9
+ * Error classes for the Tasks capability. Each carries a stable `name` so
10
+ * hosts and tests can classify failures without depending on message text.
11
+ */
12
+ /**
13
+ * Thrown by a step callback to fail its run immediately, skipping any
14
+ * remaining retry attempts.
15
+ *
16
+ * Errors named `"NonRetryableError"` from other sources (for example
17
+ * `cloudflare:workflows`) are honored the same way.
18
+ *
19
+ * @experimental The API surface may change before stabilizing.
20
+ */
21
+ var NonRetryableError = class extends Error {
22
+ constructor(message, options) {
23
+ super(message, options);
24
+ this.name = "NonRetryableError";
25
+ }
26
+ };
27
+ /** True when an error should skip remaining step retry attempts. */
28
+ function isNonRetryableError(error) {
29
+ return error instanceof NonRetryableError || error instanceof Error && error.name === "NonRetryableError";
30
+ }
31
+ /**
32
+ * Thrown before executing user code when one replay uses the same step name
33
+ * twice. Step names are durable journal keys and must be unique within a run.
34
+ *
35
+ * @experimental The API surface may change before stabilizing.
36
+ */
37
+ var DuplicateTaskStepError = class extends Error {
38
+ constructor(stepName) {
39
+ super(`Step name "${stepName}" was already used in this run. Step names are durable journal keys; suffix loop steps with a stable index, e.g. "${stepName}:0".`);
40
+ this.name = "DuplicateTaskStepError";
41
+ this.stepName = stepName;
42
+ }
43
+ };
44
+ /**
45
+ * Thrown when a replay observes a journal that this handler code cannot have
46
+ * written — a known step under a different kind, for example. The run fails
47
+ * rather than guessing; changing a definition's step layout for in-flight
48
+ * runs requires versioning the definition name.
49
+ *
50
+ * @experimental The API surface may change before stabilizing.
51
+ */
52
+ var TaskReplayDivergedError = class extends Error {
53
+ constructor(stepName, detail) {
54
+ super(`Replay diverged from the journal at step "${stepName}": ${detail}. Version the definition name (e.g. "name@v2") instead of changing the step layout of in-flight runs.`);
55
+ this.name = "TaskReplayDivergedError";
56
+ this.stepName = stepName;
57
+ }
58
+ };
59
+ /**
60
+ * Recorded against a run whose persisted definition name is no longer
61
+ * registered after a deployment. The run fails visibly; it is never silently
62
+ * deleted and never replayed against a different handler.
63
+ *
64
+ * @experimental The API surface may change before stabilizing.
65
+ */
66
+ var MissingTaskDefinitionError = class extends Error {
67
+ constructor(definition) {
68
+ super(`No Task definition named "${definition}" is registered. A deployment removed or renamed it while this run was active. Re-register the definition (or a versioned successor with the same name) to let the run finish.`);
69
+ this.name = "MissingTaskDefinitionError";
70
+ this.definition = definition;
71
+ }
72
+ };
73
+ /**
74
+ * Thrown when a Task input, step result, metadata value, or final result is
75
+ * not JSON-serializable or exceeds the serialized size limit.
76
+ *
77
+ * @experimental The API surface may change before stabilizing.
78
+ */
79
+ var TaskSerializationError = class extends Error {
80
+ constructor(context, detail) {
81
+ super(`Cannot serialize ${context}: ${detail}`);
82
+ this.name = "TaskSerializationError";
83
+ }
84
+ };
85
+ //#endregion
86
+ //#region src/tasks/serialization.ts
87
+ /**
88
+ * Value serialization for the Tasks capability.
89
+ *
90
+ * Task inputs, step results, metadata, and final results persist as JSON
91
+ * text in SQLite. `undefined` (and a `void` handler result) is represented
92
+ * as SQL `NULL` rather than a JSON envelope, so the JSON column space stays
93
+ * plain: `"null"` is JSON `null`, column `NULL` is `undefined`.
94
+ */
95
+ /** Default ceiling for one serialized value (1 MiB). */
96
+ const MAX_SERIALIZED_BYTES = 1048576;
97
+ const utf8 = new TextEncoder();
98
+ /**
99
+ * Serialize one Task value for storage.
100
+ *
101
+ * @param value - The value to persist.
102
+ * @param context - What is being serialized, for error messages
103
+ * (e.g. `input for definition "report"`, `result of step "fetch"`).
104
+ * @returns JSON text, or `null` when the value is `undefined`.
105
+ * @throws TaskSerializationError when the value is not JSON-serializable or
106
+ * its serialized form exceeds {@link MAX_SERIALIZED_BYTES}.
107
+ */
108
+ function serializeTaskValue(value, context) {
109
+ if (value === void 0) return null;
110
+ let json;
111
+ try {
112
+ json = JSON.stringify(value);
113
+ } catch (error) {
114
+ throw new TaskSerializationError(context, error instanceof Error ? error.message : String(error));
115
+ }
116
+ if (json === void 0) throw new TaskSerializationError(context, `value of type ${typeof value} has no JSON representation`);
117
+ const bytes = utf8.encode(json).byteLength;
118
+ if (bytes > 1048576) throw new TaskSerializationError(context, `serialized size ${bytes} bytes exceeds the ${MAX_SERIALIZED_BYTES}-byte limit`);
119
+ return json;
120
+ }
121
+ /**
122
+ * Restore a value serialized by {@link serializeTaskValue}.
123
+ *
124
+ * @param stored - The stored column value.
125
+ * @returns The original value; column `NULL` restores `undefined`.
126
+ */
127
+ function deserializeTaskValue(stored) {
128
+ if (stored === null) return void 0;
129
+ return JSON.parse(stored);
130
+ }
131
+ //#endregion
132
+ //#region src/tasks/store.ts
133
+ /**
134
+ * Storage layer for the Tasks capability: owns the `cf_agents_task_runs` and
135
+ * `cf_agents_task_steps` tables — DDL, row access, generation-fenced writes,
136
+ * and the snapshot projection. The engine in `tasks.ts` holds the state
137
+ * machine; every byte that touches SQLite goes through here.
138
+ */
139
+ var _storage = /* @__PURE__ */ new WeakMap();
140
+ /** @internal SQL-backed store for one Tasks capability instance. */
141
+ var TaskStore = class {
142
+ constructor(storage) {
143
+ _classPrivateFieldInitSpec(this, _storage, void 0);
144
+ _classPrivateFieldSet2(_storage, this, storage);
145
+ }
146
+ sql(strings, ...values) {
147
+ const query = strings.reduce((result, part, index) => result + part + (index < values.length ? "?" : ""), "");
148
+ try {
149
+ return [..._classPrivateFieldGet2(_storage, this).sql.exec(query, ...values)];
150
+ } catch (cause) {
151
+ throw new SqlError(query, cause);
152
+ }
153
+ }
154
+ write(query, params) {
155
+ try {
156
+ return _classPrivateFieldGet2(_storage, this).sql.exec(query, ...params).rowsWritten;
157
+ } catch (cause) {
158
+ throw new SqlError(query, cause);
159
+ }
160
+ }
161
+ /**
162
+ * Run one generation-fenced run mutation. Returns false when the fence
163
+ * rejected it because another attempt superseded this one.
164
+ */
165
+ fencedWrite(runId, generation, query, leadingParams) {
166
+ try {
167
+ return _classPrivateFieldGet2(_storage, this).sql.exec(query, ...leadingParams, runId, generation).rowsWritten > 0;
168
+ } catch (cause) {
169
+ throw new SqlError(query, cause);
170
+ }
171
+ }
172
+ getRun(runId) {
173
+ return this.sql`
174
+ SELECT * FROM cf_agents_task_runs WHERE run_id = ${runId}
175
+ `[0];
176
+ }
177
+ getRunByKey(idempotencyKey) {
178
+ return this.sql`
179
+ SELECT * FROM cf_agents_task_runs WHERE idempotency_key = ${idempotencyKey}
180
+ `[0];
181
+ }
182
+ deleteRun(runId) {
183
+ this.sql`DELETE FROM cf_agents_task_steps WHERE run_id = ${runId}`;
184
+ this.sql`DELETE FROM cf_agents_task_runs WHERE run_id = ${runId}`;
185
+ }
186
+ ensureTables() {
187
+ const rawSql = (query) => {
188
+ try {
189
+ _classPrivateFieldGet2(_storage, this).sql.exec(query);
190
+ } catch (cause) {
191
+ throw new SqlError(query, cause);
192
+ }
193
+ };
194
+ rawSql(`
195
+ CREATE TABLE IF NOT EXISTS cf_agents_task_runs (
196
+ run_id TEXT PRIMARY KEY,
197
+ definition TEXT NOT NULL,
198
+ input TEXT,
199
+ state TEXT NOT NULL CHECK (state IN (
200
+ 'pending', 'running', 'waiting',
201
+ 'completed', 'failed', 'cancelled'
202
+ )),
203
+ result TEXT,
204
+ error_name TEXT,
205
+ error_message TEXT,
206
+ status_message TEXT,
207
+ metadata TEXT,
208
+ idempotency_key TEXT UNIQUE,
209
+ retain INTEGER NOT NULL DEFAULT 1,
210
+ attempt INTEGER NOT NULL DEFAULT 0,
211
+ generation TEXT,
212
+ next_at INTEGER,
213
+ wait_reason TEXT,
214
+ cancel_requested INTEGER NOT NULL DEFAULT 0,
215
+ cancel_reason TEXT,
216
+ created_at INTEGER NOT NULL,
217
+ started_at INTEGER,
218
+ updated_at INTEGER NOT NULL,
219
+ settled_at INTEGER
220
+ ) WITHOUT ROWID`);
221
+ rawSql(`
222
+ CREATE INDEX IF NOT EXISTS cf_agents_task_runs_definition
223
+ ON cf_agents_task_runs (definition, created_at)
224
+ `);
225
+ rawSql(`
226
+ CREATE TABLE IF NOT EXISTS cf_agents_task_steps (
227
+ run_id TEXT NOT NULL,
228
+ step_name TEXT NOT NULL,
229
+ kind TEXT NOT NULL CHECK (kind IN ('do', 'sleep')),
230
+ state TEXT NOT NULL CHECK (state IN (
231
+ 'running', 'waiting', 'completed', 'failed'
232
+ )),
233
+ result TEXT,
234
+ error_name TEXT,
235
+ error_message TEXT,
236
+ attempt INTEGER NOT NULL DEFAULT 0,
237
+ next_at INTEGER,
238
+ created_at INTEGER NOT NULL,
239
+ started_at INTEGER,
240
+ updated_at INTEGER NOT NULL,
241
+ completed_at INTEGER,
242
+ PRIMARY KEY (run_id, step_name)
243
+ ) WITHOUT ROWID`);
244
+ }
245
+ rowToSnapshot(row) {
246
+ const metadata = row.metadata !== null ? JSON.parse(row.metadata) : void 0;
247
+ const base = {
248
+ runId: row.run_id,
249
+ definition: row.definition,
250
+ createdAt: row.created_at,
251
+ ...metadata !== void 0 ? { metadata } : {}
252
+ };
253
+ switch (row.state) {
254
+ case "pending": return {
255
+ ...base,
256
+ state: "pending"
257
+ };
258
+ case "running": return {
259
+ ...base,
260
+ state: "running",
261
+ attempt: row.attempt,
262
+ startedAt: row.started_at ?? row.created_at,
263
+ ...row.status_message !== null ? { statusMessage: row.status_message } : {}
264
+ };
265
+ case "waiting": return {
266
+ ...base,
267
+ state: "waiting",
268
+ reason: row.wait_reason ?? "sleep",
269
+ wakeAt: row.next_at ?? row.updated_at,
270
+ ...row.status_message !== null ? { statusMessage: row.status_message } : {}
271
+ };
272
+ case "completed": return {
273
+ ...base,
274
+ state: "completed",
275
+ result: deserializeTaskValue(row.result),
276
+ settledAt: row.settled_at ?? row.updated_at
277
+ };
278
+ case "failed": return {
279
+ ...base,
280
+ state: "failed",
281
+ error: {
282
+ name: row.error_name ?? "Error",
283
+ message: row.error_message ?? "Task run failed"
284
+ },
285
+ settledAt: row.settled_at ?? row.updated_at
286
+ };
287
+ case "cancelled": return {
288
+ ...base,
289
+ state: "cancelled",
290
+ ...row.cancel_reason !== null ? { reason: row.cancel_reason } : {},
291
+ settledAt: row.settled_at ?? row.updated_at
292
+ };
293
+ }
294
+ }
295
+ };
296
+ //#endregion
297
+ //#region src/tasks/duration.ts
298
+ const UNIT_MILLISECONDS = {
299
+ second: 1e3,
300
+ minute: 60 * 1e3,
301
+ hour: 3600 * 1e3,
302
+ day: 1440 * 60 * 1e3,
303
+ week: 10080 * 60 * 1e3
304
+ };
305
+ const DURATION_PATTERN = /^(\d+(?:\.\d+)?)\s+(second|minute|hour|day|week)s?$/;
306
+ /**
307
+ * Parse a duration into whole milliseconds.
308
+ *
309
+ * @param duration - Milliseconds, or a duration string such as `"10 seconds"`.
310
+ * @param context - Name of the option being parsed, used in error messages.
311
+ * @returns The duration in milliseconds, floored to an integer.
312
+ * @throws Error when the duration is negative, not finite, or unparseable.
313
+ */
314
+ function parseTaskDuration(duration, context) {
315
+ if (typeof duration === "number") {
316
+ if (!Number.isFinite(duration) || duration < 0) throw new Error(`Invalid ${context}: expected a non-negative number of milliseconds, got ${duration}`);
317
+ return Math.floor(duration);
318
+ }
319
+ const match = DURATION_PATTERN.exec(duration.trim());
320
+ if (!match) throw new Error(`Invalid ${context}: expected milliseconds or a duration like "10 seconds", got ${JSON.stringify(duration)}`);
321
+ const amount = Number(match[1]);
322
+ const unit = match[2];
323
+ return Math.floor(amount * UNIT_MILLISECONDS[unit]);
324
+ }
325
+ //#endregion
326
+ //#region src/tasks/replay.ts
327
+ /**
328
+ * Replay step engine for the Tasks capability.
329
+ *
330
+ * `ReplayStep` implements the `TaskStep` surface one handler attempt
331
+ * receives. It owns replay semantics — journal hits, journal misses, retry
332
+ * policy, durable sleeps, the status live gate, and duplicate/divergence
333
+ * detection — while all SQL stays behind the narrow {@link TaskStepEngine}
334
+ * port implemented by the `Tasks` capability, the single owner of the
335
+ * schema.
336
+ */
337
+ /** Longest computed retry delay: backoff growth never exceeds one day. */
338
+ const MAX_RETRY_DELAY_MS = 1440 * 60 * 1e3;
339
+ /** Steps per run ceiling; crossing it fails the run instead of degrading. */
340
+ const MAX_STEPS_PER_RUN = 1e4;
341
+ /**
342
+ * Thrown by the engine to end one execution attempt while its run waits for
343
+ * a durable deadline (sleep or retry). Not an `Error` subclass so a step
344
+ * callback's `catch (error)` around unrelated work is less likely to swallow
345
+ * it; the capability re-checks with {@link isTaskSuspension}.
346
+ */
347
+ var TaskSuspension = class {
348
+ constructor(wakeAt, reason) {
349
+ this.wakeAt = wakeAt;
350
+ this.reason = reason;
351
+ }
352
+ };
353
+ /** True when a thrown value is the engine's suspension signal. */
354
+ function isTaskSuspension(value) {
355
+ return value instanceof TaskSuspension;
356
+ }
357
+ /**
358
+ * Thrown by the engine when a step boundary observes the run's cancellation
359
+ * request. The capability settles the run as cancelled.
360
+ */
361
+ var TaskCancellation = class {
362
+ constructor(reason) {
363
+ this.reason = reason;
364
+ }
365
+ };
366
+ /** True when a thrown value is the engine's cancellation signal. */
367
+ function isTaskCancellation(value) {
368
+ return value instanceof TaskCancellation;
369
+ }
370
+ /**
371
+ * Thrown by engine writes when another execution attempt has superseded this
372
+ * one. The stale attempt unwinds without settling anything; every durable
373
+ * write it might still try is generation-fenced.
374
+ */
375
+ var AttemptSupersededError = class extends Error {
376
+ constructor(runId) {
377
+ super(`Task attempt superseded: run "${runId}" is no longer claimed by this execution attempt`);
378
+ this.name = "AttemptSupersededError";
379
+ }
380
+ };
381
+ /** Compute the delay before the next attempt after `failedAttempt` failed. */
382
+ function computeRetryDelayMs(policy, failedAttempt) {
383
+ const base = policy.retryDelayMs;
384
+ let delay;
385
+ switch (policy.backoff) {
386
+ case "constant":
387
+ delay = base;
388
+ break;
389
+ case "linear":
390
+ delay = base * failedAttempt;
391
+ break;
392
+ case "exponential":
393
+ delay = base * 2 ** (failedAttempt - 1);
394
+ break;
395
+ }
396
+ return Math.min(delay, MAX_RETRY_DELAY_MS);
397
+ }
398
+ /** Resolve one `step.do()` config against the capability defaults. */
399
+ function resolveStepPolicy(defaults, config) {
400
+ const limit = config?.retries?.limit ?? defaults.retryLimit;
401
+ if (!Number.isInteger(limit) || limit < 1) throw new Error(`Invalid step retries.limit: expected an integer >= 1, got ${limit}`);
402
+ return {
403
+ retryLimit: limit,
404
+ retryDelayMs: config?.retries?.delay !== void 0 ? parseTaskDuration(config.retries.delay, "step retries.delay") : defaults.retryDelayMs,
405
+ backoff: config?.retries?.backoff ?? defaults.backoff,
406
+ timeoutMs: config?.timeout !== void 0 ? parseTaskDuration(config.timeout, "step timeout") : defaults.timeoutMs
407
+ };
408
+ }
409
+ var _engine = /* @__PURE__ */ new WeakMap();
410
+ var _usedNames = /* @__PURE__ */ new WeakMap();
411
+ var _live = /* @__PURE__ */ new WeakMap();
412
+ var _ReplayStep_brand = /* @__PURE__ */ new WeakSet();
413
+ /**
414
+ * The `TaskStep` implementation for one execution attempt.
415
+ *
416
+ * Attempt 1 starts live. A later attempt starts silent and becomes live at
417
+ * the frontier of new ground — the first journal miss, or a step still
418
+ * waiting or running — so replayed `status()` calls from completed ground
419
+ * are suppressed instead of re-published as new progress.
420
+ */
421
+ var ReplayStep = class {
422
+ constructor(engine, options) {
423
+ _classPrivateMethodInitSpec(this, _ReplayStep_brand);
424
+ _classPrivateFieldInitSpec(this, _engine, void 0);
425
+ _classPrivateFieldInitSpec(this, _usedNames, /* @__PURE__ */ new Set());
426
+ _classPrivateFieldInitSpec(this, _live, void 0);
427
+ _classPrivateFieldSet2(_engine, this, engine);
428
+ _classPrivateFieldSet2(_live, this, options.startsLive);
429
+ this.interrupted = options.interrupted ?? null;
430
+ }
431
+ async do(name, configOrCallback, maybeCallback) {
432
+ const config = typeof configOrCallback === "function" ? void 0 : configOrCallback;
433
+ const callback = typeof configOrCallback === "function" ? configOrCallback : maybeCallback;
434
+ if (typeof callback !== "function") throw new Error(`step.do("${name}") requires a callback`);
435
+ const policy = resolveStepPolicy(_classPrivateFieldGet2(_engine, this).defaults, config);
436
+ _assertClassBrand(_ReplayStep_brand, this, _enterStep).call(this, name);
437
+ const row = _classPrivateFieldGet2(_engine, this).readStep(name);
438
+ if (row === void 0) {
439
+ _classPrivateFieldSet2(_live, this, true);
440
+ if (_classPrivateFieldGet2(_engine, this).countSteps() >= 1e4) throw new Error(`Run exceeded ${MAX_STEPS_PER_RUN} steps; split the work across multiple Task runs`);
441
+ _classPrivateFieldGet2(_engine, this).insertStep(name, "do", null);
442
+ return _assertClassBrand(_ReplayStep_brand, this, _executeAttempt).call(this, name, 1, policy, callback);
443
+ }
444
+ if (row.kind !== "do") throw new TaskReplayDivergedError(name, `journaled as a ${row.kind} step but replayed as a do step`);
445
+ switch (row.state) {
446
+ case "completed": return deserializeTaskValue(row.result);
447
+ case "failed": throw restoreStepError(row);
448
+ case "waiting": {
449
+ _classPrivateFieldSet2(_live, this, true);
450
+ const wakeAt = row.next_at ?? Date.now();
451
+ if (Date.now() < wakeAt) throw new TaskSuspension(wakeAt, "retry");
452
+ const attempt = _classPrivateFieldGet2(_engine, this).claimStepAttempt(name);
453
+ _classPrivateFieldGet2(_engine, this).emit("task:step:retry", {
454
+ step: name,
455
+ attempt
456
+ });
457
+ return _assertClassBrand(_ReplayStep_brand, this, _executeAttempt).call(this, name, attempt, policy, callback);
458
+ }
459
+ case "running": {
460
+ _classPrivateFieldSet2(_live, this, true);
461
+ const attempt = _classPrivateFieldGet2(_engine, this).claimStepAttempt(name);
462
+ return _assertClassBrand(_ReplayStep_brand, this, _executeAttempt).call(this, name, attempt, policy, callback);
463
+ }
464
+ }
465
+ }
466
+ async sleep(name, duration) {
467
+ const durationMs = parseTaskDuration(duration, "sleep duration");
468
+ return _assertClassBrand(_ReplayStep_brand, this, _sleepAt).call(this, name, () => Date.now() + durationMs);
469
+ }
470
+ async sleepUntil(name, when) {
471
+ const wakeAt = when instanceof Date ? when.getTime() : when;
472
+ if (!Number.isFinite(wakeAt)) throw new Error(`Invalid sleepUntil time for step "${name}": ${String(when)}`);
473
+ return _assertClassBrand(_ReplayStep_brand, this, _sleepAt).call(this, name, () => wakeAt);
474
+ }
475
+ async status(message) {
476
+ if (!_classPrivateFieldGet2(_live, this)) return;
477
+ _classPrivateFieldGet2(_engine, this).writeStatus(String(message));
478
+ }
479
+ idempotencyKey(name) {
480
+ return _classPrivateFieldGet2(_engine, this).stepIdempotencyKey(name);
481
+ }
482
+ };
483
+ /** Validate a step boundary: name rules, duplicates, cancellation. */
484
+ function _enterStep(name) {
485
+ if (typeof name !== "string" || name.length === 0) throw new Error("Step names must be non-empty strings");
486
+ if (name.length > 256) throw new Error(`Step name exceeds 256 characters: "${name.slice(0, 40)}…"`);
487
+ if (name.startsWith("__cf")) throw new Error(`Step names must not use the reserved "__cf" prefix`);
488
+ if (_classPrivateFieldGet2(_usedNames, this).has(name)) throw new DuplicateTaskStepError(name);
489
+ _classPrivateFieldGet2(_usedNames, this).add(name);
490
+ const cancellation = _classPrivateFieldGet2(_engine, this).cancellationRequested();
491
+ if (cancellation) throw new TaskCancellation(cancellation.reason);
492
+ }
493
+ /** First persist wins: the recorded wake time is authoritative. */
494
+ async function _sleepAt(name, wakeTime) {
495
+ _assertClassBrand(_ReplayStep_brand, this, _enterStep).call(this, name);
496
+ const row = _classPrivateFieldGet2(_engine, this).readStep(name);
497
+ if (row === void 0) {
498
+ _classPrivateFieldSet2(_live, this, true);
499
+ const wakeAt = wakeTime();
500
+ if (wakeAt <= Date.now()) {
501
+ _classPrivateFieldGet2(_engine, this).insertCompletedSleep(name);
502
+ return;
503
+ }
504
+ _classPrivateFieldGet2(_engine, this).insertStep(name, "sleep", wakeAt);
505
+ throw new TaskSuspension(wakeAt, "sleep");
506
+ }
507
+ if (row.kind !== "sleep") throw new TaskReplayDivergedError(name, `journaled as a ${row.kind} step but replayed as a sleep step`);
508
+ if (row.state === "completed") return;
509
+ _classPrivateFieldSet2(_live, this, true);
510
+ const wakeAt = row.next_at ?? 0;
511
+ if (Date.now() < wakeAt) throw new TaskSuspension(wakeAt, "sleep");
512
+ _classPrivateFieldGet2(_engine, this).completeStep(name, void 0);
513
+ }
514
+ /** Execute one claimed attempt of a `do` step under timeout and retries. */
515
+ async function _executeAttempt(name, attempt, policy, callback) {
516
+ _classPrivateFieldGet2(_engine, this).refreshClaim();
517
+ _classPrivateFieldGet2(_engine, this).emit("task:step:started", {
518
+ step: name,
519
+ attempt
520
+ });
521
+ const timeout = new AbortController();
522
+ const onRunAbort = () => timeout.abort(_classPrivateFieldGet2(_engine, this).attemptSignal.reason);
523
+ _classPrivateFieldGet2(_engine, this).attemptSignal.addEventListener("abort", onRunAbort, { once: true });
524
+ const timer = setTimeout(() => {
525
+ timeout.abort(/* @__PURE__ */ new Error(`Step "${name}" attempt ${attempt} timed out after ${policy.timeoutMs}ms`));
526
+ }, policy.timeoutMs);
527
+ try {
528
+ const result = await _assertClassBrand(_ReplayStep_brand, this, _raceTimeout).call(this, Promise.resolve(callback({
529
+ attempt,
530
+ idempotencyKey: _classPrivateFieldGet2(_engine, this).stepIdempotencyKey(name),
531
+ signal: timeout.signal
532
+ })), timeout.signal);
533
+ _classPrivateFieldGet2(_engine, this).completeStep(name, result);
534
+ _classPrivateFieldGet2(_engine, this).emit("task:step:completed", {
535
+ step: name,
536
+ attempt
537
+ });
538
+ return result;
539
+ } catch (error) {
540
+ if (error instanceof AttemptSupersededError) throw error;
541
+ const cancellation = _classPrivateFieldGet2(_engine, this).cancellationRequested();
542
+ if (cancellation) throw new TaskCancellation(cancellation.reason);
543
+ if (isDurableObjectCodeUpdateReset(error) || isDurableObjectMemoryLimitReset(error)) throw error;
544
+ if (isPlatformFailure(error) && attempt >= policy.retryLimit) throw error;
545
+ if (isNonRetryableError(error) || error instanceof TaskSerializationError || attempt >= policy.retryLimit) {
546
+ _classPrivateFieldGet2(_engine, this).failStep(name, toErrorSummary(error));
547
+ throw error;
548
+ }
549
+ const wakeAt = Date.now() + computeRetryDelayMs(policy, attempt);
550
+ _classPrivateFieldGet2(_engine, this).waitStep(name, wakeAt);
551
+ throw new TaskSuspension(wakeAt, "retry");
552
+ } finally {
553
+ clearTimeout(timer);
554
+ _classPrivateFieldGet2(_engine, this).attemptSignal.removeEventListener("abort", onRunAbort);
555
+ }
556
+ }
557
+ /**
558
+ * Settle with the callback or its timeout, whichever finishes first. A
559
+ * callback that ignores its abort signal cannot wedge the attempt; its
560
+ * late settlement is discarded and generation fencing rejects late writes.
561
+ */
562
+ function _raceTimeout(pending, signal) {
563
+ if (signal.aborted) return Promise.reject(signal.reason);
564
+ return new Promise((resolve, reject) => {
565
+ const onAbort = () => reject(signal.reason);
566
+ signal.addEventListener("abort", onAbort, { once: true });
567
+ pending.then((value) => {
568
+ signal.removeEventListener("abort", onAbort);
569
+ resolve(value);
570
+ }, (error) => {
571
+ signal.removeEventListener("abort", onAbort);
572
+ reject(error);
573
+ });
574
+ });
575
+ }
576
+ /** Rebuild a persisted terminal step error for rethrow. */
577
+ function restoreStepError(row) {
578
+ const error = new Error(row.error_message ?? "Step failed");
579
+ error.name = row.error_name ?? "Error";
580
+ return error;
581
+ }
582
+ /** Safe name/message projection of an arbitrary thrown value. */
583
+ function toErrorSummary(error) {
584
+ if (error instanceof Error) return {
585
+ name: error.name,
586
+ message: error.message
587
+ };
588
+ return {
589
+ name: "Error",
590
+ message: String(error)
591
+ };
592
+ }
593
+ //#endregion
594
+ //#region src/tasks/engine-port.ts
595
+ /**
596
+ * The step-engine port: the storage-side operations `ReplayStep` drives —
597
+ * journal reads and writes, generation fencing, claim refresh, progress —
598
+ * bound to one claimed attempt. `tasks.ts` owns the state machine; this
599
+ * module owns nothing but the port's construction.
600
+ */
601
+ /** @internal Build the engine port for one claimed attempt. */
602
+ function createTaskStepEngine(deps) {
603
+ const { runId, generation } = deps;
604
+ let lastClaimWriteAt = deps.claimedAtMs;
605
+ let lastStatusMessage;
606
+ const assertCurrent = () => {
607
+ const row = deps.store.getRun(runId);
608
+ if (!row || row.generation !== generation) throw new AttemptSupersededError(runId);
609
+ };
610
+ return {
611
+ readStep: (name) => {
612
+ return deps.store.sql`
613
+ SELECT * FROM cf_agents_task_steps
614
+ WHERE run_id = ${runId} AND step_name = ${name}
615
+ `[0];
616
+ },
617
+ countSteps: () => {
618
+ return deps.store.sql`
619
+ SELECT COUNT(*) AS count FROM cf_agents_task_steps WHERE run_id = ${runId}
620
+ `[0]?.count ?? 0;
621
+ },
622
+ insertStep: (name, kind, wakeAt) => {
623
+ assertCurrent();
624
+ const now = Date.now();
625
+ deps.store.sql`
626
+ INSERT INTO cf_agents_task_steps
627
+ (run_id, step_name, kind, state, attempt, next_at, created_at,
628
+ started_at, updated_at)
629
+ VALUES
630
+ (${runId}, ${name}, ${kind},
631
+ ${kind === "do" ? "running" : wakeAt === null ? "running" : "waiting"},
632
+ ${kind === "do" ? 1 : 0}, ${wakeAt},
633
+ ${now}, ${kind === "do" ? now : null}, ${now})
634
+ `;
635
+ },
636
+ insertCompletedSleep: (name) => {
637
+ assertCurrent();
638
+ const now = Date.now();
639
+ deps.store.sql`
640
+ INSERT INTO cf_agents_task_steps
641
+ (run_id, step_name, kind, state, attempt, next_at, created_at,
642
+ completed_at, updated_at)
643
+ VALUES
644
+ (${runId}, ${name}, 'sleep', 'completed', 0, NULL, ${now},
645
+ ${now}, ${now})
646
+ `;
647
+ },
648
+ claimStepAttempt: (name) => {
649
+ assertCurrent();
650
+ const now = Date.now();
651
+ deps.store.sql`
652
+ UPDATE cf_agents_task_steps
653
+ SET state = 'running', attempt = attempt + 1, next_at = NULL,
654
+ started_at = ${now}, updated_at = ${now}
655
+ WHERE run_id = ${runId} AND step_name = ${name}
656
+ `;
657
+ return deps.store.sql`
658
+ SELECT attempt FROM cf_agents_task_steps
659
+ WHERE run_id = ${runId} AND step_name = ${name}
660
+ `[0]?.attempt ?? 1;
661
+ },
662
+ completeStep: (name, result) => {
663
+ assertCurrent();
664
+ const resultJson = serializeTaskValue(result, `result of step "${name}" in run "${runId}"`);
665
+ const now = Date.now();
666
+ deps.store.sql`
667
+ UPDATE cf_agents_task_steps
668
+ SET state = 'completed', result = ${resultJson}, next_at = NULL,
669
+ completed_at = ${now}, updated_at = ${now}
670
+ WHERE run_id = ${runId} AND step_name = ${name}
671
+ `;
672
+ },
673
+ failStep: (name, error) => {
674
+ assertCurrent();
675
+ const now = Date.now();
676
+ deps.store.sql`
677
+ UPDATE cf_agents_task_steps
678
+ SET state = 'failed', error_name = ${error.name},
679
+ error_message = ${error.message}, next_at = NULL, updated_at = ${now}
680
+ WHERE run_id = ${runId} AND step_name = ${name}
681
+ `;
682
+ },
683
+ waitStep: (name, wakeAt) => {
684
+ assertCurrent();
685
+ const now = Date.now();
686
+ deps.store.sql`
687
+ UPDATE cf_agents_task_steps
688
+ SET state = 'waiting', next_at = ${wakeAt}, updated_at = ${now}
689
+ WHERE run_id = ${runId} AND step_name = ${name}
690
+ `;
691
+ },
692
+ refreshClaim: () => {
693
+ const now = Date.now();
694
+ if (now - lastClaimWriteAt < deps.claimRefreshAfterMs) return;
695
+ if (deps.store.fencedWrite(runId, generation, `UPDATE cf_agents_task_runs SET next_at = ?, updated_at = ?
696
+ WHERE run_id = ? AND generation = ? AND state = 'running'`, [now + deps.claimTimeoutMs(), now])) lastClaimWriteAt = now;
697
+ },
698
+ writeStatus: (message) => {
699
+ if (message === lastStatusMessage) return;
700
+ if (deps.store.fencedWrite(runId, generation, `UPDATE cf_agents_task_runs SET status_message = ?, updated_at = ?
701
+ WHERE run_id = ? AND generation = ? AND state = 'running'`, [message, Date.now()])) lastStatusMessage = message;
702
+ },
703
+ cancellationRequested: () => {
704
+ const row = deps.store.getRun(runId);
705
+ if (!row || row.cancel_requested !== 1) return null;
706
+ return { reason: row.cancel_reason ?? void 0 };
707
+ },
708
+ attemptSignal: deps.signal,
709
+ emit: deps.emit,
710
+ stepIdempotencyKey: (name) => `${runId}:${name}`,
711
+ defaults: deps.defaults
712
+ };
713
+ }
714
+ //#endregion
715
+ //#region src/tasks/tasks.ts
716
+ /**
717
+ * Durable replayable execution for Lifecycle Objects. `Tasks` owns the
718
+ * `cf_agents_task_runs` and `cf_agents_task_steps` tables, the definitions registry, run
719
+ * acceptance, generation-fenced claiming, and due-run processing.
720
+ *
721
+ * Tasks consumes only the standard capability services: storage, the job
722
+ * queue, the host invocation boundary, events, and routing. A run's storage
723
+ * and step journal always live where it was accepted; only its deadline
724
+ * mirrors as one Lifecycle queue job, routed to the root Lifecycle when
725
+ * accepted on a routed sub-agent, since only the root owns the physical
726
+ * alarm. Definition handlers run through Lifecycle's host invocation
727
+ * boundary. Interrupted work replays: completed steps return journaled
728
+ * results and handlers resume from durable evidence.
729
+ */
730
+ const taskDefinitionResolvers = /* @__PURE__ */ new WeakMap();
731
+ /**
732
+ * @internal Supply a composition-root fallback for definition names outside
733
+ * the declared map. Frameworks use this to attach internal definitions (for
734
+ * example a future Agent compatibility layer) without occupying the host's
735
+ * constructor map; resolved handlers still run inside the Lifecycle host
736
+ * boundary. The resolver must return the same definition for a name on
737
+ * every Durable Object wake, or that name's in-flight runs cannot resume.
738
+ */
739
+ function setTaskDefinitionResolver(tasks, resolver) {
740
+ taskDefinitionResolvers.set(tasks, resolver);
741
+ }
742
+ const taskRoutedMemoryLimitHandlers = /* @__PURE__ */ new WeakMap();
743
+ /**
744
+ * @internal Supply a composition-root bridge from a routed run's sealed
745
+ * strike to the owning host's own `onAlarmMemoryLimit` hook. A root's own
746
+ * local runs already reach that hook through Lifecycle's alarm dispatch on
747
+ * the same Durable Object; a routed run's owner is a different instance,
748
+ * whose Lifecycle never observes the root's alarm directly.
749
+ */
750
+ function setTaskRoutedMemoryLimitHandler(tasks, handler) {
751
+ taskRoutedMemoryLimitHandlers.set(tasks, handler);
752
+ }
753
+ const FIBER_SCHEMA_VERSION_KEY = "cf_agents:tasks_schema_version";
754
+ const CURRENT_FIBER_SCHEMA_VERSION = 1;
755
+ const DEFAULT_STEP_POLICY = {
756
+ retryLimit: 5,
757
+ retryDelayMs: 1e3,
758
+ backoff: "exponential",
759
+ timeoutMs: 300 * 1e3
760
+ };
761
+ /**
762
+ * Slack added to the default step timeout to form the claim deadline — the
763
+ * durable recovery backstop that wakes the object when a claimed attempt's
764
+ * isolate disappears.
765
+ */
766
+ const CLAIM_SLACK_MS = 3e4;
767
+ const DEFAULT_LIST_LIMIT = 100;
768
+ const MAX_DEFINITION_NAME_LENGTH = 256;
769
+ /**
770
+ * Queue-job id prefix for run wakes. Run IDs are caller-selectable, so the
771
+ * job id namespaces them instead of exposing them verbatim to the shared
772
+ * job id space.
773
+ */
774
+ const WAKE_JOB_PREFIX = "task:";
775
+ /** Normal Task deadline dispatch. */
776
+ const WAKE_JOB_FN = "wake";
777
+ /**
778
+ * A platform failure that escapes an attempt (ReplayStep rethrows once the
779
+ * step's own retry budget is spent) leaves the run claimed with a future
780
+ * `next_at`. An in-driver retry would not re-run the step — `#executeRun`
781
+ * returns at its claim guard — it would only read the claim back as a clean
782
+ * `{ rescheduleAt }`, hiding the failure from the alarm boundary. One
783
+ * attempt keeps JobDriver's platform-failure contract: the wake rejects, the
784
+ * job row is preserved, and the platform re-runs the alarm on a fresh
785
+ * invocation while the claim deadline stays the durable wake.
786
+ */
787
+ const WAKE_JOB_RETRY = { maxAttempts: 1 };
788
+ /**
789
+ * How long one queue-driven attempt may hold the serial dispatch loop
790
+ * before detaching. Correctness never depends on the inline await — the
791
+ * claim backstop owns the durable wake — so this only trades a prompt
792
+ * inline settle for queue liveness.
793
+ */
794
+ const DISPATCH_BUDGET_MS = 5e3;
795
+ const TERMINAL_STATES = /* @__PURE__ */ new Set([
796
+ "completed",
797
+ "failed",
798
+ "cancelled"
799
+ ]);
800
+ function isTaskWakeJobPayload(value) {
801
+ return typeof value === "object" && value !== null && typeof value.runId === "string";
802
+ }
803
+ var _definitions = /* @__PURE__ */ new WeakMap();
804
+ var _registered = /* @__PURE__ */ new WeakMap();
805
+ var _active = /* @__PURE__ */ new WeakMap();
806
+ var _storeInstance = /* @__PURE__ */ new WeakMap();
807
+ var _stepDefaults = /* @__PURE__ */ new WeakMap();
808
+ var _onError = /* @__PURE__ */ new WeakMap();
809
+ var _Tasks_brand = /* @__PURE__ */ new WeakSet();
810
+ /**
811
+ * Durable replayable execution for a Lifecycle Object.
812
+ *
813
+ * Declare named definitions in the constructor and install the instance with
814
+ * `Lifecycle.use()`. The constructor map is the registry: it is rebuilt on
815
+ * every Durable Object wake, so in-flight runs always resolve their
816
+ * persisted definition names. Each definition's handler replays from the
817
+ * beginning on every execution attempt; completed steps return journaled
818
+ * results, sleeps consult persisted deadlines, and interrupted work
819
+ * continues from the first unfinished step after process loss.
820
+ *
821
+ * @experimental The API surface may change before stabilizing.
822
+ */
823
+ var Tasks = class extends LifecycleCapability {
824
+ /**
825
+ * Create a Tasks capability.
826
+ *
827
+ * @param options - Named definitions plus default step retry/timeout
828
+ * policy and alarm batching. Declaring `definitions` types {@link run} and
829
+ * {@link handle} against the map — names and inputs are checked where the
830
+ * handlers are declared and where runs start. Names outside the map are
831
+ * rejected unless a composition-root resolver supplies them.
832
+ */
833
+ constructor(options = {}) {
834
+ super("tasks");
835
+ _classPrivateMethodInitSpec(this, _Tasks_brand);
836
+ _classPrivateFieldInitSpec(this, _definitions, void 0);
837
+ _classPrivateFieldInitSpec(this, _registered, /* @__PURE__ */ new Map());
838
+ _classPrivateFieldInitSpec(this, _active, /* @__PURE__ */ new Map());
839
+ _classPrivateFieldInitSpec(this, _storeInstance, void 0);
840
+ _classPrivateFieldInitSpec(this, _stepDefaults, void 0);
841
+ _classPrivateFieldInitSpec(this, _onError, void 0);
842
+ _classPrivateFieldSet2(_definitions, this, options.definitions ?? {});
843
+ _classPrivateFieldSet2(_stepDefaults, this, {
844
+ retryLimit: options.retries?.limit ?? DEFAULT_STEP_POLICY.retryLimit,
845
+ retryDelayMs: options.retries?.delay !== void 0 ? parseTaskDuration(options.retries.delay, "retries.delay") : DEFAULT_STEP_POLICY.retryDelayMs,
846
+ backoff: options.retries?.backoff ?? DEFAULT_STEP_POLICY.backoff,
847
+ timeoutMs: options.stepTimeout !== void 0 ? parseTaskDuration(options.stepTimeout, "stepTimeout") : DEFAULT_STEP_POLICY.timeoutMs
848
+ });
849
+ _classPrivateFieldSet2(_onError, this, options.onError);
850
+ }
851
+ /**
852
+ * @internal Framework aperture: register one reserved (`__cf`-prefixed)
853
+ * Task definition directly on this instance, bypassing the constructor's
854
+ * `definitions` map so a host's own subclass layers can each declare their
855
+ * own `definitions` / `taskDefinitions` field without colliding with — or
856
+ * being silently clobbered by — a framework's internal names. Call once per
857
+ * name from the owning host's own constructor, unconditionally, so the
858
+ * definition is rebuilt identically on every Durable Object wake: an
859
+ * in-flight run resolves the same handler for its persisted definition name
860
+ * every time, or it cannot resume.
861
+ *
862
+ * Throws if `name` does not carry the reserved `__cf` prefix — this is not
863
+ * a general-purpose registration path; declare ordinary definitions in the
864
+ * constructor's `definitions` map instead — or if `name` is already
865
+ * registered, which is always a real conflict: this method runs exactly
866
+ * once per name per Tasks construction.
867
+ */
868
+ register(name, definition) {
869
+ if (typeof name !== "string" || name.length === 0) throw new Error("Task definition names must be non-empty strings");
870
+ if (name.length > MAX_DEFINITION_NAME_LENGTH) throw new Error(`Task definition name exceeds ${MAX_DEFINITION_NAME_LENGTH} characters`);
871
+ if (!name.startsWith("__cf")) throw new Error(`register() requires a "__cf"-prefixed reserved definition name, got "${name}"`);
872
+ if (Object.hasOwn(_classPrivateFieldGet2(_definitions, this), name) || _classPrivateFieldGet2(_registered, this).has(name)) throw new Error(`Task definition "${name}" is already registered on this Tasks capability`);
873
+ _classPrivateFieldGet2(_registered, this).set(name, definition);
874
+ }
875
+ /**
876
+ * Durably accept one run of a declared definition and return a receipt
877
+ * without waiting for terminal state. The same `idempotencyKey` or `runId`
878
+ * joins the existing run (`accepted: false`) instead of creating a second.
879
+ */
880
+ async run(definition, input, options) {
881
+ _assertClassBrand(_Tasks_brand, this, _validateDefinitionName).call(this, definition);
882
+ return _assertClassBrand(_Tasks_brand, this, _accept).call(this, definition, input, options);
883
+ }
884
+ /**
885
+ * A typed handle scoped to one declared definition: its `run`, `get`,
886
+ * `getByIdempotencyKey`, and `cancel` see only that definition's runs. The
887
+ * handle is a pure lens over this capability — it holds no state and may
888
+ * be created at any time.
889
+ */
890
+ handle(definition) {
891
+ _assertClassBrand(_Tasks_brand, this, _validateDefinitionName).call(this, definition);
892
+ return {
893
+ name: definition,
894
+ run: (input, options) => this.run(definition, input, options),
895
+ get: (runId) => _assertClassBrand(_Tasks_brand, this, _snapshot).call(this, runId, definition),
896
+ getByIdempotencyKey: (idempotencyKey) => _assertClassBrand(_Tasks_brand, this, _snapshotByKey).call(this, idempotencyKey, definition),
897
+ cancel: (runId, reason) => _assertClassBrand(_Tasks_brand, this, _cancelScoped).call(this, runId, definition, reason)
898
+ };
899
+ }
900
+ /** Migrate storage and reconcile run deadlines during Lifecycle startup. */
901
+ async onStart() {
902
+ const storage = this.lifecycle.storage;
903
+ if ((await storage.get(FIBER_SCHEMA_VERSION_KEY) ?? 0) < CURRENT_FIBER_SCHEMA_VERSION) {
904
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).ensureTables();
905
+ await storage.put(FIBER_SCHEMA_VERSION_KEY, CURRENT_FIBER_SCHEMA_VERSION);
906
+ }
907
+ _assertClassBrand(_Tasks_brand, this, _reconcile).call(this);
908
+ await _assertClassBrand(_Tasks_brand, this, _syncAllWakes).call(this);
909
+ }
910
+ /** Drive one due run's wake dispatched by the Lifecycle event loop. */
911
+ async onJob(context) {
912
+ const timing = isTaskWakeJobPayload(context.job.payload) ? context.job.payload : void 0;
913
+ const runId = timing?.runId ?? context.job.id.slice(5);
914
+ if (timing?.owner_path) {
915
+ const target = {
916
+ key: timing.owner_path_key ?? timing.owner_path,
917
+ data: timing.owner_path
918
+ };
919
+ const call = this.lifecycle.routes.to(target, {
920
+ type: "dispatch",
921
+ runId
922
+ });
923
+ let budgetTimer;
924
+ const budget = new Promise((resolve) => {
925
+ budgetTimer = setTimeout(() => resolve("budget"), DISPATCH_BUDGET_MS);
926
+ });
927
+ try {
928
+ const winner = await Promise.race([call.then((outcome) => ({ outcome })), budget]);
929
+ if (winner === "budget") {
930
+ this.lifecycle.trackAlarmWork(call);
931
+ return;
932
+ }
933
+ return winner.outcome;
934
+ } catch (error) {
935
+ if (isPlatformFailure(error)) throw error;
936
+ console.error(`error dispatching routed Task run "${runId}"`, error);
937
+ return "yield";
938
+ } finally {
939
+ clearTimeout(budgetTimer);
940
+ }
941
+ }
942
+ return _assertClassBrand(_Tasks_brand, this, _dispatchRun).call(this, runId);
943
+ }
944
+ /**
945
+ * Alarm memory-limit breaker policy (#1825) for the run whose wake struck.
946
+ *
947
+ * The run row is the durable source of truth: startup reconciliation
948
+ * re-derives due-now wakes from it, so the breaker's queue-row backoff
949
+ * and purge alone cannot contain a run whose attempt deterministically
950
+ * exhausts memory — a fresh isolate would resurrect it immediately. On a
951
+ * strike the run's claim is stripped and its deadline pushed to the
952
+ * backoff wake: the row keeps its state, so a struck `running` row still
953
+ * reads as an interrupted attempt (`step.interrupted`) when it is
954
+ * reclaimed, while reconciliation leaves the claimless row alone instead
955
+ * of flooring its deadline to now. When the breaker seals, the run
956
+ * terminally fails with an observable `task:failed` outcome.
957
+ */
958
+ async onMemoryLimit(context) {
959
+ const job = context.executing;
960
+ if (job?.capability !== this.capabilityId) return;
961
+ const timing = isTaskWakeJobPayload(job.payload) ? job.payload : void 0;
962
+ const runId = timing?.runId ?? job.id.slice(5);
963
+ if (timing?.owner_path) {
964
+ try {
965
+ await this.lifecycle.routes.to({
966
+ key: timing.owner_path_key ?? timing.owner_path,
967
+ data: timing.owner_path
968
+ }, {
969
+ type: "memoryLimit",
970
+ runId,
971
+ context
972
+ });
973
+ } catch (error) {
974
+ console.error(`Failed to route memory-limit policy for Task run "${runId}"`, error);
975
+ }
976
+ return;
977
+ }
978
+ await _assertClassBrand(_Tasks_brand, this, _applyMemoryLimit).call(this, runId, context);
979
+ }
980
+ /**
981
+ * @internal Framework aperture: durably accept one run — reserved
982
+ * (`__cf`-prefixed) definition names included, which the public `run()`
983
+ * refuses so users cannot start framework runs — and drive its first
984
+ * attempt in the caller's invocation, resolving when that attempt reaches
985
+ * its next durable boundary. The receipt's run may already be terminal
986
+ * when this resolves; callers that need the outcome read it from their own
987
+ * channel (the run handler settles it) or from the snapshot.
988
+ */
989
+ async __DO_NOT_USE_WILL_BREAK__runAttached(definition, input, options) {
990
+ const receipt = await _assertClassBrand(_Tasks_brand, this, _acceptReserved).call(this, definition, input, options, "attached");
991
+ if (receipt.accepted) await _assertClassBrand(_Tasks_brand, this, _executeRun).call(this, receipt.runId);
992
+ return receipt;
993
+ }
994
+ /**
995
+ * @internal Framework aperture: durably accept one run — reserved names
996
+ * included — and leave its first attempt to the durable queue wake instead
997
+ * of warm-starting it in the caller's invocation. Chat recovery uses this
998
+ * so a continuation always runs under an alarm, where `trackAlarmWork`
999
+ * keeps its model turn inside the memory-limit breaker domain.
1000
+ */
1001
+ async __DO_NOT_USE_WILL_BREAK__enqueue(definition, input, options) {
1002
+ return _assertClassBrand(_Tasks_brand, this, _acceptReserved).call(this, definition, input, options, "queued");
1003
+ }
1004
+ /** Handle Tasks protocol messages routed by another Lifecycle. */
1005
+ async onRoute(context) {
1006
+ const message = context.payload;
1007
+ switch (message.type) {
1008
+ case "syncWake": {
1009
+ const owner = context.source;
1010
+ if (!owner) throw new Error("Routed Tasks message missing source");
1011
+ return _assertClassBrand(_Tasks_brand, this, _syncRoutedWake).call(this, owner, message.runId, message.next);
1012
+ }
1013
+ case "dispatch": return _assertClassBrand(_Tasks_brand, this, _dispatchRoutedRun).call(this, message.runId);
1014
+ case "memoryLimit": {
1015
+ await _assertClassBrand(_Tasks_brand, this, _applyMemoryLimit).call(this, message.runId, message.context);
1016
+ const handler = taskRoutedMemoryLimitHandlers.get(this);
1017
+ if (handler) await this.lifecycle.runInHostContext(() => handler(message.context));
1018
+ return true;
1019
+ }
1020
+ default: throw new Error("Unknown routed Tasks message");
1021
+ }
1022
+ }
1023
+ /**
1024
+ * @internal Framework aperture: bulk-cancel this root's routed wake
1025
+ * mirrors for every run owned by a deleted facet subtree. The runs and
1026
+ * their step journals live on the deleted facets' own storage and are
1027
+ * wiped with them; only this root's mirror job needs an explicit cancel,
1028
+ * or it stays due forever, retrying a dispatch to a facet that is gone.
1029
+ */
1030
+ async __DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix) {
1031
+ for (const job of this.lifecycle.jobs.list()) {
1032
+ const timing = isTaskWakeJobPayload(job.payload) ? job.payload : void 0;
1033
+ const ownerKey = timing?.owner_path_key ?? timing?.owner_path;
1034
+ if (!timing?.owner_path || ownerKey === null || ownerKey === void 0) continue;
1035
+ if (ownerKey !== prefix && !ownerKey.startsWith(`${prefix}/`)) continue;
1036
+ await this.lifecycle.jobs.cancel(job.id);
1037
+ }
1038
+ }
1039
+ /** Read one run by ID across all definitions. */
1040
+ async get(runId) {
1041
+ return _assertClassBrand(_Tasks_brand, this, _snapshot).call(this, runId);
1042
+ }
1043
+ /** Read one run by idempotency key across all definitions. */
1044
+ async getByIdempotencyKey(idempotencyKey) {
1045
+ return _assertClassBrand(_Tasks_brand, this, _snapshotByKey).call(this, idempotencyKey);
1046
+ }
1047
+ /** List runs, newest first. */
1048
+ async list(options = {}) {
1049
+ await this.lifecycle.ready();
1050
+ let query = "SELECT * FROM cf_agents_task_runs WHERE 1 = 1";
1051
+ const params = [];
1052
+ if (options.definition !== void 0) {
1053
+ query += " AND definition = ?";
1054
+ params.push(options.definition);
1055
+ }
1056
+ const states = Array.isArray(options.status) ? options.status : options.status !== void 0 ? [options.status] : [];
1057
+ if (states.length > 0) {
1058
+ query += ` AND state IN (${states.map(() => "?").join(", ")})`;
1059
+ params.push(...states);
1060
+ }
1061
+ query += " ORDER BY created_at DESC, run_id DESC LIMIT ?";
1062
+ params.push(options.limit ?? DEFAULT_LIST_LIMIT);
1063
+ let rows;
1064
+ try {
1065
+ rows = this.lifecycle.storage.sql.exec(query, ...params).toArray();
1066
+ } catch (cause) {
1067
+ throw new SqlError(query, cause);
1068
+ }
1069
+ return rows.map((row) => _get_store.call(_assertClassBrand(_Tasks_brand, this)).rowToSnapshot(row));
1070
+ }
1071
+ /**
1072
+ * Request cooperative cancellation of one run.
1073
+ *
1074
+ * A live attempt is aborted and settles as cancelled at its next step
1075
+ * boundary; a parked run settles immediately.
1076
+ *
1077
+ * @returns True when a non-terminal run accepted the request.
1078
+ */
1079
+ async cancel(runId, reason) {
1080
+ await this.lifecycle.ready();
1081
+ const row = _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId);
1082
+ if (!row || TERMINAL_STATES.has(row.state)) return false;
1083
+ const active = _classPrivateFieldGet2(_active, this).get(runId);
1084
+ if (active) {
1085
+ const now = Date.now();
1086
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1087
+ UPDATE cf_agents_task_runs
1088
+ SET cancel_requested = 1, cancel_reason = ${reason ?? null},
1089
+ next_at = ${now}, updated_at = ${now}
1090
+ WHERE run_id = ${runId}
1091
+ `;
1092
+ active.controller.abort(new TaskCancellation(reason));
1093
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, runId);
1094
+ return true;
1095
+ }
1096
+ await _assertClassBrand(_Tasks_brand, this, _settleCancelled).call(this, runId, null, reason);
1097
+ return true;
1098
+ }
1099
+ /**
1100
+ * Delete retained terminal runs and their step journals.
1101
+ *
1102
+ * @returns The number of runs deleted.
1103
+ */
1104
+ async delete(options = {}) {
1105
+ await this.lifecycle.ready();
1106
+ const states = options.status ?? [
1107
+ "completed",
1108
+ "failed",
1109
+ "cancelled"
1110
+ ];
1111
+ if (states.length === 0) return 0;
1112
+ let query = `SELECT run_id, definition FROM cf_agents_task_runs WHERE state IN (${states.map(() => "?").join(", ")})`;
1113
+ const params = [...states];
1114
+ if (options.settledBefore) {
1115
+ query += " AND settled_at < ?";
1116
+ params.push(options.settledBefore.getTime());
1117
+ }
1118
+ query += " ORDER BY settled_at ASC LIMIT ?";
1119
+ params.push(options.limit ?? DEFAULT_LIST_LIMIT);
1120
+ let rows;
1121
+ try {
1122
+ rows = this.lifecycle.storage.sql.exec(query, ...params).toArray();
1123
+ } catch (cause) {
1124
+ throw new SqlError(query, cause);
1125
+ }
1126
+ for (const row of rows) {
1127
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).deleteRun(row.run_id);
1128
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:deleted", {
1129
+ runId: row.run_id,
1130
+ definition: row.definition
1131
+ });
1132
+ }
1133
+ return rows.length;
1134
+ }
1135
+ };
1136
+ function _claimTimeoutMs() {
1137
+ return _classPrivateFieldGet2(_stepDefaults, this).timeoutMs + CLAIM_SLACK_MS;
1138
+ }
1139
+ /** The SQL store over this Lifecycle's storage (see `store.ts`). */
1140
+ function _get_store() {
1141
+ _classPrivateFieldGet2(_storeInstance, this) ?? _classPrivateFieldSet2(_storeInstance, this, new TaskStore(this.lifecycle.storage));
1142
+ return _classPrivateFieldGet2(_storeInstance, this);
1143
+ }
1144
+ /** Resolve a name to its declared or composition-root-supplied handler. */
1145
+ function _resolveDefinition(name) {
1146
+ return _classPrivateFieldGet2(_definitions, this)[name] ?? _classPrivateFieldGet2(_registered, this).get(name) ?? taskDefinitionResolvers.get(this)?.(name);
1147
+ }
1148
+ /** True when a name resolves to a runnable definition. */
1149
+ function _hasDefinition(name) {
1150
+ return _assertClassBrand(_Tasks_brand, this, _resolveDefinition).call(this, name) !== void 0;
1151
+ }
1152
+ function _validateDefinitionName(name) {
1153
+ if (typeof name !== "string" || name.length === 0) throw new Error("Task definition names must be non-empty strings");
1154
+ if (name.length > MAX_DEFINITION_NAME_LENGTH) throw new Error(`Task definition name exceeds ${MAX_DEFINITION_NAME_LENGTH} characters`);
1155
+ if (name.startsWith("__cf")) throw new Error(`Task definition names must not use the reserved "__cf" prefix`);
1156
+ if (!_assertClassBrand(_Tasks_brand, this, _hasDefinition).call(this, name)) throw new Error(`Unknown Task definition "${name}": not declared on this Tasks`);
1157
+ }
1158
+ /** Cancel through a handle: another definition's run is not visible. */
1159
+ async function _cancelScoped(runId, definition, reason) {
1160
+ await this.lifecycle.ready();
1161
+ const row = _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId);
1162
+ if (!row || row.definition !== definition) return false;
1163
+ return this.cancel(runId, reason);
1164
+ }
1165
+ /** Push a live attempt's durable claim deadline forward one claim window. */
1166
+ function _refreshClaim(runId) {
1167
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1168
+ UPDATE cf_agents_task_runs
1169
+ SET next_at = ${Date.now() + _assertClassBrand(_Tasks_brand, this, _claimTimeoutMs).call(this)}, updated_at = ${Date.now()}
1170
+ WHERE run_id = ${runId} AND state = 'running'
1171
+ `;
1172
+ }
1173
+ /**
1174
+ * Drive one local due run to its next durable boundary, bounded by the
1175
+ * dispatch budget, and return the wake outcome for this capability's own
1176
+ * queue job.
1177
+ */
1178
+ async function _dispatchRun(runId) {
1179
+ const active = _classPrivateFieldGet2(_active, this).get(runId);
1180
+ if (active) {
1181
+ _assertClassBrand(_Tasks_brand, this, _refreshClaim).call(this, runId);
1182
+ this.lifecycle.trackAlarmWork(active.promise);
1183
+ return _assertClassBrand(_Tasks_brand, this, _wakeOutcome).call(this, runId);
1184
+ }
1185
+ let budgetTimer;
1186
+ const budget = new Promise((resolve) => {
1187
+ budgetTimer = setTimeout(() => resolve("budget"), DISPATCH_BUDGET_MS);
1188
+ });
1189
+ const runAttempt = _assertClassBrand(_Tasks_brand, this, _executeRun).call(this, runId);
1190
+ const attempt = runAttempt.then(() => "settled");
1191
+ try {
1192
+ if (await Promise.race([attempt, budget]) === "budget") {
1193
+ this.lifecycle.trackAlarmWork(_classPrivateFieldGet2(_active, this).get(runId)?.promise ?? runAttempt);
1194
+ return _assertClassBrand(_Tasks_brand, this, _wakeOutcome).call(this, runId);
1195
+ }
1196
+ } finally {
1197
+ clearTimeout(budgetTimer);
1198
+ }
1199
+ return _assertClassBrand(_Tasks_brand, this, _wakeOutcome).call(this, runId);
1200
+ }
1201
+ /**
1202
+ * Drive one routed dispatch to completion on this owning facet. There is
1203
+ * no local budget to race here: the root that sent this message races
1204
+ * its own await of the call instead, so a full await is safe regardless
1205
+ * of how long the attempt takes — the call keeps running on this facet
1206
+ * either way. An already-active attempt only needs its claim refreshed:
1207
+ * it is already tracked against whichever alarm's breaker domain
1208
+ * originally dispatched it (a root's pending routed call, or this
1209
+ * facet's own local alarm).
1210
+ */
1211
+ async function _dispatchRoutedRun(runId) {
1212
+ if (_classPrivateFieldGet2(_active, this).get(runId)) {
1213
+ _assertClassBrand(_Tasks_brand, this, _refreshClaim).call(this, runId);
1214
+ return _assertClassBrand(_Tasks_brand, this, _wakeOutcome).call(this, runId);
1215
+ }
1216
+ await _assertClassBrand(_Tasks_brand, this, _executeRun).call(this, runId);
1217
+ return _assertClassBrand(_Tasks_brand, this, _wakeOutcome).call(this, runId);
1218
+ }
1219
+ /**
1220
+ * Apply the alarm memory-limit breaker policy (#1825) to one run, local to
1221
+ * whichever Lifecycle owns its storage — the root for an unrouted run, or
1222
+ * the owning facet when {@link onMemoryLimit} forwarded a routed strike.
1223
+ */
1224
+ async function _applyMemoryLimit(runId, context) {
1225
+ if (context.sealed) {
1226
+ await _assertClassBrand(_Tasks_brand, this, _settleFailed).call(this, runId, null, {
1227
+ name: "TaskMemoryLimitSealed",
1228
+ message: "Sealed by the alarm memory-limit circuit breaker (#1825) after consecutive Durable Object memory-limit resets."
1229
+ });
1230
+ return;
1231
+ }
1232
+ if (context.nextTime === void 0) return;
1233
+ const now = Date.now();
1234
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).write(`UPDATE cf_agents_task_runs
1235
+ SET generation = NULL,
1236
+ next_at = CASE
1237
+ WHEN next_at IS NULL OR next_at < ? THEN ?
1238
+ ELSE next_at
1239
+ END,
1240
+ updated_at = ?
1241
+ WHERE run_id = ?
1242
+ AND state IN ('pending', 'waiting', 'running')`, [
1243
+ context.nextTime,
1244
+ context.nextTime,
1245
+ now,
1246
+ runId
1247
+ ]);
1248
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, runId);
1249
+ }
1250
+ /** Accept a run of any resolvable definition, reserved names included. */
1251
+ async function _acceptReserved(definition, input, options, startMode) {
1252
+ if (!_assertClassBrand(_Tasks_brand, this, _hasDefinition).call(this, definition)) throw new Error(`Unknown Task definition "${definition}": not declared on this Tasks`);
1253
+ return _assertClassBrand(_Tasks_brand, this, _accept).call(this, definition, input, options, startMode);
1254
+ }
1255
+ /**
1256
+ * The queue outcome for one run's wake job, derived from the run row's
1257
+ * authoritative `next_at` after dispatch. A same-id `#syncWake` push made
1258
+ * mid-drive supersedes this return at the queue (newer pushes win over
1259
+ * drive results), but both are computed from the same row, so the row is
1260
+ * the single source of truth for whether — and when — the run wakes
1261
+ * again either way.
1262
+ */
1263
+ function _wakeOutcome(runId) {
1264
+ const next = _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1265
+ SELECT next_at FROM cf_agents_task_runs
1266
+ WHERE run_id = ${runId}
1267
+ AND state IN ('pending', 'waiting', 'running')
1268
+ `[0]?.next_at;
1269
+ return typeof next === "number" ? { rescheduleAt: next } : void 0;
1270
+ }
1271
+ /**
1272
+ * Mirror one run's authoritative deadline into the Lifecycle job queue:
1273
+ * a non-terminal run with a `next_at` gets one job (id = `task:` plus the
1274
+ * run id, so a retime is a same-id replace); anything else cancels the
1275
+ * mirror. The prefix keeps caller-selected run IDs inside Tasks' own job
1276
+ * namespace. Every durable mutation of a run's deadline or state funnels
1277
+ * through here.
1278
+ *
1279
+ * @returns False when the queue already carried exactly this wake and
1280
+ * nothing was written — a same-values upsert is still a billed row write.
1281
+ */
1282
+ async function _syncWake(runId) {
1283
+ const next = _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1284
+ SELECT next_at FROM cf_agents_task_runs
1285
+ WHERE run_id = ${runId}
1286
+ AND state IN ('pending', 'waiting', 'running')
1287
+ `[0]?.next_at ?? null;
1288
+ if (this.lifecycle.routes.source) return await this.lifecycle.routes.toRoot({
1289
+ type: "syncWake",
1290
+ runId,
1291
+ next
1292
+ });
1293
+ const jobId = `${WAKE_JOB_PREFIX}${runId}`;
1294
+ if (next === null) {
1295
+ await this.lifecycle.jobs.cancel(jobId);
1296
+ return true;
1297
+ }
1298
+ const existing = this.lifecycle.jobs.get(jobId);
1299
+ if (existing?.fn === WAKE_JOB_FN && existing.time === next && existing.retry?.maxAttempts === WAKE_JOB_RETRY.maxAttempts) return false;
1300
+ await this.lifecycle.jobs.push({
1301
+ id: jobId,
1302
+ fn: WAKE_JOB_FN,
1303
+ time: next,
1304
+ payload: { runId },
1305
+ retry: WAKE_JOB_RETRY
1306
+ });
1307
+ return true;
1308
+ }
1309
+ /** Mirror a routed facet's run deadline into this root's job queue. */
1310
+ async function _syncRoutedWake(owner, runId, next) {
1311
+ const jobId = `${WAKE_JOB_PREFIX}${owner.key}:${runId}`;
1312
+ if (next === null) {
1313
+ await this.lifecycle.jobs.cancel(jobId);
1314
+ return true;
1315
+ }
1316
+ const existing = this.lifecycle.jobs.get(jobId);
1317
+ if (existing?.fn === WAKE_JOB_FN && existing.time === next && existing.retry?.maxAttempts === WAKE_JOB_RETRY.maxAttempts) return false;
1318
+ await this.lifecycle.jobs.push({
1319
+ id: jobId,
1320
+ fn: WAKE_JOB_FN,
1321
+ time: next,
1322
+ payload: {
1323
+ runId,
1324
+ owner_path: owner.data,
1325
+ owner_path_key: owner.key
1326
+ },
1327
+ retry: WAKE_JOB_RETRY
1328
+ });
1329
+ return true;
1330
+ }
1331
+ /** Mirror every non-terminal run into the queue (startup reconcile). */
1332
+ async function _syncAllWakes() {
1333
+ const rows = _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1334
+ SELECT run_id FROM cf_agents_task_runs
1335
+ WHERE state IN ('pending', 'waiting', 'running')
1336
+ AND next_at IS NOT NULL
1337
+ `;
1338
+ let pushed = false;
1339
+ for (const { run_id } of rows) if (await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, run_id)) pushed = true;
1340
+ if (rows.length > 0 && !pushed) await this.lifecycle.jobs.rearm();
1341
+ }
1342
+ async function _accept(definition, input, options = {}, startMode = "warm") {
1343
+ await this.lifecycle.ready();
1344
+ if (options.runId !== void 0 && options.runId.length === 0) throw new Error("runId must be a non-empty string when provided");
1345
+ if (options.idempotencyKey !== void 0 && options.idempotencyKey.length === 0) throw new Error("idempotencyKey must be a non-empty string when provided");
1346
+ const inputJson = serializeTaskValue(input, `input for Task definition "${definition}"`);
1347
+ const metadataJson = serializeTaskValue(options.metadata, `metadata for Task definition "${definition}"`);
1348
+ const existing = (options.runId !== void 0 ? _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(options.runId) : void 0) ?? (options.idempotencyKey !== void 0 ? _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRunByKey(options.idempotencyKey) : void 0);
1349
+ if (existing) {
1350
+ if (existing.definition !== definition) throw new Error(`Task run "${existing.run_id}" already belongs to definition "${existing.definition}"; refusing to reuse its ${options.runId !== void 0 ? "run ID" : "idempotency key"} for "${definition}"`);
1351
+ if (options.idempotencyKey !== void 0 && existing.idempotency_key !== options.idempotencyKey) throw new Error(`Task run "${existing.run_id}" carries idempotency key ${existing.idempotency_key === null ? "none" : `"${existing.idempotency_key}"`}; refusing to join it with conflicting key "${options.idempotencyKey}"`);
1352
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, existing.run_id);
1353
+ return {
1354
+ runId: existing.run_id,
1355
+ definition,
1356
+ accepted: false,
1357
+ state: existing.state,
1358
+ createdAt: existing.created_at
1359
+ };
1360
+ }
1361
+ const runId = options.runId ?? `task_${nanoid()}`;
1362
+ const now = Date.now();
1363
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1364
+ INSERT INTO cf_agents_task_runs
1365
+ (run_id, definition, input, state, metadata, idempotency_key, retain,
1366
+ attempt, next_at, cancel_requested, created_at, updated_at)
1367
+ VALUES
1368
+ (${runId}, ${definition}, ${inputJson}, 'pending', ${metadataJson},
1369
+ ${options.idempotencyKey ?? null}, ${options.retain === false ? 0 : 1},
1370
+ 0, ${now}, 0, ${now}, ${now})
1371
+ `;
1372
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, runId);
1373
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:accepted", {
1374
+ runId,
1375
+ definition,
1376
+ accepted: true
1377
+ });
1378
+ if (startMode === "warm" && !this.lifecycle.starting()) _assertClassBrand(_Tasks_brand, this, _executeRun).call(this, runId).catch(() => {});
1379
+ return {
1380
+ runId,
1381
+ definition,
1382
+ accepted: true,
1383
+ state: "pending",
1384
+ createdAt: now
1385
+ };
1386
+ }
1387
+ /** Claim and drive one due run to its next durable boundary. */
1388
+ async function _executeRun(runId) {
1389
+ if (_classPrivateFieldGet2(_active, this).has(runId)) return;
1390
+ const row = _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId);
1391
+ if (!row || TERMINAL_STATES.has(row.state)) return;
1392
+ const now = Date.now();
1393
+ if (row.cancel_requested === 1) {
1394
+ await _assertClassBrand(_Tasks_brand, this, _settleCancelled).call(this, runId, null, row.cancel_reason ?? void 0);
1395
+ return;
1396
+ }
1397
+ if (row.next_at !== null && row.next_at > now) return;
1398
+ const handler = _assertClassBrand(_Tasks_brand, this, _resolveDefinition).call(this, row.definition);
1399
+ if (!handler) {
1400
+ const error = new MissingTaskDefinitionError(row.definition);
1401
+ console.error(error.message);
1402
+ await _assertClassBrand(_Tasks_brand, this, _settleFailed).call(this, runId, null, toErrorSummary(error));
1403
+ await _assertClassBrand(_Tasks_brand, this, _observeError).call(this, error);
1404
+ return;
1405
+ }
1406
+ const interrupted = row.state === "running" ? _assertClassBrand(_Tasks_brand, this, _interruptedStep).call(this, runId) : null;
1407
+ if (row.state === "running") _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:attempt:interrupted", {
1408
+ runId,
1409
+ definition: row.definition,
1410
+ attempt: row.attempt,
1411
+ step: interrupted?.name ?? null
1412
+ });
1413
+ const generation = nanoid();
1414
+ const attempt = row.attempt + 1;
1415
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1416
+ UPDATE cf_agents_task_runs
1417
+ SET state = 'running', attempt = ${attempt}, generation = ${generation},
1418
+ started_at = coalesce(started_at, ${now}),
1419
+ next_at = ${now + _assertClassBrand(_Tasks_brand, this, _claimTimeoutMs).call(this)}, wait_reason = NULL,
1420
+ updated_at = ${now}
1421
+ WHERE run_id = ${runId}
1422
+ AND state IN ('pending', 'waiting', 'running')
1423
+ `;
1424
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, runId);
1425
+ const controller = new AbortController();
1426
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:attempt:started", {
1427
+ runId,
1428
+ definition: row.definition,
1429
+ attempt
1430
+ });
1431
+ const promise = _assertClassBrand(_Tasks_brand, this, _runAttempt).call(this, row, handler, generation, attempt, controller, interrupted, now);
1432
+ _classPrivateFieldGet2(_active, this).set(runId, {
1433
+ generation,
1434
+ controller,
1435
+ promise
1436
+ });
1437
+ try {
1438
+ await promise;
1439
+ } finally {
1440
+ _classPrivateFieldGet2(_active, this).delete(runId);
1441
+ }
1442
+ }
1443
+ /** Run one claimed attempt and persist its outcome, generation-fenced. */
1444
+ async function _runAttempt(row, handler, generation, attempt, controller, interrupted, claimedAtMs) {
1445
+ const runId = row.run_id;
1446
+ const input = deserializeTaskValue(row.input);
1447
+ const step = new ReplayStep(_assertClassBrand(_Tasks_brand, this, _createEngine).call(this, runId, row.definition, generation, controller, claimedAtMs), {
1448
+ startsLive: attempt === 1,
1449
+ interrupted
1450
+ });
1451
+ try {
1452
+ const resultJson = serializeTaskValue(await this.lifecycle.runInHostContext(() => handler(input, step)), `result of Task definition "${row.definition}"`);
1453
+ if (_get_store.call(_assertClassBrand(_Tasks_brand, this)).fencedWrite(runId, generation, `UPDATE cf_agents_task_runs
1454
+ SET state = 'completed', result = ?, generation = NULL, next_at = NULL,
1455
+ settled_at = ?, updated_at = ?
1456
+ WHERE run_id = ? AND generation = ? AND state = 'running'`, [
1457
+ resultJson,
1458
+ Date.now(),
1459
+ Date.now()
1460
+ ])) {
1461
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:completed", {
1462
+ runId,
1463
+ definition: row.definition
1464
+ });
1465
+ await _assertClassBrand(_Tasks_brand, this, _finishTerminalSettlement).call(this, runId, row);
1466
+ }
1467
+ } catch (thrown) {
1468
+ await _assertClassBrand(_Tasks_brand, this, _settleThrown).call(this, row, generation, thrown);
1469
+ }
1470
+ }
1471
+ /** Persist a non-completed attempt outcome. */
1472
+ async function _settleThrown(row, generation, thrown) {
1473
+ const runId = row.run_id;
1474
+ if (thrown instanceof AttemptSupersededError) return;
1475
+ if (isPlatformFailure(thrown)) throw thrown;
1476
+ if (isTaskCancellation(thrown)) {
1477
+ await _assertClassBrand(_Tasks_brand, this, _settleCancelled).call(this, runId, generation, thrown.reason);
1478
+ return;
1479
+ }
1480
+ if (isTaskSuspension(thrown)) {
1481
+ const current = _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId);
1482
+ if (current?.cancel_requested === 1) {
1483
+ await _assertClassBrand(_Tasks_brand, this, _settleCancelled).call(this, runId, generation, current.cancel_reason ?? void 0);
1484
+ return;
1485
+ }
1486
+ if (_get_store.call(_assertClassBrand(_Tasks_brand, this)).fencedWrite(runId, generation, `UPDATE cf_agents_task_runs
1487
+ SET state = 'waiting', wait_reason = ?, next_at = ?, generation = NULL,
1488
+ updated_at = ?
1489
+ WHERE run_id = ? AND generation = ? AND state = 'running'`, [
1490
+ thrown.reason,
1491
+ thrown.wakeAt,
1492
+ Date.now()
1493
+ ])) {
1494
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:waiting", {
1495
+ runId,
1496
+ definition: row.definition,
1497
+ reason: thrown.reason,
1498
+ wakeAt: thrown.wakeAt
1499
+ });
1500
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, runId);
1501
+ }
1502
+ return;
1503
+ }
1504
+ const summary = toErrorSummary(thrown);
1505
+ if (await _assertClassBrand(_Tasks_brand, this, _settleFailed).call(this, runId, generation, summary)) console.error(`Task run "${runId}" (definition "${row.definition}") failed: ${summary.name}: ${summary.message}`);
1506
+ await _assertClassBrand(_Tasks_brand, this, _observeError).call(this, thrown);
1507
+ }
1508
+ async function _observeError(error) {
1509
+ if (!_classPrivateFieldGet2(_onError, this)) return;
1510
+ try {
1511
+ await this.lifecycle.runInHostContext(() => _classPrivateFieldGet2(_onError, this)?.call(this, error));
1512
+ } catch {}
1513
+ }
1514
+ function _createEngine(runId, definition, generation, controller, claimedAtMs) {
1515
+ return createTaskStepEngine({
1516
+ store: _get_store.call(_assertClassBrand(_Tasks_brand, this)),
1517
+ runId,
1518
+ generation,
1519
+ signal: controller.signal,
1520
+ claimTimeoutMs: () => _assertClassBrand(_Tasks_brand, this, _claimTimeoutMs).call(this),
1521
+ claimedAtMs,
1522
+ claimRefreshAfterMs: CLAIM_SLACK_MS / 2,
1523
+ defaults: _classPrivateFieldGet2(_stepDefaults, this),
1524
+ emit: (type, payload) => _assertClassBrand(_Tasks_brand, this, _emit).call(this, type, {
1525
+ runId,
1526
+ definition,
1527
+ ...payload
1528
+ })
1529
+ });
1530
+ }
1531
+ /** The step a lost attempt left mid-execution — replay-entry evidence. */
1532
+ function _interruptedStep(runId) {
1533
+ const rows = _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1534
+ SELECT step_name, attempt FROM cf_agents_task_steps
1535
+ WHERE run_id = ${runId} AND state = 'running'
1536
+ ORDER BY started_at DESC
1537
+ LIMIT 1
1538
+ `;
1539
+ return rows[0] ? {
1540
+ name: rows[0].step_name,
1541
+ attempt: rows[0].attempt
1542
+ } : null;
1543
+ }
1544
+ /**
1545
+ * Settle one run as cancelled and sync its queue mirror. Fenced when a
1546
+ * generation is supplied.
1547
+ */
1548
+ async function _settleCancelled(runId, generation, reason) {
1549
+ const now = Date.now();
1550
+ let settled;
1551
+ if (generation !== null) settled = _get_store.call(_assertClassBrand(_Tasks_brand, this)).fencedWrite(runId, generation, `UPDATE cf_agents_task_runs
1552
+ SET state = 'cancelled', cancel_requested = 1, cancel_reason = ?,
1553
+ generation = NULL, next_at = NULL, settled_at = ?, updated_at = ?
1554
+ WHERE run_id = ? AND generation = ?
1555
+ AND state = 'running'`, [
1556
+ reason ?? null,
1557
+ now,
1558
+ now
1559
+ ]);
1560
+ else settled = _get_store.call(_assertClassBrand(_Tasks_brand, this)).write(`UPDATE cf_agents_task_runs
1561
+ SET state = 'cancelled', cancel_requested = 1, cancel_reason = ?,
1562
+ generation = NULL, next_at = NULL, settled_at = ?, updated_at = ?
1563
+ WHERE run_id = ?
1564
+ AND state IN ('pending', 'waiting', 'running')`, [
1565
+ reason ?? null,
1566
+ now,
1567
+ now,
1568
+ runId
1569
+ ]) > 0;
1570
+ const row = settled ? _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId) : void 0;
1571
+ if (row) {
1572
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:cancelled", {
1573
+ runId,
1574
+ definition: row.definition,
1575
+ reason: reason ?? null
1576
+ });
1577
+ await _assertClassBrand(_Tasks_brand, this, _finishTerminalSettlement).call(this, runId, row);
1578
+ }
1579
+ }
1580
+ /**
1581
+ * Settle one run as failed and sync its queue mirror. Fenced when a
1582
+ * generation is supplied.
1583
+ */
1584
+ async function _settleFailed(runId, generation, error) {
1585
+ const now = Date.now();
1586
+ let settled;
1587
+ if (generation !== null) settled = _get_store.call(_assertClassBrand(_Tasks_brand, this)).fencedWrite(runId, generation, `UPDATE cf_agents_task_runs
1588
+ SET state = 'failed', error_name = ?, error_message = ?,
1589
+ generation = NULL, next_at = NULL, settled_at = ?, updated_at = ?
1590
+ WHERE run_id = ? AND generation = ?
1591
+ AND state = 'running'`, [
1592
+ error.name,
1593
+ error.message,
1594
+ now,
1595
+ now
1596
+ ]);
1597
+ else settled = _get_store.call(_assertClassBrand(_Tasks_brand, this)).write(`UPDATE cf_agents_task_runs
1598
+ SET state = 'failed', error_name = ?, error_message = ?,
1599
+ generation = NULL, next_at = NULL, settled_at = ?, updated_at = ?
1600
+ WHERE run_id = ?
1601
+ AND state IN ('pending', 'waiting', 'running')`, [
1602
+ error.name,
1603
+ error.message,
1604
+ now,
1605
+ now,
1606
+ runId
1607
+ ]) > 0;
1608
+ const row = settled ? _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId) : void 0;
1609
+ if (row) {
1610
+ _assertClassBrand(_Tasks_brand, this, _emit).call(this, "task:failed", {
1611
+ runId,
1612
+ definition: row.definition,
1613
+ error: error.name
1614
+ });
1615
+ await _assertClassBrand(_Tasks_brand, this, _finishTerminalSettlement).call(this, runId, row);
1616
+ }
1617
+ return settled;
1618
+ }
1619
+ /** Apply terminal retention policy, then remove the run's wake mirror. */
1620
+ async function _finishTerminalSettlement(runId, row) {
1621
+ if (row?.retain === 0) _get_store.call(_assertClassBrand(_Tasks_brand, this)).deleteRun(runId);
1622
+ await _assertClassBrand(_Tasks_brand, this, _syncWake).call(this, runId);
1623
+ }
1624
+ /** Make deadlines sane after a fresh isolate: interrupted work wakes now. */
1625
+ function _reconcile() {
1626
+ const now = Date.now();
1627
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1628
+ UPDATE cf_agents_task_runs SET next_at = ${now}, updated_at = ${now}
1629
+ WHERE state = 'running' AND generation IS NOT NULL
1630
+ `;
1631
+ _get_store.call(_assertClassBrand(_Tasks_brand, this)).sql`
1632
+ UPDATE cf_agents_task_runs SET next_at = ${now}, updated_at = ${now}
1633
+ WHERE state IN ('pending', 'waiting') AND next_at IS NULL
1634
+ `;
1635
+ }
1636
+ async function _snapshot(runId, definition) {
1637
+ await this.lifecycle.ready();
1638
+ const row = _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRun(runId);
1639
+ if (!row) return null;
1640
+ if (definition !== void 0 && row.definition !== definition) return null;
1641
+ return _get_store.call(_assertClassBrand(_Tasks_brand, this)).rowToSnapshot(row);
1642
+ }
1643
+ async function _snapshotByKey(idempotencyKey, definition) {
1644
+ await this.lifecycle.ready();
1645
+ const row = _get_store.call(_assertClassBrand(_Tasks_brand, this)).getRunByKey(idempotencyKey);
1646
+ if (!row) return null;
1647
+ if (definition !== void 0 && row.definition !== definition) return null;
1648
+ return _get_store.call(_assertClassBrand(_Tasks_brand, this)).rowToSnapshot(row);
1649
+ }
1650
+ function _emit(type, payload) {
1651
+ this.lifecycle.events.emit(type, payload);
1652
+ }
1653
+ //#endregion
1654
+ export { DuplicateTaskStepError as a, TaskReplayDivergedError as c, MAX_SERIALIZED_BYTES as i, TaskSerializationError as l, setTaskDefinitionResolver as n, MissingTaskDefinitionError as o, setTaskRoutedMemoryLimitHandler as r, NonRetryableError as s, Tasks as t };
1655
+
1656
+ //# sourceMappingURL=tasks-ylZgBjhj.js.map