@namzu/sdk 58.1.1 → 60.0.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 (312) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/dist/account/binding.d.ts +20 -0
  3. package/dist/account/binding.d.ts.map +1 -0
  4. package/dist/account/binding.js +27 -0
  5. package/dist/account/binding.js.map +1 -0
  6. package/dist/account/index.d.ts +9 -0
  7. package/dist/account/index.d.ts.map +1 -0
  8. package/dist/account/index.js +5 -0
  9. package/dist/account/index.js.map +1 -0
  10. package/dist/account/lock.d.ts +33 -0
  11. package/dist/account/lock.d.ts.map +1 -0
  12. package/dist/account/lock.js +170 -0
  13. package/dist/account/lock.js.map +1 -0
  14. package/dist/account/registry.d.ts +21 -0
  15. package/dist/account/registry.d.ts.map +1 -0
  16. package/dist/account/registry.js +340 -0
  17. package/dist/account/registry.js.map +1 -0
  18. package/dist/account/types.d.ts +124 -0
  19. package/dist/account/types.d.ts.map +1 -0
  20. package/dist/account/types.js +35 -0
  21. package/dist/account/types.js.map +1 -0
  22. package/dist/compaction/context-window.d.ts +53 -0
  23. package/dist/compaction/context-window.d.ts.map +1 -1
  24. package/dist/compaction/context-window.js +141 -2
  25. package/dist/compaction/context-window.js.map +1 -1
  26. package/dist/execution/workflow/index.d.ts +3 -1
  27. package/dist/execution/workflow/index.d.ts.map +1 -1
  28. package/dist/execution/workflow/index.js +2 -1
  29. package/dist/execution/workflow/index.js.map +1 -1
  30. package/dist/execution/workflow/journal.d.ts +24 -4
  31. package/dist/execution/workflow/journal.d.ts.map +1 -1
  32. package/dist/execution/workflow/journal.js +131 -9
  33. package/dist/execution/workflow/journal.js.map +1 -1
  34. package/dist/execution/workflow/prelude.d.ts +2 -0
  35. package/dist/execution/workflow/prelude.d.ts.map +1 -1
  36. package/dist/execution/workflow/prelude.js +49 -8
  37. package/dist/execution/workflow/prelude.js.map +1 -1
  38. package/dist/execution/workflow/resume.d.ts +59 -0
  39. package/dist/execution/workflow/resume.d.ts.map +1 -0
  40. package/dist/execution/workflow/resume.js +273 -0
  41. package/dist/execution/workflow/resume.js.map +1 -0
  42. package/dist/execution/workflow/runtime.d.ts.map +1 -1
  43. package/dist/execution/workflow/runtime.js +112 -8
  44. package/dist/execution/workflow/runtime.js.map +1 -1
  45. package/dist/execution/workflow/types.d.ts +105 -1
  46. package/dist/execution/workflow/types.d.ts.map +1 -1
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +4 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/pals/communication/ingress-schema.d.ts +38 -38
  52. package/dist/peers/record.d.ts +98 -0
  53. package/dist/peers/record.d.ts.map +1 -1
  54. package/dist/peers/record.js +17 -0
  55. package/dist/peers/record.js.map +1 -1
  56. package/dist/peers/registry.d.ts.map +1 -1
  57. package/dist/peers/registry.js +2 -2
  58. package/dist/peers/registry.js.map +1 -1
  59. package/dist/plugin/compat.d.ts +24 -0
  60. package/dist/plugin/compat.d.ts.map +1 -0
  61. package/dist/plugin/compat.js +149 -0
  62. package/dist/plugin/compat.js.map +1 -0
  63. package/dist/plugin/loader.d.ts +2 -1
  64. package/dist/plugin/loader.d.ts.map +1 -1
  65. package/dist/plugin/loader.js +6 -0
  66. package/dist/plugin/loader.js.map +1 -1
  67. package/dist/pricing/index.d.ts.map +1 -1
  68. package/dist/pricing/index.js +2 -1
  69. package/dist/pricing/index.js.map +1 -1
  70. package/dist/process/kill-tree.d.ts +44 -0
  71. package/dist/process/kill-tree.d.ts.map +1 -1
  72. package/dist/process/kill-tree.js +102 -4
  73. package/dist/process/kill-tree.js.map +1 -1
  74. package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
  75. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  76. package/dist/prompt/coding-agent-doctrine.js +9 -1
  77. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  78. package/dist/provider/errors.d.ts.map +1 -1
  79. package/dist/provider/errors.js +25 -2
  80. package/dist/provider/errors.js.map +1 -1
  81. package/dist/provider/model-window-suffix.d.ts +18 -0
  82. package/dist/provider/model-window-suffix.d.ts.map +1 -0
  83. package/dist/provider/model-window-suffix.js +77 -0
  84. package/dist/provider/model-window-suffix.js.map +1 -0
  85. package/dist/provider/token-budget.d.ts.map +1 -1
  86. package/dist/provider/token-budget.js +9 -1
  87. package/dist/provider/token-budget.js.map +1 -1
  88. package/dist/public-runtime.d.ts +6 -3
  89. package/dist/public-runtime.d.ts.map +1 -1
  90. package/dist/public-runtime.js +6 -4
  91. package/dist/public-runtime.js.map +1 -1
  92. package/dist/public-types.d.ts +3 -0
  93. package/dist/public-types.d.ts.map +1 -1
  94. package/dist/registry/command/kernel-commands.d.ts +1 -4
  95. package/dist/registry/command/kernel-commands.d.ts.map +1 -1
  96. package/dist/registry/command/kernel-commands.js +0 -28
  97. package/dist/registry/command/kernel-commands.js.map +1 -1
  98. package/dist/runtime/jobs/awaited-jobs.d.ts +6 -0
  99. package/dist/runtime/jobs/awaited-jobs.d.ts.map +1 -1
  100. package/dist/runtime/jobs/awaited-jobs.js +11 -0
  101. package/dist/runtime/jobs/awaited-jobs.js.map +1 -1
  102. package/dist/runtime/jobs/host-exit-finalizer.d.ts +20 -0
  103. package/dist/runtime/jobs/host-exit-finalizer.d.ts.map +1 -0
  104. package/dist/runtime/jobs/host-exit-finalizer.js +57 -0
  105. package/dist/runtime/jobs/host-exit-finalizer.js.map +1 -0
  106. package/dist/runtime/jobs/liveness.d.ts +67 -0
  107. package/dist/runtime/jobs/liveness.d.ts.map +1 -0
  108. package/dist/runtime/jobs/liveness.js +94 -0
  109. package/dist/runtime/jobs/liveness.js.map +1 -0
  110. package/dist/runtime/jobs/registry.d.ts +21 -0
  111. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  112. package/dist/runtime/jobs/registry.js +104 -2
  113. package/dist/runtime/jobs/registry.js.map +1 -1
  114. package/dist/runtime/jobs/wake-budget.d.ts +35 -0
  115. package/dist/runtime/jobs/wake-budget.d.ts.map +1 -0
  116. package/dist/runtime/jobs/wake-budget.js +48 -0
  117. package/dist/runtime/jobs/wake-budget.js.map +1 -0
  118. package/dist/runtime/query/detached-calls.d.ts +65 -0
  119. package/dist/runtime/query/detached-calls.d.ts.map +1 -0
  120. package/dist/runtime/query/detached-calls.js +116 -0
  121. package/dist/runtime/query/detached-calls.js.map +1 -0
  122. package/dist/runtime/query/executor.d.ts +11 -3
  123. package/dist/runtime/query/executor.d.ts.map +1 -1
  124. package/dist/runtime/query/executor.js +100 -8
  125. package/dist/runtime/query/executor.js.map +1 -1
  126. package/dist/runtime/query/index.d.ts +20 -0
  127. package/dist/runtime/query/index.d.ts.map +1 -1
  128. package/dist/runtime/query/index.js +31 -5
  129. package/dist/runtime/query/index.js.map +1 -1
  130. package/dist/runtime/query/iteration/outstanding-work.d.ts.map +1 -1
  131. package/dist/runtime/query/iteration/outstanding-work.js +11 -3
  132. package/dist/runtime/query/iteration/outstanding-work.js.map +1 -1
  133. package/dist/runtime/query/iteration/phases/context.d.ts +7 -0
  134. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  135. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  137. package/dist/runtime/query/iteration/phases/tool-review.js +63 -1
  138. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  139. package/dist/runtime/query/session-storage.d.ts.map +1 -1
  140. package/dist/runtime/query/session-storage.js +8 -3
  141. package/dist/runtime/query/session-storage.js.map +1 -1
  142. package/dist/runtime/query/steering.d.ts +29 -3
  143. package/dist/runtime/query/steering.d.ts.map +1 -1
  144. package/dist/runtime/query/steering.js +58 -3
  145. package/dist/runtime/query/steering.js.map +1 -1
  146. package/dist/runtime/query/tooling.d.ts +1 -0
  147. package/dist/runtime/query/tooling.d.ts.map +1 -1
  148. package/dist/runtime/query/tooling.js +1 -0
  149. package/dist/runtime/query/tooling.js.map +1 -1
  150. package/dist/runtime/system-events.d.ts +5 -0
  151. package/dist/runtime/system-events.d.ts.map +1 -1
  152. package/dist/runtime/system-events.js +7 -1
  153. package/dist/runtime/system-events.js.map +1 -1
  154. package/dist/sandbox/denial.d.ts +11 -0
  155. package/dist/sandbox/denial.d.ts.map +1 -0
  156. package/dist/sandbox/denial.js +61 -0
  157. package/dist/sandbox/denial.js.map +1 -0
  158. package/dist/sandbox/file-walk-program.d.ts.map +1 -1
  159. package/dist/sandbox/file-walk-program.js +31 -13
  160. package/dist/sandbox/file-walk-program.js.map +1 -1
  161. package/dist/sandbox/file-walk.d.ts +2 -0
  162. package/dist/sandbox/file-walk.d.ts.map +1 -1
  163. package/dist/sandbox/file-walk.js +64 -13
  164. package/dist/sandbox/file-walk.js.map +1 -1
  165. package/dist/sandbox/ignore-matcher.d.ts +30 -0
  166. package/dist/sandbox/ignore-matcher.d.ts.map +1 -0
  167. package/dist/sandbox/ignore-matcher.js +11 -0
  168. package/dist/sandbox/ignore-matcher.js.map +1 -0
  169. package/dist/sandbox/ignore-source.d.ts +26 -0
  170. package/dist/sandbox/ignore-source.d.ts.map +1 -0
  171. package/dist/sandbox/ignore-source.js +221 -0
  172. package/dist/sandbox/ignore-source.js.map +1 -0
  173. package/dist/sandbox/provider/local.d.ts.map +1 -1
  174. package/dist/sandbox/provider/local.js +11 -1
  175. package/dist/sandbox/provider/local.js.map +1 -1
  176. package/dist/scheduler/completion-inbox.d.ts.map +1 -1
  177. package/dist/scheduler/completion-inbox.js +1 -0
  178. package/dist/scheduler/completion-inbox.js.map +1 -1
  179. package/dist/session/log-hash.d.ts.map +1 -1
  180. package/dist/session/log-hash.js +18 -1
  181. package/dist/session/log-hash.js.map +1 -1
  182. package/dist/store/session-index/sqlite.d.ts.map +1 -1
  183. package/dist/store/session-index/sqlite.js +169 -16
  184. package/dist/store/session-index/sqlite.js.map +1 -1
  185. package/dist/tools/builtins/bash.d.ts.map +1 -1
  186. package/dist/tools/builtins/bash.js +10 -4
  187. package/dist/tools/builtins/bash.js.map +1 -1
  188. package/dist/tools/builtins/computer-use.d.ts +35 -0
  189. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  190. package/dist/tools/builtins/computer-use.js +512 -67
  191. package/dist/tools/builtins/computer-use.js.map +1 -1
  192. package/dist/tools/builtins/glob.d.ts +1 -0
  193. package/dist/tools/builtins/glob.d.ts.map +1 -1
  194. package/dist/tools/builtins/glob.js +65 -28
  195. package/dist/tools/builtins/glob.js.map +1 -1
  196. package/dist/tools/builtins/grep.d.ts +2 -0
  197. package/dist/tools/builtins/grep.d.ts.map +1 -1
  198. package/dist/tools/builtins/grep.js +94 -40
  199. package/dist/tools/builtins/grep.js.map +1 -1
  200. package/dist/tools/builtins/job.d.ts +1 -1
  201. package/dist/tools/builtins/job.d.ts.map +1 -1
  202. package/dist/tools/builtins/job.js +34 -7
  203. package/dist/tools/builtins/job.js.map +1 -1
  204. package/dist/tools/builtins/search-support.d.ts +44 -0
  205. package/dist/tools/builtins/search-support.d.ts.map +1 -0
  206. package/dist/tools/builtins/search-support.js +89 -0
  207. package/dist/tools/builtins/search-support.js.map +1 -0
  208. package/dist/tools/builtins/wait-for-job-bounds.d.ts +2 -0
  209. package/dist/tools/builtins/wait-for-job-bounds.d.ts.map +1 -1
  210. package/dist/tools/builtins/wait-for-job-bounds.js +3 -1
  211. package/dist/tools/builtins/wait-for-job-bounds.js.map +1 -1
  212. package/dist/tools/builtins/wait-for-job.d.ts +1 -1
  213. package/dist/tools/builtins/wait-for-job.d.ts.map +1 -1
  214. package/dist/tools/builtins/wait-for-job.js +13 -2
  215. package/dist/tools/builtins/wait-for-job.js.map +1 -1
  216. package/dist/tools/command-shell.d.ts.map +1 -1
  217. package/dist/tools/command-shell.js +14 -5
  218. package/dist/tools/command-shell.js.map +1 -1
  219. package/dist/types/computer-use/index.d.ts +129 -13
  220. package/dist/types/computer-use/index.d.ts.map +1 -1
  221. package/dist/types/computer-use/index.js.map +1 -1
  222. package/dist/types/errors/catalog.d.ts.map +1 -1
  223. package/dist/types/errors/catalog.js +6 -0
  224. package/dist/types/errors/catalog.js.map +1 -1
  225. package/dist/types/plugin/index.d.ts +24 -0
  226. package/dist/types/plugin/index.d.ts.map +1 -1
  227. package/dist/types/plugin/index.js +4 -0
  228. package/dist/types/plugin/index.js.map +1 -1
  229. package/dist/types/provider/errors.d.ts +7 -0
  230. package/dist/types/provider/errors.d.ts.map +1 -1
  231. package/dist/types/provider/errors.js +27 -3
  232. package/dist/types/provider/errors.js.map +1 -1
  233. package/dist/types/sandbox/index.d.ts +38 -1
  234. package/dist/types/sandbox/index.d.ts.map +1 -1
  235. package/dist/types/sandbox/index.js.map +1 -1
  236. package/dist/types/session/records.d.ts +479 -1
  237. package/dist/types/session/records.d.ts.map +1 -1
  238. package/dist/types/session/records.js +45 -1
  239. package/dist/types/session/records.js.map +1 -1
  240. package/dist/types/tool/index.d.ts +30 -0
  241. package/dist/types/tool/index.d.ts.map +1 -1
  242. package/dist/types/tool/index.js.map +1 -1
  243. package/dist/types/tool/presentation.d.ts +8 -0
  244. package/dist/types/tool/presentation.d.ts.map +1 -1
  245. package/package.json +1 -1
  246. package/src/account/binding.ts +43 -0
  247. package/src/account/index.ts +32 -0
  248. package/src/account/lock.ts +184 -0
  249. package/src/account/registry.ts +413 -0
  250. package/src/account/types.ts +137 -0
  251. package/src/compaction/context-window.ts +163 -2
  252. package/src/execution/workflow/index.ts +14 -1
  253. package/src/execution/workflow/journal.ts +161 -11
  254. package/src/execution/workflow/prelude.ts +51 -8
  255. package/src/execution/workflow/resume.ts +313 -0
  256. package/src/execution/workflow/runtime.ts +127 -9
  257. package/src/execution/workflow/types.ts +109 -1
  258. package/src/index.ts +17 -0
  259. package/src/peers/record.ts +18 -0
  260. package/src/peers/registry.ts +2 -2
  261. package/src/plugin/compat.ts +173 -0
  262. package/src/plugin/loader.ts +7 -1
  263. package/src/pricing/index.ts +2 -1
  264. package/src/process/kill-tree.ts +106 -4
  265. package/src/prompt/coding-agent-doctrine.ts +9 -1
  266. package/src/provider/errors.ts +27 -1
  267. package/src/provider/model-window-suffix.ts +84 -0
  268. package/src/provider/token-budget.ts +9 -3
  269. package/src/public-runtime.ts +38 -2
  270. package/src/public-types.ts +16 -0
  271. package/src/registry/command/kernel-commands.ts +1 -32
  272. package/src/runtime/jobs/awaited-jobs.ts +11 -0
  273. package/src/runtime/jobs/host-exit-finalizer.ts +67 -0
  274. package/src/runtime/jobs/liveness.ts +144 -0
  275. package/src/runtime/jobs/registry.ts +114 -2
  276. package/src/runtime/jobs/wake-budget.ts +55 -0
  277. package/src/runtime/query/detached-calls.ts +135 -0
  278. package/src/runtime/query/executor.ts +102 -12
  279. package/src/runtime/query/index.ts +50 -7
  280. package/src/runtime/query/iteration/outstanding-work.ts +12 -3
  281. package/src/runtime/query/iteration/phases/context.ts +7 -0
  282. package/src/runtime/query/iteration/phases/tool-review.ts +73 -8
  283. package/src/runtime/query/session-storage.ts +8 -3
  284. package/src/runtime/query/steering.ts +77 -6
  285. package/src/runtime/query/tooling.ts +2 -0
  286. package/src/runtime/system-events.ts +12 -1
  287. package/src/sandbox/denial.ts +79 -0
  288. package/src/sandbox/file-walk-program.ts +32 -13
  289. package/src/sandbox/file-walk.ts +75 -12
  290. package/src/sandbox/ignore-matcher.ts +46 -0
  291. package/src/sandbox/ignore-source.ts +220 -0
  292. package/src/sandbox/provider/local.ts +11 -1
  293. package/src/scheduler/completion-inbox.ts +1 -0
  294. package/src/session/log-hash.ts +21 -0
  295. package/src/store/session-index/sqlite.ts +171 -16
  296. package/src/tools/builtins/bash.ts +11 -4
  297. package/src/tools/builtins/computer-use.ts +593 -68
  298. package/src/tools/builtins/glob.ts +79 -32
  299. package/src/tools/builtins/grep.ts +107 -43
  300. package/src/tools/builtins/job.ts +42 -12
  301. package/src/tools/builtins/search-support.ts +107 -0
  302. package/src/tools/builtins/wait-for-job-bounds.ts +5 -1
  303. package/src/tools/builtins/wait-for-job.ts +14 -2
  304. package/src/tools/command-shell.ts +13 -5
  305. package/src/types/computer-use/index.ts +157 -15
  306. package/src/types/errors/catalog.ts +6 -0
  307. package/src/types/plugin/index.ts +8 -0
  308. package/src/types/provider/errors.ts +40 -3
  309. package/src/types/sandbox/index.ts +40 -1
  310. package/src/types/session/records.ts +52 -2
  311. package/src/types/tool/index.ts +30 -0
  312. package/src/types/tool/presentation.ts +8 -0
@@ -0,0 +1,59 @@
1
+ import type { CheckedWorkflowJournal, WorkflowJournalRecord, WorkflowResumeState } from './types.js';
2
+ /** What of an `agent()` call decides its result. `label`, `phase` and progress callbacks do not. */
3
+ export interface WorkflowCallIdentity {
4
+ readonly schema?: unknown;
5
+ readonly model?: string;
6
+ readonly effort?: string;
7
+ readonly agentType?: string;
8
+ }
9
+ /**
10
+ * The key a call is saved and looked up under: the prompt, the options that
11
+ * change what the agent does (schema, model, effort, agent type) and how many
12
+ * identical calls came before it in the same pass. Each part is length-prefixed
13
+ * so one cannot be read as part of another.
14
+ */
15
+ export declare function workflowCallKey(prompt: string, call: WorkflowCallIdentity, ordinal: number): string;
16
+ /**
17
+ * What makes two calls the same call: the prompt and the options that change
18
+ * the agent, in a form where key order and `undefined` do not matter. The
19
+ * runtime counts repeats of this text to give identical calls distinct slots,
20
+ * and the key hashes the same text, so the two can never disagree.
21
+ */
22
+ export declare function workflowCallContent(prompt: string, call: WorkflowCallIdentity): string;
23
+ /** The hash the first line of a run's journal chains from. It binds the file to its run id. */
24
+ export declare function workflowJournalSeed(runId: string): string;
25
+ /** The hash a line contributes to the next one: its exact bytes, newline included. */
26
+ export declare function workflowJournalLineHash(line: string): string;
27
+ /** The line `record` is written as. With a key, a `mac` over the rest of the line is added last. */
28
+ export declare function formatWorkflowJournalLine(record: WorkflowJournalRecord, seq: number, prev: string, key?: string | Uint8Array): string;
29
+ /**
30
+ * Reads a journal's text and keeps only the records that can be trusted.
31
+ *
32
+ * Every line carries `seq` (counting trusted lines from 1) and `prev` (the hash
33
+ * of the line before; the first chains from a seed bound to the run id). A
34
+ * line that does not chain, a line that does not parse, and anything after
35
+ * one is dropped until a `run_started` of a resumed pass picks the chain up
36
+ * again at the last trusted line. A last line without its newline that is not
37
+ * whole JSON is a torn write and is ignored; one that is whole JSON is read
38
+ * like any other line. The file is never rewritten.
39
+ *
40
+ * The chain alone cannot notice an edit to the final line (nothing chains from
41
+ * it yet). With `key`, every line must also carry a `mac` made with that
42
+ * secret, which closes that gap against anyone who does not hold the key.
43
+ */
44
+ export declare function readWorkflowJournalChecked(text: string, runId: string, options?: {
45
+ readonly key?: string | Uint8Array;
46
+ }): CheckedWorkflowJournal;
47
+ /**
48
+ * Folds a checked journal into what a resumed pass needs.
49
+ *
50
+ * The last saved result for a key wins. An agent that was asked for again
51
+ * after a result was saved gives that result up until it finishes again. A
52
+ * failed or skipped outcome removes the result and marks the key failed; an
53
+ * outcome the run recorded as interrupted (stopped, not failed) leaves the key
54
+ * as seen but unfinished. Lines marked `cached` repeat an earlier result and
55
+ * are skipped, and so is any agent record from a pass older than the latest
56
+ * `run_started`, which a slow agent of an earlier pass can still write.
57
+ */
58
+ export declare function loadWorkflowResume(checked: CheckedWorkflowJournal): WorkflowResumeState;
59
+ //# sourceMappingURL=resume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/execution/workflow/resume.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,sBAAsB,EAEtB,qBAAqB,EACrB,mBAAmB,EACnB,MAAM,YAAY,CAAA;AAsBnB,oGAAoG;AACpG,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,MAAM,GACb,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAQtF;AAED,+FAA+F;AAC/F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,oGAAoG;AACpG,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,qBAAqB,EAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,GACvB,MAAM,CAKR;AAqBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAAO,GAClD,sBAAsB,CAgFxB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,sBAAsB,GAAG,mBAAmB,CAsFvF"}
@@ -0,0 +1,273 @@
1
+ import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
2
+ /** The key version. A change to how keys are made bumps it, so old keys simply never match. */
3
+ const KEY_VERSION = 'k1';
4
+ function sha256Hex(text) {
5
+ return createHash('sha256').update(text).digest('hex');
6
+ }
7
+ /** JSON with every object's keys sorted and `__proto__` dropped, so equal values give equal text. */
8
+ function canonicalJson(value) {
9
+ if (Array.isArray(value))
10
+ return `[${value.map((item) => canonicalJson(item)).join(',')}]`;
11
+ if (typeof value === 'object' && value !== null) {
12
+ const record = value;
13
+ const keys = Object.keys(record)
14
+ .filter((key) => key !== '__proto__' && record[key] !== undefined)
15
+ .sort();
16
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(',')}}`;
17
+ }
18
+ return JSON.stringify(value) ?? 'null';
19
+ }
20
+ /**
21
+ * The key a call is saved and looked up under: the prompt, the options that
22
+ * change what the agent does (schema, model, effort, agent type) and how many
23
+ * identical calls came before it in the same pass. Each part is length-prefixed
24
+ * so one cannot be read as part of another.
25
+ */
26
+ export function workflowCallKey(prompt, call, ordinal) {
27
+ return `${KEY_VERSION}:${sha256Hex(`${workflowCallContent(prompt, call)}${ordinal}`)}`;
28
+ }
29
+ /**
30
+ * What makes two calls the same call: the prompt and the options that change
31
+ * the agent, in a form where key order and `undefined` do not matter. The
32
+ * runtime counts repeats of this text to give identical calls distinct slots,
33
+ * and the key hashes the same text, so the two can never disagree.
34
+ */
35
+ export function workflowCallContent(prompt, call) {
36
+ const options = canonicalJson({
37
+ schema: call.schema,
38
+ model: call.model,
39
+ effort: call.effort,
40
+ agentType: call.agentType,
41
+ });
42
+ return `${prompt.length}:${prompt}${options.length}:${options}`;
43
+ }
44
+ /** The hash the first line of a run's journal chains from. It binds the file to its run id. */
45
+ export function workflowJournalSeed(runId) {
46
+ return sha256Hex(`namzu-workflow-journal\0${runId}`);
47
+ }
48
+ /** The hash a line contributes to the next one: its exact bytes, newline included. */
49
+ export function workflowJournalLineHash(line) {
50
+ return sha256Hex(`${line}\n`);
51
+ }
52
+ /** The line `record` is written as. With a key, a `mac` over the rest of the line is added last. */
53
+ export function formatWorkflowJournalLine(record, seq, prev, key) {
54
+ const body = { seq, prev, ...record };
55
+ if (key === undefined)
56
+ return JSON.stringify(body);
57
+ const mac = createHmac('sha256', key).update(JSON.stringify(body)).digest('hex');
58
+ return JSON.stringify({ ...body, mac });
59
+ }
60
+ function macMatches(record, key) {
61
+ const { mac, ...rest } = record;
62
+ if (typeof mac !== 'string')
63
+ return false;
64
+ const expected = createHmac('sha256', key).update(JSON.stringify(rest)).digest();
65
+ const actual = Buffer.from(mac, 'hex');
66
+ return actual.length === expected.length && timingSafeEqual(actual, expected);
67
+ }
68
+ function parseLine(line) {
69
+ try {
70
+ const value = JSON.parse(line);
71
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
72
+ ? value
73
+ : undefined;
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ /**
80
+ * Reads a journal's text and keeps only the records that can be trusted.
81
+ *
82
+ * Every line carries `seq` (counting trusted lines from 1) and `prev` (the hash
83
+ * of the line before; the first chains from a seed bound to the run id). A
84
+ * line that does not chain, a line that does not parse, and anything after
85
+ * one is dropped until a `run_started` of a resumed pass picks the chain up
86
+ * again at the last trusted line. A last line without its newline that is not
87
+ * whole JSON is a torn write and is ignored; one that is whole JSON is read
88
+ * like any other line. The file is never rewritten.
89
+ *
90
+ * The chain alone cannot notice an edit to the final line (nothing chains from
91
+ * it yet). With `key`, every line must also carry a `mac` made with that
92
+ * secret, which closes that gap against anyone who does not hold the key.
93
+ */
94
+ export function readWorkflowJournalChecked(text, runId, options = {}) {
95
+ const endsWithNewline = text === '' || text.endsWith('\n');
96
+ const pieces = text.split('\n');
97
+ const fragment = endsWithNewline ? undefined : pieces.pop();
98
+ if (endsWithNewline)
99
+ pieces.pop();
100
+ // A last line without its newline that is whole JSON was only missing the newline: it is
101
+ // judged like any other line (the writer adds the newline before the next record, so the
102
+ // hash it contributes is the one of the line with its newline). Anything else is a torn write.
103
+ const wholeFragment = fragment !== undefined && fragment !== '' && parseLine(fragment) !== undefined;
104
+ if (wholeFragment)
105
+ pieces.push(fragment);
106
+ const tornTail = fragment !== undefined && fragment !== '' && !wholeFragment;
107
+ const parsed = pieces.map((line) => parseLine(line));
108
+ const firstRecord = parsed.find((record) => record !== undefined);
109
+ if (firstRecord !== undefined && typeof firstRecord.seq !== 'number') {
110
+ return {
111
+ records: [],
112
+ legacy: true,
113
+ tornTail,
114
+ lastSeq: 0,
115
+ lastHash: workflowJournalSeed(runId),
116
+ endsWithNewline,
117
+ untrusted: parsed.filter((record) => record !== undefined),
118
+ };
119
+ }
120
+ const records = [];
121
+ let lastSeq = 0;
122
+ let lastHash = workflowJournalSeed(runId);
123
+ let gapStart;
124
+ let gapLines = 0;
125
+ let gapParsed = false;
126
+ let brokenAt;
127
+ const closeGap = () => {
128
+ if (gapStart !== undefined && (gapLines > 1 || gapParsed))
129
+ brokenAt ??= gapStart;
130
+ gapStart = undefined;
131
+ gapLines = 0;
132
+ gapParsed = false;
133
+ };
134
+ for (let i = 0; i < pieces.length; i++) {
135
+ const line = pieces[i];
136
+ const record = parsed[i];
137
+ const chains = record !== undefined &&
138
+ record.seq === lastSeq + 1 &&
139
+ record.prev === lastHash &&
140
+ (options.key === undefined || macMatches(record, options.key));
141
+ const first = records.length === 0;
142
+ const acceptable = chains &&
143
+ (first
144
+ ? record.type === 'run_started' && record.runId === runId
145
+ : gapStart === undefined || record.type === 'run_started');
146
+ if (acceptable) {
147
+ closeGap();
148
+ records.push(record);
149
+ lastSeq = record.seq;
150
+ lastHash = workflowJournalLineHash(line);
151
+ continue;
152
+ }
153
+ if (gapStart === undefined)
154
+ gapStart = i + 1;
155
+ gapLines++;
156
+ if (record !== undefined)
157
+ gapParsed = true;
158
+ }
159
+ // A gap that runs to the end is a change, except a single line that does not parse
160
+ // (what a torn write leaves once the next pass closed it with a newline).
161
+ closeGap();
162
+ return {
163
+ records,
164
+ legacy: false,
165
+ tornTail,
166
+ ...(brokenAt !== undefined ? { brokenAt } : {}),
167
+ lastSeq,
168
+ lastHash,
169
+ endsWithNewline,
170
+ untrusted: [],
171
+ };
172
+ }
173
+ function hasResult(record) {
174
+ return Object.hasOwn(record, 'result') && record.result !== undefined && record.result !== null;
175
+ }
176
+ /**
177
+ * Folds a checked journal into what a resumed pass needs.
178
+ *
179
+ * The last saved result for a key wins. An agent that was asked for again
180
+ * after a result was saved gives that result up until it finishes again. A
181
+ * failed or skipped outcome removes the result and marks the key failed; an
182
+ * outcome the run recorded as interrupted (stopped, not failed) leaves the key
183
+ * as seen but unfinished. Lines marked `cached` repeat an earlier result and
184
+ * are skipped, and so is any agent record from a pass older than the latest
185
+ * `run_started`, which a slow agent of an earlier pass can still write.
186
+ */
187
+ export function loadWorkflowResume(checked) {
188
+ const results = new Map();
189
+ const seen = new Set();
190
+ const failed = new Set();
191
+ const demoted = new Set();
192
+ const problems = [];
193
+ const keysByIndex = new Map();
194
+ let currentPass = 0;
195
+ let scriptSha256;
196
+ let previousFinish = 'unfinished';
197
+ for (const record of checked.records) {
198
+ if (record.type === 'run_started') {
199
+ currentPass = record.resume?.pass ?? 1;
200
+ scriptSha256 = record.scriptSha256;
201
+ previousFinish = 'unfinished';
202
+ keysByIndex.clear();
203
+ continue;
204
+ }
205
+ if (record.type === 'run_finished') {
206
+ previousFinish = record.status;
207
+ continue;
208
+ }
209
+ if (record.type !== 'agent_request' && record.type !== 'agent_outcome')
210
+ continue;
211
+ const pass = record.pass ?? 1;
212
+ if (pass < currentPass || record.cached === true)
213
+ continue;
214
+ if (record.type === 'agent_request') {
215
+ if (record.key === undefined)
216
+ continue;
217
+ keysByIndex.set(`${pass}:${record.index}`, record.key);
218
+ seen.add(record.key);
219
+ if (results.delete(record.key))
220
+ demoted.add(record.key);
221
+ continue;
222
+ }
223
+ const key = record.key ?? keysByIndex.get(`${pass}:${record.index}`);
224
+ if (key === undefined)
225
+ continue;
226
+ seen.add(key);
227
+ if (record.state === 'done' && hasResult(record)) {
228
+ results.set(key, {
229
+ value: record.result,
230
+ pass,
231
+ index: record.index,
232
+ ...(record.childSessionId !== undefined ? { childSessionId: record.childSessionId } : {}),
233
+ });
234
+ failed.delete(key);
235
+ demoted.delete(key);
236
+ }
237
+ else if (record.state === 'done' && record.unsaved === true) {
238
+ results.delete(key);
239
+ demoted.add(key);
240
+ }
241
+ else if (record.interrupted === true) {
242
+ // Stopped, not failed: the agent runs again and the cache stays open.
243
+ results.delete(key);
244
+ }
245
+ else {
246
+ results.delete(key);
247
+ demoted.delete(key);
248
+ failed.add(key);
249
+ }
250
+ }
251
+ if (checked.legacy) {
252
+ problems.push('This run was saved before resuming was possible, so its agents cannot be reused.');
253
+ }
254
+ if (checked.brokenAt !== undefined) {
255
+ problems.push(`The journal was changed or cut short at line ${checked.brokenAt}. Results after that point are not trusted and those agents will run again.`);
256
+ }
257
+ const started = checked.records.some((record) => record.type === 'run_started');
258
+ if (!checked.legacy && !started)
259
+ problems.push('The journal has no usable record of the run.');
260
+ return {
261
+ pass: currentPass + 1,
262
+ results,
263
+ seen,
264
+ failed,
265
+ demoted,
266
+ ...(scriptSha256 !== undefined ? { scriptSha256 } : {}),
267
+ previousFinish,
268
+ problems,
269
+ resumable: !checked.legacy && started,
270
+ ...(checked.brokenAt !== undefined ? { brokenAt: checked.brokenAt } : {}),
271
+ };
272
+ }
273
+ //# sourceMappingURL=resume.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../src/execution/workflow/resume.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/execution/workflow/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,WAAW,EAAkB,MAAM,0BAA0B,CAAA;AAQtF,OAAO,KAAK,EAQX,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAEjB,MAAM,YAAY,CAAA;AAEnB,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,UAAU,CAAA;AAoBzC,sHAAsH;AACtH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvE;AASD,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,cAAmB,GAAG,sBAAsB,CAmCzF;AA+GD,MAAM,WAAW,sBAAsB;IACtC,4FAA4F;IAC5F,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAA;IAClC,oCAAoC;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAC3B,qGAAqG;IACrG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAC/B;AAkCD;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAGf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;gBAEL,OAAO,GAAE,sBAA2B;IAI3D,GAAG,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA6gBlE"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/execution/workflow/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,WAAW,EAAkB,MAAM,0BAA0B,CAAA;AAStF,OAAO,KAAK,EAQX,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAEjB,MAAM,YAAY,CAAA;AAEnB,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,UAAU,CAAA;AAoBzC,sHAAsH;AACtH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvE;AASD,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,cAAmB,GAAG,sBAAsB,CAqCzF;AAuHD,MAAM,WAAW,sBAAsB;IACtC,4FAA4F;IAC5F,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAA;IAClC,oCAAoC;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAC3B,qGAAqG;IACrG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAC/B;AAkCD;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAGf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;gBAEL,OAAO,GAAE,sBAA2B;IAI3D,GAAG,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAwnBlE"}
@@ -7,6 +7,7 @@ import { preview, sha256 } from './journal.js';
7
7
  import { splitWorkflowScript } from './meta.js';
8
8
  import { PhaseRegistry, summarizeWorkflowPhases } from './phases.js';
9
9
  import { buildPrelude, preludeLineCount } from './prelude.js';
10
+ import { workflowCallContent, workflowCallKey } from './resume.js';
10
11
  import { parseJsonText, validateAgainstSchema } from './schema.js';
11
12
  /** The one capability a script has. */
12
13
  export const WORKFLOW_HOST_CALL = 'agent';
@@ -47,7 +48,9 @@ export function resolveWorkflowLimits(limits = {}) {
47
48
  memoryLimitBytes: integer('memoryLimitBytes', limits.memoryLimitBytes ?? 64 * 1024 * 1024, 16 * 1024 * 1024, 256 * 1024 * 1024),
48
49
  maxValueBytes: integer('maxValueBytes', limits.maxValueBytes ?? 1024 * 1024, 1, 16 * 1024 * 1024),
49
50
  ...(limits.tokenBudget !== undefined
50
- ? { tokenBudget: integer('tokenBudget', limits.tokenBudget, 1, Number.MAX_SAFE_INTEGER) }
51
+ ? {
52
+ tokenBudget: integer('tokenBudget', limits.tokenBudget, 1, Number.MAX_SAFE_INTEGER),
53
+ }
51
54
  : {}),
52
55
  maxDurationMs: integer('maxDurationMs', limits.maxDurationMs ?? 6 * 3_600_000, 1, 2_147_483_647),
53
56
  cpuSliceMs: integer('cpuSliceMs', limits.cpuSliceMs ?? 2_000, 1, 2_147_483_647),
@@ -176,6 +179,10 @@ export class WorkflowRuntime {
176
179
  const now = this.now;
177
180
  const runId = options.runId ?? `w${[...randomBytes(8)].map((b) => (b % 36).toString(36)).join('')}`;
178
181
  const startedAt = now();
182
+ const pass = options.resume?.pass ?? 1;
183
+ const resume = options.resume;
184
+ let latch = false;
185
+ const seenContent = new Map();
179
186
  const phases = new PhaseRegistry();
180
187
  const agents = new Map();
181
188
  const inFlight = new Set();
@@ -232,7 +239,13 @@ export class WorkflowRuntime {
232
239
  declared,
233
240
  at,
234
241
  });
235
- journal({ type: 'phase', at, index: phase.index, title: phase.title, declared });
242
+ journal({
243
+ type: 'phase',
244
+ at,
245
+ index: phase.index,
246
+ title: phase.title,
247
+ declared,
248
+ });
236
249
  }
237
250
  return phase;
238
251
  };
@@ -258,6 +271,7 @@ export class WorkflowRuntime {
258
271
  ...(agent.resultPreview !== undefined ? { resultPreview: agent.resultPreview } : {}),
259
272
  ...(agent.error !== undefined ? { error: agent.error } : {}),
260
273
  ...(agent.skipped ? { skipped: true } : {}),
274
+ ...(agent.cached !== undefined ? { cached: true } : {}),
261
275
  });
262
276
  // FIFO admission: an agent starts when fewer than maxConcurrency run.
263
277
  const acquire = (agent) => {
@@ -333,11 +347,26 @@ export class WorkflowRuntime {
333
347
  value = null;
334
348
  }
335
349
  emit(agentEvent(agent));
350
+ const finalState = agent.state === 'error' ? 'error' : agent.skipped ? 'skipped' : 'done';
351
+ let saved;
352
+ if (finalState === 'done' && value !== null && agent.cached === undefined) {
353
+ try {
354
+ saved = { result: JSON.parse(JSON.stringify(value)) };
355
+ }
356
+ catch {
357
+ saved = { unsaved: true };
358
+ journal({
359
+ type: 'log',
360
+ at,
361
+ message: "An agent's result could not be saved, so a resume will run it again.",
362
+ });
363
+ }
364
+ }
336
365
  journal({
337
366
  type: 'agent_outcome',
338
367
  at,
339
368
  index: agent.index,
340
- state: agent.state === 'error' ? 'error' : agent.skipped ? 'skipped' : 'done',
369
+ state: finalState,
341
370
  tokens: agent.tokens,
342
371
  toolCalls: agent.toolCalls,
343
372
  queuedAt: agent.queuedAt,
@@ -345,12 +374,30 @@ export class WorkflowRuntime {
345
374
  finishedAt: at,
346
375
  ...(agent.resultPreview !== undefined ? { resultPreview: agent.resultPreview } : {}),
347
376
  ...(agent.error !== undefined ? { error: agent.error } : {}),
377
+ ...(agent.key !== undefined ? { key: agent.key } : {}),
378
+ pass,
379
+ ...(agent.cached !== undefined
380
+ ? {
381
+ cached: true,
382
+ resultFrom: {
383
+ pass: agent.cached.pass,
384
+ index: agent.cached.index,
385
+ },
386
+ }
387
+ : {}),
388
+ ...(saved ?? {}),
389
+ ...(outcome.interrupted === true && finalState === 'error'
390
+ ? { interrupted: true }
391
+ : {}),
392
+ ...(agent.childSessionId !== undefined ? { childSessionId: agent.childSessionId } : {}),
348
393
  });
349
394
  return { ok: true, value: { v: value, t: totalTokens() } };
350
395
  };
351
396
  const interpret = (agent, call, outcome) => {
352
397
  agent.tokens = count(outcome.tokens);
353
398
  agent.toolCalls = count(outcome.toolCalls);
399
+ if (outcome.sessionId !== undefined)
400
+ agent.childSessionId = outcome.sessionId;
354
401
  if (agent.userSkipped || outcome.state === 'skipped') {
355
402
  return settle(agent, { state: 'done', skipped: true });
356
403
  }
@@ -358,10 +405,14 @@ export class WorkflowRuntime {
358
405
  return settle(agent, {
359
406
  state: 'error',
360
407
  error: 'The workflow was stopped before this agent finished.',
408
+ interrupted: true,
361
409
  });
362
410
  }
363
411
  if (outcome.state === 'error') {
364
- return settle(agent, { state: 'error', error: outcome.error ?? 'The agent failed.' });
412
+ return settle(agent, {
413
+ state: 'error',
414
+ error: outcome.error ?? 'The agent failed.',
415
+ });
365
416
  }
366
417
  if (call.schema === undefined) {
367
418
  return settle(agent, { state: 'done', value: outcome.text ?? '' });
@@ -396,6 +447,16 @@ export class WorkflowRuntime {
396
447
  return { ok: false, error: reason };
397
448
  }
398
449
  const index = ++requested;
450
+ const contentId = workflowCallContent(call.prompt, call);
451
+ const ordinal = seenContent.get(contentId) ?? 0;
452
+ seenContent.set(contentId, ordinal + 1);
453
+ const key = workflowCallKey(call.prompt, call, ordinal);
454
+ const hit = latch ? undefined : resume?.results.get(key);
455
+ if (resume !== undefined &&
456
+ hit === undefined &&
457
+ !(resume.seen.has(key) && !resume.failed.has(key) && !resume.demoted.has(key))) {
458
+ latch = true;
459
+ }
399
460
  const phaseTitle = call.phase ?? call.cursor;
400
461
  const phase = phaseTitle?.trim() ? announce(phaseTitle, false) : undefined;
401
462
  const queuedAt = now();
@@ -414,13 +475,27 @@ export class WorkflowRuntime {
414
475
  toolCalls: 0,
415
476
  skipped: false,
416
477
  userSkipped: false,
478
+ key,
417
479
  };
418
480
  agents.set(index, agent);
481
+ if (hit !== undefined) {
482
+ agent.cached = {
483
+ pass: hit.pass,
484
+ index: hit.index,
485
+ ...(hit.childSessionId !== undefined ? { childSessionId: hit.childSessionId } : {}),
486
+ };
487
+ if (hit.childSessionId !== undefined)
488
+ agent.childSessionId = hit.childSessionId;
489
+ agent.startedAt = queuedAt;
490
+ }
419
491
  emit(agentEvent(agent));
420
492
  journal({
421
493
  type: 'agent_request',
422
494
  at: queuedAt,
423
495
  index,
496
+ key,
497
+ pass,
498
+ ...(hit !== undefined ? { cached: true } : {}),
424
499
  label: agent.label,
425
500
  ...(phase ? { phaseIndex: phase.index, phaseTitle: phase.title } : {}),
426
501
  promptPreview: preview(call.prompt),
@@ -432,6 +507,12 @@ export class WorkflowRuntime {
432
507
  schema: call.schema !== undefined,
433
508
  },
434
509
  });
510
+ if (hit !== undefined) {
511
+ return settle(agent, {
512
+ state: 'done',
513
+ value: structuredClone(hit.value),
514
+ });
515
+ }
435
516
  const admitted = await acquire(agent);
436
517
  if (!admitted) {
437
518
  return agent.userSkipped
@@ -439,6 +520,7 @@ export class WorkflowRuntime {
439
520
  : settle(agent, {
440
521
  state: 'error',
441
522
  error: 'The workflow was stopped before this agent started.',
523
+ interrupted: true,
442
524
  });
443
525
  }
444
526
  try {
@@ -446,6 +528,7 @@ export class WorkflowRuntime {
446
528
  return settle(agent, {
447
529
  state: 'error',
448
530
  error: `Not started: the token budget for this workflow (${limits.tokenBudget} tokens) is spent.`,
531
+ interrupted: true,
449
532
  });
450
533
  }
451
534
  if (controller.signal.aborted || agent.userSkipped) {
@@ -454,6 +537,7 @@ export class WorkflowRuntime {
454
537
  : settle(agent, {
455
538
  state: 'error',
456
539
  error: 'The workflow was stopped before this agent started.',
540
+ interrupted: true,
457
541
  });
458
542
  }
459
543
  agent.startedAt = now();
@@ -526,7 +610,7 @@ export class WorkflowRuntime {
526
610
  // --- start ---
527
611
  if (options.journal?.writeScript) {
528
612
  try {
529
- await options.journal.writeScript(options.script, sha256(options.script));
613
+ await options.journal.writeScript(options.script, sha256(options.script), pass);
530
614
  }
531
615
  catch (error) {
532
616
  journalError = error instanceof Error ? error.message : String(error);
@@ -541,6 +625,16 @@ export class WorkflowRuntime {
541
625
  meta,
542
626
  args,
543
627
  limits,
628
+ ...(resume !== undefined
629
+ ? {
630
+ resume: {
631
+ pass,
632
+ previousFinish: resume.previousFinish,
633
+ scriptSha256Changed: resume.scriptSha256 !== undefined && resume.scriptSha256 !== sha256(options.script),
634
+ },
635
+ }
636
+ : {}),
637
+ ...(options.scriptPath !== undefined ? { scriptPath: options.scriptPath } : {}),
544
638
  });
545
639
  emit({
546
640
  type: 'workflow_started',
@@ -560,7 +654,11 @@ export class WorkflowRuntime {
560
654
  maxHostCalls: limits.maxAgents + 100,
561
655
  maxPendingHostCalls: limits.maxAgents + 100,
562
656
  });
563
- const preludeConfig = { args, tokenLimit: limits.tokenBudget ?? null };
657
+ const preludeConfig = {
658
+ args,
659
+ tokenLimit: limits.tokenBudget ?? null,
660
+ deterministic: options.deterministic === true,
661
+ };
564
662
  const runResult = await codeRuntime.run({
565
663
  source: `${buildPrelude(preludeConfig)}\n${body}`,
566
664
  allowedCalls: [WORKFLOW_HOST_CALL],
@@ -571,7 +669,10 @@ export class WorkflowRuntime {
571
669
  onOutput,
572
670
  onHostCall: (request) => {
573
671
  if (request.name !== WORKFLOW_HOST_CALL) {
574
- return Promise.resolve({ ok: false, error: `Unknown host call "${request.name}".` });
672
+ return Promise.resolve({
673
+ ok: false,
674
+ error: `Unknown host call "${request.name}".`,
675
+ });
575
676
  }
576
677
  let call;
577
678
  try {
@@ -634,7 +735,7 @@ export class WorkflowRuntime {
634
735
  const failedAgents = begun.filter((a) => a.state === 'error').length;
635
736
  const progress = begun.length === 0
636
737
  ? 'No agent had started.'
637
- : `Before it failed: ${begun.length} agent${begun.length === 1 ? '' : 's'} started, ${finished} finished, ${failedAgents} failed. A rerun starts the whole script again, so finished agents run again.`;
738
+ : `Before it failed: ${begun.length} agent${begun.length === 1 ? '' : 's'} started, ${finished} finished, ${failedAgents} failed. ${options.canResume === true ? 'Resume the run to continue: agents whose prompt and options are unchanged return their saved results.' : 'A rerun starts the whole script again, so finished agents run again.'}`;
638
739
  error = `${describeScriptFailure(outcome, body, preludeLineCount(preludeConfig))}\n${progress}${scriptPhase !== undefined ? ` Last phase: "${scriptPhase}".` : ''}`;
639
740
  }
640
741
  const all = [...agents.values()];
@@ -646,6 +747,9 @@ export class WorkflowRuntime {
646
747
  tokens: all.reduce((sum, a) => sum + a.tokens, 0),
647
748
  toolCalls: all.reduce((sum, a) => sum + a.toolCalls, 0),
648
749
  elapsedMs: Math.max(0, now() - startedAt),
750
+ ...(all.some((a) => a.cached !== undefined)
751
+ ? { cachedAgents: all.filter((a) => a.cached !== undefined).length }
752
+ : {}),
649
753
  };
650
754
  const at = now();
651
755
  emit({