@loopingai/core 0.5.0 → 0.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 (304) hide show
  1. package/README.md +6 -4
  2. package/dist/a2a/agent-stub.d.ts +0 -1
  3. package/dist/a2a/agent-stub.js +0 -1
  4. package/dist/a2a/caller.d.ts +0 -1
  5. package/dist/a2a/caller.js +0 -1
  6. package/dist/a2a/card.d.ts +0 -1
  7. package/dist/a2a/card.js +0 -1
  8. package/dist/a2a/context.d.ts +0 -1
  9. package/dist/a2a/context.js +0 -1
  10. package/dist/a2a/executor.d.ts +0 -1
  11. package/dist/a2a/executor.js +0 -1
  12. package/dist/a2a/index.d.ts +0 -1
  13. package/dist/a2a/index.js +0 -1
  14. package/dist/a2a/notify.d.ts +4 -4
  15. package/dist/a2a/notify.js +4 -4
  16. package/dist/a2a/parts.d.ts +0 -1
  17. package/dist/a2a/parts.js +0 -1
  18. package/dist/a2a/push.d.ts +0 -1
  19. package/dist/a2a/push.js +0 -1
  20. package/dist/a2a/task-store.d.ts +0 -1
  21. package/dist/a2a/task-store.js +0 -1
  22. package/dist/a2a/task.d.ts +0 -1
  23. package/dist/a2a/task.js +0 -1
  24. package/dist/a2a/verify.d.ts +0 -1
  25. package/dist/a2a/verify.js +0 -1
  26. package/dist/agent/anthropic/index.d.ts +15 -0
  27. package/dist/agent/anthropic/index.js +19 -0
  28. package/dist/agent/anthropic/language-model.d.ts +59 -0
  29. package/dist/agent/anthropic/language-model.js +442 -0
  30. package/dist/agent/anthropic/prompt.d.ts +84 -0
  31. package/dist/agent/anthropic/prompt.js +541 -0
  32. package/dist/agent/anthropic/runtime.d.ts +79 -0
  33. package/dist/agent/anthropic/runtime.js +130 -0
  34. package/dist/agent/budget.d.ts +0 -1
  35. package/dist/agent/budget.js +0 -1
  36. package/dist/agent/control.d.ts +0 -1
  37. package/dist/agent/control.js +10 -10
  38. package/dist/agent/errors.d.ts +85 -0
  39. package/dist/agent/errors.js +64 -0
  40. package/dist/agent/final-reply.d.ts +14 -14
  41. package/dist/agent/final-reply.js +28 -12
  42. package/dist/agent/history.d.ts +3 -4
  43. package/dist/agent/history.js +2 -3
  44. package/dist/agent/index.d.ts +4 -3
  45. package/dist/agent/index.js +4 -3
  46. package/dist/agent/inference.d.ts +58 -2
  47. package/dist/agent/inference.js +44 -1
  48. package/dist/agent/model.d.ts +42 -26
  49. package/dist/agent/model.js +1 -49
  50. package/dist/agent/session.d.ts +6 -8
  51. package/dist/agent/session.js +3 -4
  52. package/dist/agent/workers-ai/index.d.ts +23 -0
  53. package/dist/agent/workers-ai/index.js +23 -0
  54. package/dist/agent/workers-ai/runtime.d.ts +42 -0
  55. package/dist/agent/workers-ai/runtime.js +63 -0
  56. package/dist/config.d.ts +49 -16
  57. package/dist/config.js +30 -2
  58. package/dist/contract/index.d.ts +0 -1
  59. package/dist/contract/index.js +0 -1
  60. package/dist/contract/plugin.d.ts +63 -4
  61. package/dist/contract/plugin.js +76 -1
  62. package/dist/contract/recipe.d.ts +16 -18
  63. package/dist/contract/recipe.js +0 -1
  64. package/dist/contract/validation.d.ts +0 -1
  65. package/dist/contract/validation.js +0 -1
  66. package/dist/db/db.d.ts +0 -2
  67. package/dist/db/db.js +0 -1
  68. package/dist/db/index.d.ts +0 -1
  69. package/dist/db/index.js +0 -1
  70. package/dist/db/migrations/index.d.ts +0 -1
  71. package/dist/db/migrations/index.js +8 -2
  72. package/dist/db/models/subtasks.d.ts +24 -26
  73. package/dist/db/models/subtasks.js +33 -77
  74. package/dist/db/models/tasks.d.ts +0 -1
  75. package/dist/db/models/tasks.js +0 -1
  76. package/dist/db/schema.d.ts +2 -22
  77. package/dist/db/schema.js +2 -5
  78. package/dist/env.d.ts +0 -1
  79. package/dist/env.js +0 -1
  80. package/dist/host/agent.d.ts +58 -5
  81. package/dist/host/agent.js +63 -10
  82. package/dist/host/index.d.ts +0 -1
  83. package/dist/host/index.js +0 -1
  84. package/dist/host/plugin-host.d.ts +0 -1
  85. package/dist/host/plugin-host.js +0 -1
  86. package/dist/index.d.ts +2 -3
  87. package/dist/index.js +2 -3
  88. package/dist/platform.d.ts +74 -12
  89. package/dist/platform.js +76 -14
  90. package/dist/round/agent.d.ts +36 -32
  91. package/dist/round/agent.js +61 -90
  92. package/dist/round/index.d.ts +3 -3
  93. package/dist/round/index.js +2 -3
  94. package/dist/round/policy.d.ts +2 -3
  95. package/dist/round/policy.js +0 -1
  96. package/dist/round/subagent.d.ts +19 -2
  97. package/dist/round/subagent.js +22 -6
  98. package/dist/round/turn.d.ts +32 -14
  99. package/dist/round/turn.js +83 -17
  100. package/dist/round/workflow.d.ts +30 -9
  101. package/dist/round/workflow.js +146 -78
  102. package/dist/runtime/index.d.ts +4 -3
  103. package/dist/runtime/index.js +6 -1
  104. package/dist/runtime/tool-families.d.ts +0 -1
  105. package/dist/runtime/tool-families.js +0 -1
  106. package/dist/subagent/fingerprint.d.ts +2 -3
  107. package/dist/subagent/fingerprint.js +8 -18
  108. package/dist/subagent/index.d.ts +6 -5
  109. package/dist/subagent/index.js +8 -7
  110. package/dist/subagent/prompt.d.ts +4 -6
  111. package/dist/subagent/prompt.js +0 -9
  112. package/dist/subagent/run.d.ts +8 -2
  113. package/dist/subagent/run.js +59 -10
  114. package/dist/subagent/workspace.d.ts +0 -1
  115. package/dist/subagent/workspace.js +0 -1
  116. package/dist/subtasks/catalog.d.ts +1 -2
  117. package/dist/subtasks/catalog.js +1 -2
  118. package/dist/subtasks/decomposition.d.ts +16 -21
  119. package/dist/subtasks/decomposition.js +27 -76
  120. package/dist/subtasks/delegate.d.ts +20 -2
  121. package/dist/subtasks/delegate.js +21 -17
  122. package/dist/subtasks/index.d.ts +1 -3
  123. package/dist/subtasks/index.js +1 -3
  124. package/dist/subtasks/subtask-types.d.ts +0 -9
  125. package/dist/subtasks/subtask-types.js +0 -8
  126. package/dist/subtasks/types.d.ts +45 -71
  127. package/dist/subtasks/types.js +0 -1
  128. package/dist/testing/auth.d.ts +0 -1
  129. package/dist/testing/auth.js +0 -1
  130. package/dist/testing/do.d.ts +0 -1
  131. package/dist/testing/do.js +0 -1
  132. package/dist/testing/fake-session.d.ts +0 -1
  133. package/dist/testing/fake-session.js +0 -1
  134. package/dist/testing/fixtures.d.ts +0 -1
  135. package/dist/testing/fixtures.js +0 -1
  136. package/dist/testing/harness.d.ts +0 -1
  137. package/dist/testing/harness.js +0 -1
  138. package/dist/testing/index.d.ts +0 -1
  139. package/dist/testing/index.js +0 -1
  140. package/dist/testing/mock-model.d.ts +35 -1
  141. package/dist/testing/mock-model.js +75 -1
  142. package/dist/testing/node.d.ts +0 -1
  143. package/dist/testing/node.js +0 -1
  144. package/dist/testing/vcr-global-setup.d.ts +1 -4
  145. package/dist/testing/vcr-global-setup.js +1 -4
  146. package/dist/testing/vcr-shared.d.ts +0 -1
  147. package/dist/testing/vcr-shared.js +0 -1
  148. package/dist/testing/vcr-spec.d.ts +0 -1
  149. package/dist/testing/vcr-spec.js +0 -1
  150. package/dist/testing/vcr-store.d.ts +0 -1
  151. package/dist/testing/vcr-store.js +0 -1
  152. package/dist/testing/vcr.d.ts +0 -1
  153. package/dist/testing/vcr.js +0 -1
  154. package/dist/worker/define-agent.d.ts +0 -1
  155. package/dist/worker/define-agent.js +0 -1
  156. package/dist/worker/index.d.ts +5 -13
  157. package/dist/worker/index.js +5 -13
  158. package/package.json +20 -2
  159. package/dist/a2a/agent-stub.d.ts.map +0 -1
  160. package/dist/a2a/agent-stub.js.map +0 -1
  161. package/dist/a2a/caller.d.ts.map +0 -1
  162. package/dist/a2a/caller.js.map +0 -1
  163. package/dist/a2a/card.d.ts.map +0 -1
  164. package/dist/a2a/card.js.map +0 -1
  165. package/dist/a2a/context.d.ts.map +0 -1
  166. package/dist/a2a/context.js.map +0 -1
  167. package/dist/a2a/executor.d.ts.map +0 -1
  168. package/dist/a2a/executor.js.map +0 -1
  169. package/dist/a2a/index.d.ts.map +0 -1
  170. package/dist/a2a/index.js.map +0 -1
  171. package/dist/a2a/notify.d.ts.map +0 -1
  172. package/dist/a2a/notify.js.map +0 -1
  173. package/dist/a2a/parts.d.ts.map +0 -1
  174. package/dist/a2a/parts.js.map +0 -1
  175. package/dist/a2a/push.d.ts.map +0 -1
  176. package/dist/a2a/push.js.map +0 -1
  177. package/dist/a2a/task-store.d.ts.map +0 -1
  178. package/dist/a2a/task-store.js.map +0 -1
  179. package/dist/a2a/task.d.ts.map +0 -1
  180. package/dist/a2a/task.js.map +0 -1
  181. package/dist/a2a/verify.d.ts.map +0 -1
  182. package/dist/a2a/verify.js.map +0 -1
  183. package/dist/agent/budget.d.ts.map +0 -1
  184. package/dist/agent/budget.js.map +0 -1
  185. package/dist/agent/control.d.ts.map +0 -1
  186. package/dist/agent/control.js.map +0 -1
  187. package/dist/agent/final-reply.d.ts.map +0 -1
  188. package/dist/agent/final-reply.js.map +0 -1
  189. package/dist/agent/history.d.ts.map +0 -1
  190. package/dist/agent/history.js.map +0 -1
  191. package/dist/agent/index.d.ts.map +0 -1
  192. package/dist/agent/index.js.map +0 -1
  193. package/dist/agent/inference.d.ts.map +0 -1
  194. package/dist/agent/inference.js.map +0 -1
  195. package/dist/agent/model.d.ts.map +0 -1
  196. package/dist/agent/model.js.map +0 -1
  197. package/dist/agent/session.d.ts.map +0 -1
  198. package/dist/agent/session.js.map +0 -1
  199. package/dist/config.d.ts.map +0 -1
  200. package/dist/config.js.map +0 -1
  201. package/dist/contract/index.d.ts.map +0 -1
  202. package/dist/contract/index.js.map +0 -1
  203. package/dist/contract/plugin.d.ts.map +0 -1
  204. package/dist/contract/plugin.js.map +0 -1
  205. package/dist/contract/recipe.d.ts.map +0 -1
  206. package/dist/contract/recipe.js.map +0 -1
  207. package/dist/contract/validation.d.ts.map +0 -1
  208. package/dist/contract/validation.js.map +0 -1
  209. package/dist/db/db.d.ts.map +0 -1
  210. package/dist/db/db.js.map +0 -1
  211. package/dist/db/index.d.ts.map +0 -1
  212. package/dist/db/index.js.map +0 -1
  213. package/dist/db/migrations/index.d.ts.map +0 -1
  214. package/dist/db/migrations/index.js.map +0 -1
  215. package/dist/db/models/subtasks.d.ts.map +0 -1
  216. package/dist/db/models/subtasks.js.map +0 -1
  217. package/dist/db/models/tasks.d.ts.map +0 -1
  218. package/dist/db/models/tasks.js.map +0 -1
  219. package/dist/db/schema.d.ts.map +0 -1
  220. package/dist/db/schema.js.map +0 -1
  221. package/dist/env.d.ts.map +0 -1
  222. package/dist/env.js.map +0 -1
  223. package/dist/host/agent.d.ts.map +0 -1
  224. package/dist/host/agent.js.map +0 -1
  225. package/dist/host/index.d.ts.map +0 -1
  226. package/dist/host/index.js.map +0 -1
  227. package/dist/host/plugin-host.d.ts.map +0 -1
  228. package/dist/host/plugin-host.js.map +0 -1
  229. package/dist/index.d.ts.map +0 -1
  230. package/dist/index.js.map +0 -1
  231. package/dist/platform.d.ts.map +0 -1
  232. package/dist/platform.js.map +0 -1
  233. package/dist/round/agent.d.ts.map +0 -1
  234. package/dist/round/agent.js.map +0 -1
  235. package/dist/round/index.d.ts.map +0 -1
  236. package/dist/round/index.js.map +0 -1
  237. package/dist/round/policy.d.ts.map +0 -1
  238. package/dist/round/policy.js.map +0 -1
  239. package/dist/round/subagent.d.ts.map +0 -1
  240. package/dist/round/subagent.js.map +0 -1
  241. package/dist/round/turn.d.ts.map +0 -1
  242. package/dist/round/turn.js.map +0 -1
  243. package/dist/round/workflow.d.ts.map +0 -1
  244. package/dist/round/workflow.js.map +0 -1
  245. package/dist/runtime/index.d.ts.map +0 -1
  246. package/dist/runtime/index.js.map +0 -1
  247. package/dist/runtime/tool-families.d.ts.map +0 -1
  248. package/dist/runtime/tool-families.js.map +0 -1
  249. package/dist/subagent/fingerprint.d.ts.map +0 -1
  250. package/dist/subagent/fingerprint.js.map +0 -1
  251. package/dist/subagent/index.d.ts.map +0 -1
  252. package/dist/subagent/index.js.map +0 -1
  253. package/dist/subagent/prompt.d.ts.map +0 -1
  254. package/dist/subagent/prompt.js.map +0 -1
  255. package/dist/subagent/run.d.ts.map +0 -1
  256. package/dist/subagent/run.js.map +0 -1
  257. package/dist/subagent/workspace.d.ts.map +0 -1
  258. package/dist/subagent/workspace.js.map +0 -1
  259. package/dist/subtasks/catalog.d.ts.map +0 -1
  260. package/dist/subtasks/catalog.js.map +0 -1
  261. package/dist/subtasks/decomposition.d.ts.map +0 -1
  262. package/dist/subtasks/decomposition.js.map +0 -1
  263. package/dist/subtasks/delegate.d.ts.map +0 -1
  264. package/dist/subtasks/delegate.js.map +0 -1
  265. package/dist/subtasks/index.d.ts.map +0 -1
  266. package/dist/subtasks/index.js.map +0 -1
  267. package/dist/subtasks/scheduler.d.ts +0 -49
  268. package/dist/subtasks/scheduler.d.ts.map +0 -1
  269. package/dist/subtasks/scheduler.js +0 -48
  270. package/dist/subtasks/scheduler.js.map +0 -1
  271. package/dist/subtasks/subtask-types.d.ts.map +0 -1
  272. package/dist/subtasks/subtask-types.js.map +0 -1
  273. package/dist/subtasks/types.d.ts.map +0 -1
  274. package/dist/subtasks/types.js.map +0 -1
  275. package/dist/testing/auth.d.ts.map +0 -1
  276. package/dist/testing/auth.js.map +0 -1
  277. package/dist/testing/do.d.ts.map +0 -1
  278. package/dist/testing/do.js.map +0 -1
  279. package/dist/testing/fake-session.d.ts.map +0 -1
  280. package/dist/testing/fake-session.js.map +0 -1
  281. package/dist/testing/fixtures.d.ts.map +0 -1
  282. package/dist/testing/fixtures.js.map +0 -1
  283. package/dist/testing/harness.d.ts.map +0 -1
  284. package/dist/testing/harness.js.map +0 -1
  285. package/dist/testing/index.d.ts.map +0 -1
  286. package/dist/testing/index.js.map +0 -1
  287. package/dist/testing/mock-model.d.ts.map +0 -1
  288. package/dist/testing/mock-model.js.map +0 -1
  289. package/dist/testing/node.d.ts.map +0 -1
  290. package/dist/testing/node.js.map +0 -1
  291. package/dist/testing/vcr-global-setup.d.ts.map +0 -1
  292. package/dist/testing/vcr-global-setup.js.map +0 -1
  293. package/dist/testing/vcr-shared.d.ts.map +0 -1
  294. package/dist/testing/vcr-shared.js.map +0 -1
  295. package/dist/testing/vcr-spec.d.ts.map +0 -1
  296. package/dist/testing/vcr-spec.js.map +0 -1
  297. package/dist/testing/vcr-store.d.ts.map +0 -1
  298. package/dist/testing/vcr-store.js.map +0 -1
  299. package/dist/testing/vcr.d.ts.map +0 -1
  300. package/dist/testing/vcr.js.map +0 -1
  301. package/dist/worker/define-agent.d.ts.map +0 -1
  302. package/dist/worker/define-agent.js.map +0 -1
  303. package/dist/worker/index.d.ts.map +0 -1
  304. package/dist/worker/index.js.map +0 -1
@@ -7,14 +7,13 @@ import type { SubagentClass } from "./subagent.js";
7
7
  import type { RoundPolicy } from "./policy.js";
8
8
  import { type RoundMode } from "./turn.js";
9
9
  /**
10
- * A **delegating** agent: the round loop, the durable Subtask DAG, and the
11
- * isolated subagent execution beneath it.
10
+ * A **delegating** agent: the round loop, the durable Subtasks it hands out, and
11
+ * the isolated subagent execution beneath them.
12
12
  *
13
13
  * Everything {@link LoopingAgent} gives every agent, plus the half that only a
14
14
  * delegating one needs — and it is all mechanism. A Workflow drives it through
15
- * native Cloudflare RPC (`runTaskTurn`, `skipBlockedSubtasks`,
16
- * `executeSubtaskChunk`, …), never HTTP: the DO is a private implementation
17
- * detail of the Worker.
15
+ * native Cloudflare RPC (`runTaskTurn`, `scanSubtasks`, `executeSubtaskChunk`,
16
+ * …), never HTTP: the DO is a private implementation detail of the Worker.
18
17
  *
19
18
  * ## Why core owns this
20
19
  *
@@ -73,8 +72,8 @@ export declare abstract class RoundAgentBase<TEnv extends Cloudflare.Env & AiEnv
73
72
  /** Age out this agent's subtask rows alongside the task rows. */
74
73
  protected cleanupAgentState(): void;
75
74
  /**
76
- * One main-agent round: answer the user, or delegate a durable Subtask DAG and
77
- * return the acknowledgment the user sees while it runs.
75
+ * One main-agent round: answer the user, or delegate a durable set of Subtasks
76
+ * and return the acknowledgment the user sees while it runs.
78
77
  *
79
78
  * This is the RPC boundary, so it is where the round's cost becomes a field. The
80
79
  * budget is created here, handed to {@link decideRound} to be spent, and read
@@ -120,29 +119,33 @@ export declare abstract class RoundAgentBase<TEnv extends Cloudflare.Env & AiEnv
120
119
  * Every round's branches for a Task, in stable ordinal order — what a round
121
120
  * needs to reunite each earlier `delegate` call with its result. Built inside
122
121
  * the DO and consumed here, so the 1 MiB Workflow-step cap that keeps
123
- * {@link SubtaskNode} narrow does not apply.
122
+ * {@link SubtaskScan} down to ids does not apply.
124
123
  */
125
124
  private compositionBranches;
126
125
  /** A Task's Subtasks, every round, in stable ordinal order. */
127
126
  listSubtasks(taskId: string): Promise<Subtask[]>;
128
127
  /**
129
- * The Workflow's per-wave scan for **one round's** DAG: report a cancellation,
130
- * or skip every pending Subtask blocked by a dependency that did not succeed and
131
- * return the refreshed DAG as scheduler {@link SubtaskNode}s.
128
+ * The Workflow's scan for **one round's** Subtasks: report a cancellation, or
129
+ * return the ids that still owe an outcome, in ordinal order.
132
130
  *
133
- * Scoped to the round because dependency edges never cross one: an earlier
134
- * round's rows are already terminal and irrelevant to this wave, and including
135
- * them would only widen a projection that has a size cap.
131
+ * Scoped to the round because the Workflow drives one round at a time: an
132
+ * earlier round's rows are already terminal and would only widen a projection
133
+ * that has a size cap.
136
134
  *
137
- * Skipping runs to a fixpoint because it propagates: a node skipped for a
138
- * failed prerequisite blocks *its* dependents in turn. Bounded by the
139
- * per-round maximum. Independent branches are untouched one branch's failure
140
- * never stops work that does not depend on it.
135
+ * `running` counts alongside `pending` on purpose. `executeSubtaskChunk`
136
+ * accepts a row that is either: the latter is its ambiguous-retry path, where a
137
+ * previous attempt crashed mid-execution and the managed child's fingerprint
138
+ * cache may still hold the terminal result that makes the retry free. So a row
139
+ * stranded `running` is re-runnable, and omitting it here would abandon it.
140
+ *
141
+ * Ordinal order comes from {@link listRound} and is not incidental: these ids
142
+ * become durable Workflow step names, so the traversal that produces them has
143
+ * to be deterministic.
141
144
  *
142
145
  * The cancellation verdict rides along rather than being probed separately, so
143
- * a wave costs one round trip and cannot act on a stale answer.
146
+ * the scan costs one round trip and cannot act on a stale answer.
144
147
  */
145
- skipBlockedSubtasks(taskId: string, round: number): Promise<SubtaskScan>;
148
+ scanSubtasks(taskId: string, round: number): Promise<SubtaskScan>;
146
149
  /** Parent cancellation: cancel every still-pending Subtask. Returns the count. */
147
150
  cancelPendingSubtasks(taskId: string): Promise<number>;
148
151
  /**
@@ -182,8 +185,9 @@ export declare abstract class RoundAgentBase<TEnv extends Cloudflare.Env & AiEnv
182
185
  * delete; that now happens strictly later.
183
186
  *
184
187
  * Throws on a transient fault (the step retries and the child resumes from its
185
- * checkpoint) and on scheduler-invariant violations both are bugs, not
186
- * outcomes.
188
+ * checkpoint) and when the row is in a status this cannot accept — a subtask
189
+ * that is neither `pending` nor `running` nor already terminal. Both are bugs,
190
+ * not outcomes.
187
191
  */
188
192
  executeSubtaskChunk(id: SubtaskId, chunk: number, push?: TurnPushContext): Promise<SubtaskChunkOutcome>;
189
193
  /**
@@ -230,14 +234,6 @@ export declare abstract class RoundAgentBase<TEnv extends Cloudflare.Env & AiEnv
230
234
  private abortChildQuietly;
231
235
  /** Persist a child's terminal outcome. Returns whether the guarded write applied. */
232
236
  private persistResult;
233
- /**
234
- * Load a Subtask's dependency results, in ordinal order.
235
- *
236
- * Order is semantic: it feeds the child's request fingerprint, so a retry must
237
- * build the identical array or the cache misses. A dependency that has not
238
- * completed means the scheduler ran this node too early.
239
- */
240
- private loadDependencyResults;
241
237
  /** Re-read a Subtask that must exist (it was just written). */
242
238
  private requireSubtask;
243
239
  /** Delete a managed child, swallowing failures (used on best-effort sweeps). */
@@ -248,7 +244,7 @@ export declare abstract class RoundAgentBase<TEnv extends Cloudflare.Env & AiEnv
248
244
  * Interrupt a canceled Task's live children: each `running` Subtask's managed
249
245
  * child gets `abortRun`, so a long recipe stops at its current model call
250
246
  * instead of at the next chunk boundary (up to `chunkSoftMs` later). A subtask
251
- * that already finished (e.g. one branch of a wave completed while another was
247
+ * that already finished (e.g. one branch completed while another was
252
248
  * still running) is deliberately retained until the terminal-delivery sweep —
253
249
  * but a canceled Task never reaches delivery, so its idle child is deleted
254
250
  * here instead, or it would leak until the 30-day row cleanup regardless of
@@ -261,7 +257,15 @@ export declare abstract class RoundAgentBase<TEnv extends Cloudflare.Env & AiEnv
261
257
  * status. Bounded by `maxSubtasks`. Best-effort throughout: a child that
262
258
  * cannot be reached is logged, never fatal — cancellation must not fail
263
259
  * because cleanup did.
260
+ *
261
+ * The `pending` rows are transitioned here too, and that is not bookkeeping:
262
+ * it is the only thing that resolves them. Nothing else is coming back to a
263
+ * pending row once a Task is canceled — the Workflow's scheduler runs a
264
+ * single pass and does not re-scan, and `prepareChunk` reports a canceled
265
+ * Task's pending row as terminal *without* claiming it, so a branch whose RPC
266
+ * had not yet reached the claim when the cancellation landed simply returns.
267
+ * Left to the loop below, which only deletes the child, the row would sit
268
+ * non-terminal until the 30-day cleanup.
264
269
  */
265
270
  protected onTaskCanceled(taskId: string): Promise<void>;
266
271
  }
267
- //# sourceMappingURL=agent.d.ts.map
@@ -7,14 +7,13 @@ import { FINGERPRINT_MISMATCH, subagentName } from "../subagent/index.js";
7
7
  import { LoopingAgent } from "../host/agent.js";
8
8
  import { buildTurnInstructions, runTurn } from "./turn.js";
9
9
  /**
10
- * A **delegating** agent: the round loop, the durable Subtask DAG, and the
11
- * isolated subagent execution beneath it.
10
+ * A **delegating** agent: the round loop, the durable Subtasks it hands out, and
11
+ * the isolated subagent execution beneath them.
12
12
  *
13
13
  * Everything {@link LoopingAgent} gives every agent, plus the half that only a
14
14
  * delegating one needs — and it is all mechanism. A Workflow drives it through
15
- * native Cloudflare RPC (`runTaskTurn`, `skipBlockedSubtasks`,
16
- * `executeSubtaskChunk`, …), never HTTP: the DO is a private implementation
17
- * detail of the Worker.
15
+ * native Cloudflare RPC (`runTaskTurn`, `scanSubtasks`, `executeSubtaskChunk`,
16
+ * …), never HTTP: the DO is a private implementation detail of the Worker.
18
17
  *
19
18
  * ## Why core owns this
20
19
  *
@@ -75,8 +74,8 @@ export class RoundAgentBase extends LoopingAgent {
75
74
  // idempotent on its durable output, and execution recovers from either the
76
75
  // parent row or the child's cached result.
77
76
  /**
78
- * One main-agent round: answer the user, or delegate a durable Subtask DAG and
79
- * return the acknowledgment the user sees while it runs.
77
+ * One main-agent round: answer the user, or delegate a durable set of Subtasks
78
+ * and return the acknowledgment the user sees while it runs.
80
79
  *
81
80
  * This is the RPC boundary, so it is where the round's cost becomes a field. The
82
81
  * budget is created here, handed to {@link decideRound} to be spent, and read
@@ -154,12 +153,15 @@ export class RoundAgentBase extends LoopingAgent {
154
153
  types: this.runtime.types,
155
154
  maxSubtasks: this.config.maxSubtasks,
156
155
  maxOutputTokens: this.config.model.maxOutputTokens,
156
+ maxRetries: this.config.model.maxRetries,
157
157
  instructions: this.instructions,
158
158
  partialNote: policy.copy.partialNote,
159
159
  // The key carries the round so two rounds of one Task cannot collide on
160
160
  // the gateway, which a bare step index would.
161
161
  onContent: channel?.stream((step) => `r${round}:step:${step}`)
162
162
  });
163
+ // Terminal for this round with nothing to persist — the kind rides out with
164
+ // it, and the Workflow turns it into words.
163
165
  if (outcome.status === "failed")
164
166
  return outcome;
165
167
  // Cancelled while the model worked: persist nothing and publish nothing. The
@@ -182,7 +184,7 @@ export class RoundAgentBase extends LoopingAgent {
182
184
  * Every round's branches for a Task, in stable ordinal order — what a round
183
185
  * needs to reunite each earlier `delegate` call with its result. Built inside
184
186
  * the DO and consumed here, so the 1 MiB Workflow-step cap that keeps
185
- * {@link SubtaskNode} narrow does not apply.
187
+ * {@link SubtaskScan} down to ids does not apply.
186
188
  */
187
189
  compositionBranches(taskId) {
188
190
  return this.db.subtasks.list(taskId).map((s) => ({
@@ -191,7 +193,6 @@ export class RoundAgentBase extends LoopingAgent {
191
193
  ordinal: s.ordinal,
192
194
  type: s.type,
193
195
  prompt: s.prompt,
194
- dependsOn: s.dependsOn,
195
196
  params: s.params,
196
197
  status: s.status,
197
198
  resultParts: s.resultParts,
@@ -203,40 +204,34 @@ export class RoundAgentBase extends LoopingAgent {
203
204
  return this.db.subtasks.list(taskId);
204
205
  }
205
206
  /**
206
- * The Workflow's per-wave scan for **one round's** DAG: report a cancellation,
207
- * or skip every pending Subtask blocked by a dependency that did not succeed and
208
- * return the refreshed DAG as scheduler {@link SubtaskNode}s.
207
+ * The Workflow's scan for **one round's** Subtasks: report a cancellation, or
208
+ * return the ids that still owe an outcome, in ordinal order.
209
209
  *
210
- * Scoped to the round because dependency edges never cross one: an earlier
211
- * round's rows are already terminal and irrelevant to this wave, and including
212
- * them would only widen a projection that has a size cap.
210
+ * Scoped to the round because the Workflow drives one round at a time: an
211
+ * earlier round's rows are already terminal and would only widen a projection
212
+ * that has a size cap.
213
213
  *
214
- * Skipping runs to a fixpoint because it propagates: a node skipped for a
215
- * failed prerequisite blocks *its* dependents in turn. Bounded by the
216
- * per-round maximum. Independent branches are untouched one branch's failure
217
- * never stops work that does not depend on it.
214
+ * `running` counts alongside `pending` on purpose. `executeSubtaskChunk`
215
+ * accepts a row that is either: the latter is its ambiguous-retry path, where a
216
+ * previous attempt crashed mid-execution and the managed child's fingerprint
217
+ * cache may still hold the terminal result that makes the retry free. So a row
218
+ * stranded `running` is re-runnable, and omitting it here would abandon it.
219
+ *
220
+ * Ordinal order comes from {@link listRound} and is not incidental: these ids
221
+ * become durable Workflow step names, so the traversal that produces them has
222
+ * to be deterministic.
218
223
  *
219
224
  * The cancellation verdict rides along rather than being probed separately, so
220
- * a wave costs one round trip and cannot act on a stale answer.
225
+ * the scan costs one round trip and cannot act on a stale answer.
221
226
  */
222
- async skipBlockedSubtasks(taskId, round) {
227
+ async scanSubtasks(taskId, round) {
223
228
  if (await this.isTaskCanceled(taskId))
224
229
  return { canceled: true };
225
- const blocked = new Set(["failed", "skipped", "canceled"]);
226
- for (;;) {
227
- const current = this.db.subtasks.listRound(taskId, round);
228
- const byId = new Map(current.map((s) => [s.id, s]));
229
- const next = current.filter((s) => s.status === "pending" &&
230
- s.dependsOn.some((dep) => {
231
- const parent = byId.get(dep);
232
- return parent !== undefined && blocked.has(parent.status);
233
- }));
234
- if (next.length === 0) {
235
- return { canceled: false, nodes: current.map(toSubtaskNode) };
236
- }
237
- for (const s of next)
238
- this.db.subtasks.skip(s.id);
239
- }
230
+ const ids = this.db.subtasks
231
+ .listRound(taskId, round)
232
+ .filter((s) => s.status === "pending" || s.status === "running")
233
+ .map((s) => s.id);
234
+ return { canceled: false, ids };
240
235
  }
241
236
  /** Parent cancellation: cancel every still-pending Subtask. Returns the count. */
242
237
  async cancelPendingSubtasks(taskId) {
@@ -258,17 +253,17 @@ export class RoundAgentBase extends LoopingAgent {
258
253
  const subtask = this.db.subtasks.get(id);
259
254
  if (!subtask)
260
255
  return;
261
- // `fail` is a guarded `running|pending -> failed`, and its verdict is the
262
- // whole idempotency claim above. Dropping it made this method a no-op in
263
- // *name* only: a late workflow failure that lost the race to a real result
264
- // would still release the branch's runtime, abort its child and delete it
265
- // tearing down a branch that had already succeeded.
256
+ // `fail` is a guarded `running|pending -> failed`, and **its verdict is the
257
+ // whole idempotency claim above** read it before tearing anything down. A
258
+ // late workflow failure that lost the race to a real result would otherwise
259
+ // still release the branch's runtime, abort its child and delete it, tearing
260
+ // down a branch that had already succeeded.
266
261
  //
267
- // That is also precisely the teardown `executeSubtaskChunk` defers on the
268
- // success path, because aborting a facet in the same tick its RPC returned
269
- // makes telemetry record the success as a failure. Cleanup for an
270
- // already-terminal row belongs to `sweepTaskChildren`, which runs after
271
- // delivery and knows the whole task is done with.
262
+ // Cleanup for an already-terminal row belongs to `sweepTaskChildren`, which
263
+ // runs after delivery and knows the whole task is done with the same
264
+ // teardown `executeSubtaskChunk` defers on its success path, because
265
+ // aborting a facet in the same tick its RPC returned makes telemetry record
266
+ // the success as a failure.
272
267
  if (!this.db.subtasks.fail(id, error))
273
268
  return;
274
269
  const name = subagentName(subtask.taskId, id);
@@ -300,8 +295,9 @@ export class RoundAgentBase extends LoopingAgent {
300
295
  * delete; that now happens strictly later.
301
296
  *
302
297
  * Throws on a transient fault (the step retries and the child resumes from its
303
- * checkpoint) and on scheduler-invariant violations both are bugs, not
304
- * outcomes.
298
+ * checkpoint) and when the row is in a status this cannot accept — a subtask
299
+ * that is neither `pending` nor `running` nor already terminal. Both are bugs,
300
+ * not outcomes.
305
301
  */
306
302
  async executeSubtaskChunk(id, chunk, push) {
307
303
  const prepared = await this.prepareChunk(id);
@@ -412,7 +408,6 @@ export class RoundAgentBase extends LoopingAgent {
412
408
  }
413
409
  return { kind: "terminal", subtask };
414
410
  }
415
- const dependencyResults = this.loadDependencyResults(subtask);
416
411
  let recipe;
417
412
  let validated;
418
413
  try {
@@ -422,7 +417,8 @@ export class RoundAgentBase extends LoopingAgent {
422
417
  catch (err) {
423
418
  // An unknown/retired type or a disabled/soul-less Recipe is a
424
419
  // configuration bug, not a transient fault. Record it as a branch failure
425
- // so the DAG's skip semantics apply to its dependents.
420
+ // so a later round can disclose the gap, rather than as a throw that would
421
+ // be retried forever.
426
422
  const recipeId = recipe?.key ?? subtask.type;
427
423
  const recipeVersion = recipe?.version ?? 0;
428
424
  const message = recipe
@@ -456,7 +452,6 @@ export class RoundAgentBase extends LoopingAgent {
456
452
  recipe: validated,
457
453
  prompt: subtask.prompt,
458
454
  references: subtask.references,
459
- dependencyResults,
460
455
  params: subtask.params
461
456
  };
462
457
  return {
@@ -572,34 +567,6 @@ export class RoundAgentBase extends LoopingAgent {
572
567
  return this.db.subtasks.fail(id, `malformed result: ${String(err)}`);
573
568
  }
574
569
  }
575
- /**
576
- * Load a Subtask's dependency results, in ordinal order.
577
- *
578
- * Order is semantic: it feeds the child's request fingerprint, so a retry must
579
- * build the identical array or the cache misses. A dependency that has not
580
- * completed means the scheduler ran this node too early.
581
- */
582
- loadDependencyResults(subtask) {
583
- if (subtask.dependsOn.length === 0)
584
- return [];
585
- const deps = this.db.subtasks
586
- .list(subtask.taskId)
587
- .filter((s) => subtask.dependsOn.includes(s.id));
588
- if (deps.length !== subtask.dependsOn.length) {
589
- throw new Error(`subtask ${subtask.id} has unknown dependencies`);
590
- }
591
- return deps.map((dep) => {
592
- if (dep.status !== "completed" || !dep.resultParts) {
593
- throw new Error(`subtask ${subtask.id} ran before dependency ${dep.id} completed ` +
594
- `(status=${dep.status})`);
595
- }
596
- return {
597
- subtaskId: dep.id,
598
- type: dep.type,
599
- resultParts: dep.resultParts
600
- };
601
- });
602
- }
603
570
  /** Re-read a Subtask that must exist (it was just written). */
604
571
  requireSubtask(id) {
605
572
  const row = this.db.subtasks.get(id);
@@ -628,7 +595,7 @@ export class RoundAgentBase extends LoopingAgent {
628
595
  * Interrupt a canceled Task's live children: each `running` Subtask's managed
629
596
  * child gets `abortRun`, so a long recipe stops at its current model call
630
597
  * instead of at the next chunk boundary (up to `chunkSoftMs` later). A subtask
631
- * that already finished (e.g. one branch of a wave completed while another was
598
+ * that already finished (e.g. one branch completed while another was
632
599
  * still running) is deliberately retained until the terminal-delivery sweep —
633
600
  * but a canceled Task never reaches delivery, so its idle child is deleted
634
601
  * here instead, or it would leak until the 30-day row cleanup regardless of
@@ -641,8 +608,22 @@ export class RoundAgentBase extends LoopingAgent {
641
608
  * status. Bounded by `maxSubtasks`. Best-effort throughout: a child that
642
609
  * cannot be reached is logged, never fatal — cancellation must not fail
643
610
  * because cleanup did.
611
+ *
612
+ * The `pending` rows are transitioned here too, and that is not bookkeeping:
613
+ * it is the only thing that resolves them. Nothing else is coming back to a
614
+ * pending row once a Task is canceled — the Workflow's scheduler runs a
615
+ * single pass and does not re-scan, and `prepareChunk` reports a canceled
616
+ * Task's pending row as terminal *without* claiming it, so a branch whose RPC
617
+ * had not yet reached the claim when the cancellation landed simply returns.
618
+ * Left to the loop below, which only deletes the child, the row would sit
619
+ * non-terminal until the 30-day cleanup.
644
620
  */
645
621
  async onTaskCanceled(taskId) {
622
+ // First, and outside the loop: `cancelPending` is one guarded bulk
623
+ // `pending -> canceled`, so it cannot be skipped by a best-effort teardown
624
+ // below throwing partway through, and a branch that won the claim a moment
625
+ // ago is left alone to resolve through `cancelRunning` on its own path.
626
+ this.db.subtasks.cancelPending(taskId);
646
627
  for (const subtask of this.db.subtasks.list(taskId)) {
647
628
  const name = subagentName(taskId, subtask.id);
648
629
  if (subtask.status !== "running") {
@@ -679,13 +660,3 @@ export class RoundAgentBase extends LoopingAgent {
679
660
  }
680
661
  }
681
662
  }
682
- /** Project a durable row to the scheduler's view. */
683
- function toSubtaskNode(s) {
684
- return {
685
- id: s.id,
686
- ordinal: s.ordinal,
687
- status: s.status,
688
- dependsOn: s.dependsOn
689
- };
690
- }
691
- //# sourceMappingURL=agent.js.map
@@ -4,8 +4,8 @@
4
4
  * A round agent answers the user or hands work to isolated subagents, waits for
5
5
  * their durable results, and decides again. This subpath ships the whole
6
6
  * mechanism of that: the round loop and its primary→fallback→repair ladder
7
- * ({@link runTurn}), the durable Workflow orchestration with its DAG wave
8
- * scheduler ({@link runHandleTask}), the Durable Object body ({@link
7
+ * ({@link runTurn}), the durable Workflow orchestration that runs a round's
8
+ * subtasks concurrently ({@link runHandleTask}), the Durable Object body ({@link
9
9
  * RoundAgentBase}), and the subagent facet host ({@link RecipeSubagentHost}).
10
10
  *
11
11
  * **Opt-in, and its own subpath on purpose.** An agent whose turn is a single
@@ -21,5 +21,5 @@ export type { RoundPolicy } from "./policy.js";
21
21
  export { RoundAgentBase } from "./agent.js";
22
22
  export { RecipeSubagentHost, type SubagentClass } from "./subagent.js";
23
23
  export { runHandleTask, type HandleTaskDeps, type HandleTaskParams } from "./workflow.js";
24
+ export type { NonRecoverableKind, RoundFailureKind } from "../agent/inference.js";
24
25
  export { buildTurnInstructions, joinSuccessfulBranches, renderTurnMessages, runTurn, type RoundMode, type RunTurnArgs, type RunTurnOutcome, type TurnInstructions } from "./turn.js";
25
- //# sourceMappingURL=index.d.ts.map
@@ -4,8 +4,8 @@
4
4
  * A round agent answers the user or hands work to isolated subagents, waits for
5
5
  * their durable results, and decides again. This subpath ships the whole
6
6
  * mechanism of that: the round loop and its primary→fallback→repair ladder
7
- * ({@link runTurn}), the durable Workflow orchestration with its DAG wave
8
- * scheduler ({@link runHandleTask}), the Durable Object body ({@link
7
+ * ({@link runTurn}), the durable Workflow orchestration that runs a round's
8
+ * subtasks concurrently ({@link runHandleTask}), the Durable Object body ({@link
9
9
  * RoundAgentBase}), and the subagent facet host ({@link RecipeSubagentHost}).
10
10
  *
11
11
  * **Opt-in, and its own subpath on purpose.** An agent whose turn is a single
@@ -21,4 +21,3 @@ export { RoundAgentBase } from "./agent.js";
21
21
  export { RecipeSubagentHost } from "./subagent.js";
22
22
  export { runHandleTask } from "./workflow.js";
23
23
  export { buildTurnInstructions, joinSuccessfulBranches, renderTurnMessages, runTurn } from "./turn.js";
24
- //# sourceMappingURL=index.js.map
@@ -2,8 +2,8 @@ import type { AgentLimits } from "../config.js";
2
2
  /**
3
3
  * Everything about a round loop that is **yours**, not core's.
4
4
  *
5
- * The round loop in this subpath is mechanism: the DAG wave scheduler, chunked
6
- * subagent execution, cancellation ordering, idempotent recovery, the
5
+ * The round loop in this subpath is mechanism: concurrent subtask execution,
6
+ * chunked subagent runs, cancellation ordering, idempotent recovery, the
7
7
  * primary→fallback→repair ladder. None of it varies between agents, and every
8
8
  * place it *did* vary between two agents in one repo turned out to be a bug.
9
9
  *
@@ -96,4 +96,3 @@ export interface RoundPolicy {
96
96
  partialNote: string;
97
97
  };
98
98
  }
99
- //# sourceMappingURL=policy.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=policy.js.map
@@ -1,6 +1,7 @@
1
1
  import type { AgentPlugin } from "../contract/plugin.js";
2
- import type { CoreConfigOverrides } from "../config.js";
2
+ import type { CoreConfigOverrides, ModelConfig } from "../config.js";
3
3
  import type { AiEnv } from "../env.js";
4
+ import type { ModelRuntime } from "../agent/model.js";
4
5
  import { RecipeSubagentBase, type SubagentRuntime } from "../subagent/index.js";
5
6
  import type { PluginHost } from "../host/plugin-host.js";
6
7
  /**
@@ -54,6 +55,23 @@ export declare abstract class RecipeSubagentHost<TEnv extends Cloudflare.Env & A
54
55
  * that an implementation building anything expensive should memoize its own.
55
56
  */
56
57
  protected subagentRuntime(): SubagentRuntime;
58
+ /**
59
+ * Which provider this facet's chunks run on. Mirrors
60
+ * {@link file://../host/agent.ts LoopingAgent.modelRuntime}, and **must be
61
+ * overridden to match it** — a facet that keeps the Workers AI default while
62
+ * its parent runs on Claude would silently execute every subtask on a
63
+ * different model than the round that delegated it.
64
+ *
65
+ * The two seams take the same arguments precisely so that keeping them in step
66
+ * needs no discipline: write the provider once as a
67
+ * {@link file://../agent/model.ts ModelRuntimeFactory} and have both return
68
+ * it. Two hand-copied `createAnthropicModelRuntime({...})` bodies is what this
69
+ * shape exists to stop, because nothing type-checks their agreement.
70
+ *
71
+ * Takes the model config rather than reading `this.config`, because the facet
72
+ * resolves its config inside `buildRuntime` and this is called from there.
73
+ */
74
+ protected modelRuntime(model: ModelConfig): ModelRuntime;
57
75
  private buildRuntime;
58
76
  }
59
77
  /**
@@ -64,4 +82,3 @@ export declare abstract class RecipeSubagentHost<TEnv extends Cloudflare.Env & A
64
82
  * typed as this: any named subclass below it.
65
83
  */
66
84
  export type SubagentClass = new (...args: ConstructorParameters<typeof RecipeSubagentBase>) => RecipeSubagentBase;
67
- //# sourceMappingURL=subagent.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { createAgentRuntime } from "../runtime/index.js";
2
- import { createModelRuntime } from "../agent/model.js";
2
+ import { workersAIModels } from "../agent/workers-ai/index.js";
3
3
  import { RecipeSubagentBase } from "../subagent/index.js";
4
4
  /**
5
5
  * The host half of a subagent facet: the same plugins as its parent, assembled
@@ -50,6 +50,25 @@ export class RecipeSubagentHost extends RecipeSubagentBase {
50
50
  subagentRuntime() {
51
51
  return (this._rt ??= this.buildRuntime());
52
52
  }
53
+ /**
54
+ * Which provider this facet's chunks run on. Mirrors
55
+ * {@link file://../host/agent.ts LoopingAgent.modelRuntime}, and **must be
56
+ * overridden to match it** — a facet that keeps the Workers AI default while
57
+ * its parent runs on Claude would silently execute every subtask on a
58
+ * different model than the round that delegated it.
59
+ *
60
+ * The two seams take the same arguments precisely so that keeping them in step
61
+ * needs no discipline: write the provider once as a
62
+ * {@link file://../agent/model.ts ModelRuntimeFactory} and have both return
63
+ * it. Two hand-copied `createAnthropicModelRuntime({...})` bodies is what this
64
+ * shape exists to stop, because nothing type-checks their agreement.
65
+ *
66
+ * Takes the model config rather than reading `this.config`, because the facet
67
+ * resolves its config inside `buildRuntime` and this is called from there.
68
+ */
69
+ modelRuntime(model) {
70
+ return workersAIModels(this.env, model);
71
+ }
53
72
  buildRuntime() {
54
73
  const config = this.agentConfig();
55
74
  const runtime = createAgentRuntime({
@@ -73,17 +92,14 @@ export class RecipeSubagentHost extends RecipeSubagentBase {
73
92
  return {
74
93
  policy: runtime.policy,
75
94
  types: runtime.types,
76
- models: createModelRuntime({
77
- ai: this.env.AI,
78
- config: runtime.config.model
79
- }),
95
+ models: this.modelRuntime(runtime.config.model),
80
96
  toolFamilies: runtime.toolFamilies,
81
97
  toolOutputWindow: runtime.config.toolOutputWindow,
82
98
  maxOutputTokens: runtime.config.model.maxOutputTokens,
99
+ maxRetries: runtime.config.model.maxRetries,
83
100
  // Always defined: the plugin that declared a backend, or core's in-memory
84
101
  // fallback. So this needs no null check.
85
102
  workspaceBacking: runtime.workspaceBacking
86
103
  };
87
104
  }
88
105
  }
89
- //# sourceMappingURL=subagent.js.map
@@ -3,7 +3,7 @@ import type { SessionMessage } from "agents/experimental/memory/session";
3
3
  import type { AgentLimits } from "../config.js";
4
4
  import type { SubtaskTypeRegistry } from "../subtasks/subtask-types.js";
5
5
  import { type SessionLike } from "../agent/session.js";
6
- import { type OnContent } from "../agent/inference.js";
6
+ import { type OnContent, type RoundFailureKind } from "../agent/inference.js";
7
7
  import { type TurnBudget } from "../agent/budget.js";
8
8
  import type { ModelPair } from "../agent/model.js";
9
9
  import { type ReferenceCatalogEntry } from "../subtasks/catalog.js";
@@ -14,7 +14,7 @@ import type { RoundPolicy } from "./policy.js";
14
14
  * Session that ends in one of two decisions — answer the user, or delegate.
15
15
  *
16
16
  * This is the whole task pipeline's control point. The Workflow runs rounds in a
17
- * loop: a round that delegates gets its Subtask DAG executed and is followed by
17
+ * loop: a round that delegates gets its Subtasks executed and is followed by
18
18
  * another round; a round that answers ends the Task. So "compose" is not a
19
19
  * separate phase with its own rules — it is simply the round in which the model
20
20
  * decides it has enough to answer.
@@ -43,12 +43,8 @@ import type { RoundPolicy } from "./policy.js";
43
43
  * delegating twice is allowed.
44
44
  *
45
45
  * What *is* forced is that the round end in a control call at all —
46
- * `toolChoice: "required"`, with both endings declared as tools. Plain text used to
47
- * be an outcome, and it made narration indistinguishable from an answer: a model
48
- * that wrote "I'll start the game" and emitted no call ended the Task successfully
49
- * having done nothing. Two named tools is also a much easier discrimination for a
50
- * small model than prose-versus-tool, which is what the weaker fallback models
51
- * consistently got wrong.
46
+ * `toolChoice: "required"`, with both endings declared as tools. Prose is not an
47
+ * outcome: see {@link file://../agent/final-reply.ts final-reply.ts} for why not.
52
48
  *
53
49
  * Narration survived that fix by moving house. A round whose results have just come
54
50
  * back can still announce its next step *inside* a `final_reply` — "now sending the
@@ -179,6 +175,8 @@ export interface RunTurnArgs {
179
175
  maxSubtasks: number;
180
176
  /** `CoreConfig.model.maxOutputTokens`. */
181
177
  maxOutputTokens: number;
178
+ /** `CoreConfig.model.maxRetries` — retries on *this* model before the fallback. */
179
+ maxRetries: number;
182
180
  /** The prompt suffixes, memoized by the DO. See {@link buildTurnInstructions}. */
183
181
  instructions: TurnInstructions;
184
182
  /** The note a deterministic join appends when it has to disclose gaps. */
@@ -187,10 +185,17 @@ export interface RunTurnArgs {
187
185
  onContent?: OnContent;
188
186
  }
189
187
  /**
190
- * Terminal outcome of one round. `failed` means both models produced unusable
191
- * output *and* there was no durable work to fall back on — the parent Task fails
192
- * rather than running a synthesized subtask nobody asked for. Transient faults
193
- * throw instead (the Workflow step retries).
188
+ * Terminal outcome of one round. `failed` means the round produced no answer and
189
+ * there was no durable work to fall back on — the parent Task fails rather than
190
+ * running a synthesized subtask nobody asked for. Transient faults throw instead
191
+ * (the Workflow step retries).
192
+ *
193
+ * `kind` is *why*, not a second outcome. The round is over either way and the
194
+ * Task it delivers has the same shape; what the kind decides is what a human is
195
+ * told — `exhausted` is "the models could not do it", a credential kind is "a
196
+ * human must fix the deployment". Both were once separate statuses, and every
197
+ * consumer promptly bundled them back together to do the same three things. See
198
+ * {@link RoundFailureKind}.
194
199
  *
195
200
  * What the round cost is not here: it is in the caller's `TurnBudget`, which every
196
201
  * exit has already charged — including the ones that failed. A round that burned
@@ -206,6 +211,7 @@ export type RunTurnOutcome = {
206
211
  drafts: SubtaskDraft[];
207
212
  } | {
208
213
  status: "failed";
214
+ kind: RoundFailureKind;
209
215
  error: string;
210
216
  };
211
217
  /**
@@ -225,7 +231,19 @@ export type RunTurnOutcome = {
225
231
  * Throws only on a transient platform fault (for the Workflow step to retry).
226
232
  * A deterministic failure that outlasts every repair on both slots, with durable
227
233
  * work behind it, degrades to {@link joinSuccessfulBranches} rather than discarding
228
- * completed branches; with nothing behind it, it resolves to `{ status: "failed" }`.
234
+ * completed branches; with nothing behind it, it resolves to
235
+ * `{ status: "failed", kind: "exhausted" }`.
236
+ *
237
+ * The third failure is neither, and it short-circuits the *model* recoveries
238
+ * above: a {@link nonRecoverableKind} error ends the round from wherever it
239
+ * happens, carrying that kind — without repairing and **without reaching the
240
+ * fallback slot**, both of which would only present the same dead credential
241
+ * again. See that function for why the transient/deterministic split cannot
242
+ * express it.
243
+ *
244
+ * It does **not** skip the deterministic join. That path needs no credential —
245
+ * it is string concatenation over rows that are already durable — so completed
246
+ * branches are still delivered, and the credential fault reaches the operator
247
+ * through the log rather than by throwing away finished work.
229
248
  */
230
249
  export declare function runTurn(args: RunTurnArgs): Promise<RunTurnOutcome>;
231
- //# sourceMappingURL=turn.d.ts.map