@warlock.js/core 4.5.0 → 4.6.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 (200) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/esm/ai/src/agent/agent.mjs +126 -7
  3. package/esm/ai/src/agent/agent.mjs.map +1 -1
  4. package/esm/ai/src/agent/signature.mjs +57 -0
  5. package/esm/ai/src/agent/signature.mjs.map +1 -0
  6. package/esm/ai/src/agent/snapshot.mjs +101 -0
  7. package/esm/ai/src/agent/snapshot.mjs.map +1 -0
  8. package/esm/ai/src/ai.mjs +37 -1
  9. package/esm/ai/src/ai.mjs.map +1 -1
  10. package/esm/ai/src/errors/agent-drift-error.mjs +31 -0
  11. package/esm/ai/src/errors/agent-drift-error.mjs.map +1 -0
  12. package/esm/ai/src/errors/index.mjs +2 -0
  13. package/esm/ai/src/errors/planner-drift-error.mjs +33 -0
  14. package/esm/ai/src/errors/planner-drift-error.mjs.map +1 -0
  15. package/esm/ai/src/image/image-cost.mjs +55 -0
  16. package/esm/ai/src/image/image-cost.mjs.map +1 -0
  17. package/esm/ai/src/image/image.mjs +112 -0
  18. package/esm/ai/src/image/image.mjs.map +1 -0
  19. package/esm/ai/src/image/index.mjs +4 -0
  20. package/esm/ai/src/index.mjs +20 -0
  21. package/esm/ai/src/planner/planner-run.mjs +161 -6
  22. package/esm/ai/src/planner/planner-run.mjs.map +1 -1
  23. package/esm/ai/src/planner/planner.mjs +25 -1
  24. package/esm/ai/src/planner/planner.mjs.map +1 -1
  25. package/esm/ai/src/planner/snapshot.mjs +95 -0
  26. package/esm/ai/src/planner/snapshot.mjs.map +1 -0
  27. package/esm/ai/src/rag/hybrid/bm25.mjs +51 -0
  28. package/esm/ai/src/rag/hybrid/bm25.mjs.map +1 -0
  29. package/esm/ai/src/rag/hybrid/hybrid-rank.mjs +29 -0
  30. package/esm/ai/src/rag/hybrid/hybrid-rank.mjs.map +1 -0
  31. package/esm/ai/src/rag/hybrid/rrf.mjs +30 -0
  32. package/esm/ai/src/rag/hybrid/rrf.mjs.map +1 -0
  33. package/esm/ai/src/rag/index.mjs +11 -0
  34. package/esm/ai/src/rag/loaders/errors.mjs +25 -0
  35. package/esm/ai/src/rag/loaders/errors.mjs.map +1 -0
  36. package/esm/ai/src/rag/loaders/index.mjs +7 -0
  37. package/esm/ai/src/rag/loaders/load-html.mjs +138 -0
  38. package/esm/ai/src/rag/loaders/load-html.mjs.map +1 -0
  39. package/esm/ai/src/rag/loaders/load-pdf.mjs +150 -0
  40. package/esm/ai/src/rag/loaders/load-pdf.mjs.map +1 -0
  41. package/esm/ai/src/rag/loaders/load-text.mjs +60 -0
  42. package/esm/ai/src/rag/loaders/load-text.mjs.map +1 -0
  43. package/esm/ai/src/rag/loaders/load-web.mjs +89 -0
  44. package/esm/ai/src/rag/loaders/load-web.mjs.map +1 -0
  45. package/esm/ai/src/rag/store/pg-vector-store.mjs +328 -0
  46. package/esm/ai/src/rag/store/pg-vector-store.mjs.map +1 -0
  47. package/esm/ai/src/rag/transforms/multi-query.mjs +41 -0
  48. package/esm/ai/src/rag/transforms/multi-query.mjs.map +1 -0
  49. package/esm/ai/src/speech/index.mjs +3 -0
  50. package/esm/ai/src/speech/speech.mjs +122 -0
  51. package/esm/ai/src/speech/speech.mjs.map +1 -0
  52. package/esm/ai/src/supervisor/entries.mjs +2 -2
  53. package/esm/ai/src/supervisor/entries.mjs.map +1 -1
  54. package/esm/ai/src/transcribe/audio-input.mjs +84 -0
  55. package/esm/ai/src/transcribe/audio-input.mjs.map +1 -0
  56. package/esm/ai/src/transcribe/index.mjs +4 -0
  57. package/esm/ai/src/transcribe/transcribe.mjs +127 -0
  58. package/esm/ai/src/transcribe/transcribe.mjs.map +1 -0
  59. package/esm/ai-openai/src/image.mjs +5 -0
  60. package/esm/ai-openai/src/index.mjs +3 -0
  61. package/esm/ai-openai/src/sdk.mjs +3 -0
  62. package/esm/ai-openai/src/speech.mjs +5 -0
  63. package/esm/ai-openai/src/transcription.mjs +6 -0
  64. package/esm/ai-openai/src/utils/index.mjs +1 -0
  65. package/esm/ai-openai/src/utils/to-openai-messages.mjs +3 -0
  66. package/esm/cli/commands/doctor/checks/config.check.mjs +42 -0
  67. package/esm/cli/commands/doctor/checks/config.check.mjs.map +1 -0
  68. package/esm/cli/commands/doctor/checks/connectors.check.mjs +28 -0
  69. package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -0
  70. package/esm/cli/commands/doctor/checks/health.check.mjs +35 -0
  71. package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -0
  72. package/esm/cli/commands/doctor/checks/index.mjs +25 -0
  73. package/esm/cli/commands/doctor/checks/index.mjs.map +1 -0
  74. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +90 -0
  75. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -0
  76. package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs +69 -0
  77. package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs.map +1 -0
  78. package/esm/cli/commands/doctor/checks/routes.check.mjs +30 -0
  79. package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -0
  80. package/esm/cli/commands/doctor/doctor-command.action.mjs +28 -0
  81. package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -0
  82. package/esm/cli/commands/doctor/format-report.mjs +44 -0
  83. package/esm/cli/commands/doctor/format-report.mjs.map +1 -0
  84. package/esm/cli/commands/doctor/run-checks.mjs +50 -0
  85. package/esm/cli/commands/doctor/run-checks.mjs.map +1 -0
  86. package/esm/cli/commands/doctor.command.mjs +29 -0
  87. package/esm/cli/commands/doctor.command.mjs.map +1 -0
  88. package/esm/cli/commands/generate/templates/stubs.mjs +6 -8
  89. package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
  90. package/esm/cli/commands/routes/format-routes-table.mjs +129 -0
  91. package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -0
  92. package/esm/cli/commands/routes/route-row.mjs +92 -0
  93. package/esm/cli/commands/routes/route-row.mjs.map +1 -0
  94. package/esm/cli/commands/routes/routes-command.action.mjs +43 -0
  95. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -0
  96. package/esm/cli/commands/routes.command.mjs +51 -0
  97. package/esm/cli/commands/routes.command.mjs.map +1 -0
  98. package/esm/cli/commands/seed.command.mjs +5 -0
  99. package/esm/cli/commands/seed.command.mjs.map +1 -1
  100. package/esm/cli/framework-cli-commands.mjs +4 -0
  101. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  102. package/esm/config/config-getter.mjs +5 -5
  103. package/esm/config/config-getter.mjs.map +1 -1
  104. package/esm/config/config-loader.mjs +2 -2
  105. package/esm/config/config-loader.mjs.map +1 -1
  106. package/esm/connectors/access-connector.mjs +2 -2
  107. package/esm/connectors/access-connector.mjs.map +1 -1
  108. package/esm/connectors/ai-connector.mjs +2 -2
  109. package/esm/connectors/ai-connector.mjs.map +1 -1
  110. package/esm/connectors/cache-connector.mjs +2 -2
  111. package/esm/connectors/cache-connector.mjs.map +1 -1
  112. package/esm/connectors/database-connector.mjs +2 -2
  113. package/esm/connectors/database-connector.mjs.map +1 -1
  114. package/esm/connectors/herald-connector.mjs +2 -2
  115. package/esm/connectors/herald-connector.mjs.map +1 -1
  116. package/esm/connectors/http-connector.d.mts.map +1 -1
  117. package/esm/connectors/http-connector.mjs +9 -7
  118. package/esm/connectors/http-connector.mjs.map +1 -1
  119. package/esm/connectors/logger-connector.mjs +2 -2
  120. package/esm/connectors/logger-connector.mjs.map +1 -1
  121. package/esm/connectors/mail-connector.mjs +2 -2
  122. package/esm/connectors/mail-connector.mjs.map +1 -1
  123. package/esm/connectors/notifications-connector.mjs +2 -2
  124. package/esm/connectors/notifications-connector.mjs.map +1 -1
  125. package/esm/connectors/socket-connector.mjs +3 -3
  126. package/esm/connectors/socket-connector.mjs.map +1 -1
  127. package/esm/database/index.d.mts +2 -1
  128. package/esm/database/index.mjs +1 -0
  129. package/esm/database/seed-command-action.mjs +44 -4
  130. package/esm/database/seed-command-action.mjs.map +1 -1
  131. package/esm/database/seeds/seed-records-table-migration.mjs +36 -0
  132. package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -0
  133. package/esm/database/seeds/seeder.d.mts +22 -5
  134. package/esm/database/seeds/seeder.d.mts.map +1 -1
  135. package/esm/database/seeds/seeder.errors.d.mts +22 -0
  136. package/esm/database/seeds/seeder.errors.d.mts.map +1 -0
  137. package/esm/database/seeds/seeder.errors.mjs +29 -0
  138. package/esm/database/seeds/seeder.errors.mjs.map +1 -0
  139. package/esm/database/seeds/seeder.mjs.map +1 -1
  140. package/esm/database/seeds/seeders.manager.mjs +90 -11
  141. package/esm/database/seeds/seeders.manager.mjs.map +1 -1
  142. package/esm/database/seeds/types.d.mts +117 -1
  143. package/esm/database/seeds/types.d.mts.map +1 -1
  144. package/esm/database/seeds/utils.mjs +6 -1
  145. package/esm/database/seeds/utils.mjs.map +1 -1
  146. package/esm/dev-server/module-loader.d.mts +12 -0
  147. package/esm/dev-server/module-loader.d.mts.map +1 -1
  148. package/esm/dev-server/module-loader.mjs +30 -2
  149. package/esm/dev-server/module-loader.mjs.map +1 -1
  150. package/esm/http/config.mjs +2 -2
  151. package/esm/http/config.mjs.map +1 -1
  152. package/esm/http/createHttpApplication.mjs +2 -2
  153. package/esm/http/createHttpApplication.mjs.map +1 -1
  154. package/esm/http/health.d.mts +14 -0
  155. package/esm/http/health.d.mts.map +1 -1
  156. package/esm/http/health.mjs +16 -0
  157. package/esm/http/health.mjs.map +1 -1
  158. package/esm/http/middleware/idempotency.middleware.mjs +5 -5
  159. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  160. package/esm/http/middleware/inject-request-context.mjs +2 -2
  161. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  162. package/esm/http/middleware/maintenance.middleware.mjs +4 -4
  163. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  164. package/esm/http/plugins.mjs +9 -9
  165. package/esm/http/plugins.mjs.map +1 -1
  166. package/esm/http/response.mjs +5 -5
  167. package/esm/http/response.mjs.map +1 -1
  168. package/esm/http/server.mjs +4 -4
  169. package/esm/http/server.mjs.map +1 -1
  170. package/esm/index.d.mts +4 -3
  171. package/esm/index.mjs +2 -1
  172. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +28 -1
  173. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
  174. package/esm/repositories/adapters/cascade/cascade-query-builder.mjs +39 -0
  175. package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
  176. package/esm/repositories/contracts/index.d.mts +1 -1
  177. package/esm/repositories/contracts/query-builder.contract.d.mts +87 -1
  178. package/esm/repositories/contracts/query-builder.contract.d.mts.map +1 -1
  179. package/esm/repositories/index.d.mts +1 -1
  180. package/esm/repositories/repository.manager.d.mts +99 -1
  181. package/esm/repositories/repository.manager.d.mts.map +1 -1
  182. package/esm/repositories/repository.manager.mjs +128 -0
  183. package/esm/repositories/repository.manager.mjs.map +1 -1
  184. package/esm/router/router.d.mts +9 -0
  185. package/esm/router/router.d.mts.map +1 -1
  186. package/esm/router/router.mjs +21 -1
  187. package/esm/router/router.mjs.map +1 -1
  188. package/esm/utils/paths.mjs +2 -2
  189. package/esm/utils/paths.mjs.map +1 -1
  190. package/esm/validation/validateAll.mjs +2 -2
  191. package/esm/validation/validateAll.mjs.map +1 -1
  192. package/llms-full.txt +427 -45
  193. package/llms.txt +4 -2
  194. package/package.json +9 -9
  195. package/skills/README.md +65 -0
  196. package/skills/health-checks/SKILL.md +13 -0
  197. package/skills/use-repository/SKILL.md +38 -1
  198. package/skills/warlock-doctor/SKILL.md +135 -0
  199. package/skills/warlock-routes/SKILL.md +91 -0
  200. package/skills/write-seeder/SKILL.md +143 -45
package/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ All notable changes to `@warlock.js/core` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 4.6.0
8
+
9
+ ### Added
10
+
11
+ - release-hygiene tests: version↔changelog invariant + generator-stub import check
12
+ - `router.routeCount()` exposes the number of registered routes as a boot/readiness signal
13
+ - `health.addRoutesRegisteredCheck(getRouteCount)` registers a readiness check that reports not-ready when a booted HTTP app has zero routes
14
+ - seeders now receive a `{ track }` context — `track(model)`, `track(models[])`, and `track(table, id)` register created records (each call returns its argument so it can be chained inline); `recordsCreated` is auto-derived from the track count
15
+ - `seed_records` table (created via the new `SeedRecordsTableMigration`) records every tracked seed reference within the same transaction the seed runs in; only the last run's refs are kept per seeder
16
+ - `warlock seed --drop [name]` undoes a seed: deletes its tracked records in reverse run/insertion order inside a transaction, then resets the matching seeds-log rows so `once: true` seeds re-run; scope to one seeder with `--drop=<name>`
17
+ - `Seeder.dependsOn` is now resolved — seeders are topologically sorted so dependencies run before dependents, layered over the numeric `order` tie-break; throws `UnknownSeederDependencyError` for a missing dependency and `SeederDependencyCycleError` for a cycle
18
+ - seeders receive an injectable clock and a meaningful batch size — `run({ track, now, batchSize })`; `now()` (default `() => new Date()`) drives both seed data and the seeds-log timestamps so historical/back-fill runs are deterministic, and `batchSize` surfaces the seeder's own `batchSize` for `Model.createMany(rows, { batchSize })`
19
+ - repository-level aggregation — `aggregate()`, `sum()`, `avg()`, `min()`, `max()`, and `groupBy()` on `RepositoryManager`, each reusing `filterBy` (and its operator-injection guard), `where`, and scopes before the aggregate, exactly like `count()`
20
+ - `warlock doctor` — a read-only diagnostics command that runs routes / config / connectors / optional-peers / health / release-hygiene checks and prints a pass/warn/fail report (exits non-zero on any failure, never opens a DB/cache/socket connection)
21
+ - `warlock routes` — a read-only command that lists the registered HTTP routes as a verb-colored table (method / path / name / action / middleware-count / source); filter with `--method` / `--path` / `--name`, or emit the normalized rows as JSON with `--json`. Boots app code to register routes but starts no connectors
22
+
23
+ ### Changed
24
+
25
+ - `Seeder.run` now receives a `SeedContext` (`run(ctx)`) — backward compatible, an existing zero-arg `run()` keeps working unchanged
26
+
27
+ ### Fixed
28
+
29
+ - route-module load/registration failures are no longer swallowed: a route file that throws on import or registration now surfaces loudly instead of silently 404'ing the whole surface
30
+ - `ModuleLoader.loadModule` rethrows after logging (wrapped in a new `ModuleLoadError` carrying the failing file + cause), so a broken module aborts boot and is caught loudly by the HMR batch-reload handler in dev
31
+ - `ModuleLoader.loadAll` aggregates per-file failures and throws an `AggregateError` at the end, so one broken module no longer hides the others
32
+ - `router.withSourceFile` rethrows the callback error after logging instead of consuming it with a bare `console.log` (the `try/finally` source-file stack cleanup is preserved)
33
+
7
34
  ## 4.5.0
8
35
 
9
36
  ### Changed
@@ -24,6 +24,8 @@ import { logAgentEvent } from "./agent-log-event.mjs";
24
24
  import { createAgentStream } from "./agent-stream.mjs";
25
25
  import { agentEventToStreamEvent } from "./agent-to-stream-event.mjs";
26
26
  import { JsonStreamGuard } from "./json-stream-guard.mjs";
27
+ import { computeAgentSignature } from "./signature.mjs";
28
+ import { deleteAgentSnapshot, loadAgentSnapshotForResume, persistAgentSnapshot } from "./snapshot.mjs";
27
29
  import { log } from "@warlock.js/logger";
28
30
 
29
31
  //#region ../@warlock.js/ai/src/agent/agent.ts
@@ -154,11 +156,23 @@ function agent(config) {
154
156
  tools
155
157
  }) : config.name;
156
158
  const skillsLib = config.skills ? isSkillsContract(config.skills) ? config.skills : skills(config.skills) : void 0;
159
+ const signature = computeAgentSignature({
160
+ name: isAnonymous ? void 0 : name,
161
+ version: config.version,
162
+ model: {
163
+ name: config.model?.name,
164
+ provider: config.model?.provider
165
+ },
166
+ tools,
167
+ maxTrips: config.maxTrips,
168
+ output: config.output
169
+ });
157
170
  const resolvedConfig = {
158
171
  ...config,
159
172
  name,
160
173
  tools,
161
- skillsLib
174
+ skillsLib,
175
+ signature
162
176
  };
163
177
  const instanceHandlers = /* @__PURE__ */ new Map();
164
178
  function on(event, handler) {
@@ -178,6 +192,7 @@ function agent(config) {
178
192
  name,
179
193
  isAnonymous,
180
194
  description: config.description,
195
+ signature,
181
196
  async execute(input, options) {
182
197
  return new Execution(resolvedConfig, input, options, void 0, instanceHandlers).run();
183
198
  },
@@ -186,6 +201,19 @@ function agent(config) {
186
201
  new Execution(resolvedConfig, input, options, controller, instanceHandlers).run();
187
202
  return stream;
188
203
  },
204
+ async resume(runId, options) {
205
+ const snapshot = await loadAgentSnapshotForResume({
206
+ durable: resolvedConfig.durable,
207
+ agentName: name,
208
+ signature,
209
+ runId,
210
+ options
211
+ });
212
+ return new Execution(resolvedConfig, snapshot.input, {
213
+ ...options,
214
+ runId
215
+ }, void 0, instanceHandlers, snapshot).run();
216
+ },
189
217
  on,
190
218
  off,
191
219
  eval(options) {
@@ -272,12 +300,13 @@ agent.judge = judgeAgent;
272
300
  * return stream;
273
301
  */
274
302
  var Execution = class {
275
- constructor(config, input, options, streamController, instanceHandlers) {
303
+ constructor(config, input, options, streamController, instanceHandlers, resumeFrom) {
276
304
  this.config = config;
277
305
  this.input = input;
278
306
  this.options = options;
279
307
  this.streamController = streamController;
280
308
  this.instanceHandlers = instanceHandlers;
309
+ this.resumeFrom = resumeFrom;
281
310
  this.trips = [];
282
311
  this.toolCalls = [];
283
312
  this.usage = {
@@ -286,15 +315,25 @@ var Execution = class {
286
315
  total: 0
287
316
  };
288
317
  this.messages = [];
289
- this.startedAt = /* @__PURE__ */ new Date();
290
318
  this.start = performance.now();
291
- this.runId = generateRunId("agent");
292
319
  this.logger = log;
293
320
  this.warnedHandlerEvents = /* @__PURE__ */ new Set();
294
321
  this.middlewareState = /* @__PURE__ */ new Map();
295
322
  this.maxTrips = config.maxTrips ?? 10;
296
323
  this.middleware = config.middleware ?? [];
297
324
  this.judgeConfig = resolveJudgeConfig(config.judge);
325
+ this.runId = resumeFrom?.runId ?? options?.runId ?? generateRunId("agent");
326
+ this.startedAt = resumeFrom ? new Date(resumeFrom.startedAt) : /* @__PURE__ */ new Date();
327
+ if (resumeFrom) {
328
+ this.messages.push(...resumeFrom.messages);
329
+ this.trips.push(...resumeFrom.trips);
330
+ this.toolCalls.push(...resumeFrom.toolCalls);
331
+ mergeUsage(this.usage, resumeFrom.usage);
332
+ this.systemPrompt = resumeFrom.systemPrompt;
333
+ this.responseSchema = resumeFrom.responseSchema;
334
+ this.promptName = resumeFrom.promptName;
335
+ this.promptVersion = resumeFrom.promptVersion;
336
+ }
298
337
  const skillTools = config.skillsLib ? normalizeAgentTools(config.skillsLib.tools(this.runId)) ?? [] : [];
299
338
  this.effectiveTools = [...config.tools ?? [], ...skillTools];
300
339
  }
@@ -358,14 +397,28 @@ var Execution = class {
358
397
  * with `error` populated when things went wrong.
359
398
  */
360
399
  async runCore() {
400
+ if (this.resumeFrom && this.resumeFrom.status === "completed") return this.rebuildResumedResult(this.resumeFrom);
361
401
  try {
362
- await this.buildInitialMessages();
363
- this.emit("agent.starting", { input: this.input });
402
+ if (!this.resumeFrom) {
403
+ await this.buildInitialMessages();
404
+ this.emit("agent.starting", { input: this.input });
405
+ }
364
406
  await this.runTripLoop();
365
407
  if (await this.parseOutput() === "failed" && this.resolveRepairAttempts() > 0) await this.runRepairLoop();
366
408
  } catch (thrown) {
367
409
  this.error = this.toAIError(thrown);
368
410
  }
411
+ await this.checkpoint(this.resolveSnapshotStatus());
412
+ if (!this.error && this.config.durable?.deleteOnComplete) {
413
+ const outcome = await deleteAgentSnapshot({
414
+ durable: this.config.durable,
415
+ runId: this.runId
416
+ });
417
+ if (!outcome.ok) this.logger.warn(LOG_MODULE, "snapshot.delete.failed", "durable snapshot delete failed", {
418
+ runId: this.runId,
419
+ error: outcome.error instanceof Error ? outcome.error.message : String(outcome.error)
420
+ });
421
+ }
369
422
  return this.buildResult();
370
423
  }
371
424
  /**
@@ -430,7 +483,7 @@ var Execution = class {
430
483
  * error so the caller can distinguish runaway tool loops from a real result.
431
484
  */
432
485
  async runTripLoop() {
433
- for (let tripIndex = 0; tripIndex < this.maxTrips; tripIndex++) {
486
+ for (let tripIndex = this.trips.length; tripIndex < this.maxTrips; tripIndex++) {
434
487
  if (this.options?.signal?.aborted) {
435
488
  this.error = this.makeCancelledError();
436
489
  return;
@@ -478,6 +531,7 @@ var Execution = class {
478
531
  this.trips.push(failedTrip);
479
532
  this.emit("agent.trip.completed", { trip: failedTrip });
480
533
  this.emit("agent.error", { error: this.error });
534
+ await this.checkpoint("failed");
481
535
  return "error";
482
536
  }
483
537
  if (response.usage.cost === void 0) response.usage.cost = computeCost(response.usage, this.config.model.pricing);
@@ -509,6 +563,7 @@ var Execution = class {
509
563
  };
510
564
  this.trips.push(trip);
511
565
  this.emit("agent.trip.completed", { trip });
566
+ await this.checkpoint("running");
512
567
  if (!isToolCallTrip) return "stop";
513
568
  return response.toolCalls.every((request) => {
514
569
  return this.effectiveTools.find((tool) => tool.name === request.name)?.mode === "silent";
@@ -919,6 +974,70 @@ var Execution = class {
919
974
  };
920
975
  }
921
976
  /**
977
+ * Map the run's terminal outcome to the persisted snapshot status.
978
+ * A cancelled error reads as `"cancelled"`, any other error as
979
+ * `"failed"`, otherwise `"completed"`. Mirrors the report-status
980
+ * mapping in {@link buildResult}.
981
+ */
982
+ resolveSnapshotStatus() {
983
+ if (!this.error) return "completed";
984
+ return this.error instanceof AgentCancelledError ? "cancelled" : "failed";
985
+ }
986
+ /**
987
+ * Build and persist an {@link AgentSnapshot} from the current
988
+ * accumulators. The per-trip and terminal checkpoints both route
989
+ * through here. Reuses {@link captureMessages} to normalize the live
990
+ * `Message[]` into JSON-safe form so the snapshot round-trips through
991
+ * any store backend.
992
+ *
993
+ * No-op (returns immediately) when `durable` is absent — the common
994
+ * non-durable path stays free. A failed persist is logged and
995
+ * swallowed (never aborts the run), matching the supervisor / workflow
996
+ * checkpoint policy.
997
+ */
998
+ async checkpoint(status) {
999
+ if (!this.config.durable) return;
1000
+ const outcome = await persistAgentSnapshot({
1001
+ durable: this.config.durable,
1002
+ runId: this.runId,
1003
+ agentName: this.config.name ?? this.config.model.name,
1004
+ signature: this.config.signature,
1005
+ version: this.config.version,
1006
+ input: this.input,
1007
+ systemPrompt: this.systemPrompt,
1008
+ responseSchema: this.responseSchema,
1009
+ promptName: this.promptName,
1010
+ promptVersion: this.promptVersion,
1011
+ messages: this.captureMessages(),
1012
+ trips: this.trips,
1013
+ toolCalls: this.toolCalls,
1014
+ usage: this.usage,
1015
+ status,
1016
+ startedAt: this.startedAt.toISOString()
1017
+ });
1018
+ if (!outcome.ok) this.logger.warn(LOG_MODULE, "snapshot.persist.failed", "durable snapshot persist failed", {
1019
+ runId: this.runId,
1020
+ status,
1021
+ error: outcome.error instanceof Error ? outcome.error.message : String(outcome.error)
1022
+ });
1023
+ }
1024
+ /**
1025
+ * Rebuild the final {@link AgentResult} from a COMPLETED snapshot
1026
+ * WITHOUT re-running anything. Used by the completed-run resume
1027
+ * short-circuit: the persisted trips / tool calls / usage are the
1028
+ * authoritative outcome, so a resume of a settled run re-returns that
1029
+ * outcome idempotently. Re-derives `this.data` from the final trip
1030
+ * output against the schema (cheap, no model call) so the rebuilt
1031
+ * result carries the same structured payload the original produced.
1032
+ *
1033
+ * Only reached for a `completed` snapshot — `failed` / `cancelled`
1034
+ * snapshots re-enter the trip loop to retry the remaining work instead.
1035
+ */
1036
+ async rebuildResumedResult(_snapshot) {
1037
+ await this.parseOutput();
1038
+ return this.buildResult();
1039
+ }
1040
+ /**
922
1041
  * Normalize the accumulated runtime `Message[]` into the JSON-safe
923
1042
  * {@link CapturedMessage}[] persisted on `AgentReport.messages` (F2).
924
1043
  * Flattens `ContentPart[]` content to a string, and forwards