@namzu/sdk 7.0.0 → 9.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 (206) hide show
  1. package/CHANGELOG.md +652 -0
  2. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  3. package/dist/agents/SupervisorAgent.js +186 -163
  4. package/dist/agents/SupervisorAgent.js.map +1 -1
  5. package/dist/agents/__tests__/a-policy-nobody-can-select.test.d.ts +2 -0
  6. package/dist/agents/__tests__/a-policy-nobody-can-select.test.d.ts.map +1 -0
  7. package/dist/agents/__tests__/a-policy-nobody-can-select.test.js +177 -0
  8. package/dist/agents/__tests__/a-policy-nobody-can-select.test.js.map +1 -0
  9. package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts +2 -0
  10. package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts.map +1 -0
  11. package/dist/agents/__tests__/supervisor-inbox-scope.test.js +125 -0
  12. package/dist/agents/__tests__/supervisor-inbox-scope.test.js.map +1 -0
  13. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  14. package/dist/bridge/a2a/mapper.js +4 -0
  15. package/dist/bridge/a2a/mapper.js.map +1 -1
  16. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  17. package/dist/bridge/sse/mapper.js +24 -0
  18. package/dist/bridge/sse/mapper.js.map +1 -1
  19. package/dist/contracts/api.d.ts +1 -1
  20. package/dist/contracts/api.d.ts.map +1 -1
  21. package/dist/gateway/__tests__/a-knob-nobody-can-turn.test.d.ts +2 -0
  22. package/dist/gateway/__tests__/a-knob-nobody-can-turn.test.d.ts.map +1 -0
  23. package/dist/gateway/__tests__/a-knob-nobody-can-turn.test.js +114 -0
  24. package/dist/gateway/__tests__/a-knob-nobody-can-turn.test.js.map +1 -0
  25. package/dist/gateway/__tests__/completion-inbox.test.js +292 -2
  26. package/dist/gateway/__tests__/completion-inbox.test.js.map +1 -1
  27. package/dist/gateway/completion-inbox.d.ts +94 -6
  28. package/dist/gateway/completion-inbox.d.ts.map +1 -1
  29. package/dist/gateway/completion-inbox.js +235 -15
  30. package/dist/gateway/completion-inbox.js.map +1 -1
  31. package/dist/gateway/local.d.ts.map +1 -1
  32. package/dist/gateway/local.js +19 -6
  33. package/dist/gateway/local.js.map +1 -1
  34. package/dist/manager/agent/__tests__/lifecycle.test.js +56 -0
  35. package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -1
  36. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  37. package/dist/manager/agent/lifecycle.js +62 -33
  38. package/dist/manager/agent/lifecycle.js.map +1 -1
  39. package/dist/manager/plan/__tests__/a-plan-that-succeeded-is-not-failed.test.d.ts +2 -0
  40. package/dist/manager/plan/__tests__/a-plan-that-succeeded-is-not-failed.test.d.ts.map +1 -0
  41. package/dist/manager/plan/__tests__/a-plan-that-succeeded-is-not-failed.test.js +73 -0
  42. package/dist/manager/plan/__tests__/a-plan-that-succeeded-is-not-failed.test.js.map +1 -0
  43. package/dist/manager/plan/lifecycle.d.ts +48 -0
  44. package/dist/manager/plan/lifecycle.d.ts.map +1 -1
  45. package/dist/manager/plan/lifecycle.js +56 -0
  46. package/dist/manager/plan/lifecycle.js.map +1 -1
  47. package/dist/manager/run/persistence.d.ts +8 -0
  48. package/dist/manager/run/persistence.d.ts.map +1 -1
  49. package/dist/manager/run/persistence.js +12 -0
  50. package/dist/manager/run/persistence.js.map +1 -1
  51. package/dist/run/reporter.d.ts.map +1 -1
  52. package/dist/run/reporter.js +11 -0
  53. package/dist/run/reporter.js.map +1 -1
  54. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts +2 -0
  55. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts.map +1 -0
  56. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js +142 -0
  57. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js.map +1 -0
  58. package/dist/runtime/query/__tests__/completion-notification.test.js +414 -32
  59. package/dist/runtime/query/__tests__/completion-notification.test.js.map +1 -1
  60. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts +2 -0
  61. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts.map +1 -0
  62. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js +100 -0
  63. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js.map +1 -0
  64. package/dist/runtime/query/__tests__/the-plan-graph-reaches-a-host.test.d.ts +2 -0
  65. package/dist/runtime/query/__tests__/the-plan-graph-reaches-a-host.test.d.ts.map +1 -0
  66. package/dist/runtime/query/__tests__/the-plan-graph-reaches-a-host.test.js +62 -0
  67. package/dist/runtime/query/__tests__/the-plan-graph-reaches-a-host.test.js.map +1 -0
  68. package/dist/runtime/query/events.d.ts.map +1 -1
  69. package/dist/runtime/query/events.js +5 -0
  70. package/dist/runtime/query/events.js.map +1 -1
  71. package/dist/runtime/query/guard.d.ts +28 -0
  72. package/dist/runtime/query/guard.d.ts.map +1 -1
  73. package/dist/runtime/query/guard.js +31 -0
  74. package/dist/runtime/query/guard.js.map +1 -1
  75. package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts +2 -0
  76. package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts.map +1 -0
  77. package/dist/runtime/query/iteration/__tests__/settle-grace.test.js +226 -0
  78. package/dist/runtime/query/iteration/__tests__/settle-grace.test.js.map +1 -0
  79. package/dist/runtime/query/iteration/index.d.ts +92 -0
  80. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  81. package/dist/runtime/query/iteration/index.js +814 -566
  82. package/dist/runtime/query/iteration/index.js.map +1 -1
  83. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts +2 -0
  84. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts.map +1 -0
  85. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js +95 -0
  86. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js.map +1 -0
  87. package/dist/runtime/query/iteration/phases/compaction.d.ts +34 -0
  88. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  89. package/dist/runtime/query/iteration/phases/compaction.js +61 -4
  90. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  91. package/dist/store/task/disk.d.ts.map +1 -1
  92. package/dist/store/task/disk.js +14 -0
  93. package/dist/store/task/disk.js.map +1 -1
  94. package/dist/store/task/memory.d.ts.map +1 -1
  95. package/dist/store/task/memory.js +14 -0
  96. package/dist/store/task/memory.js.map +1 -1
  97. package/dist/tools/__tests__/untrusted-envelope.test.js +16 -0
  98. package/dist/tools/__tests__/untrusted-envelope.test.js.map +1 -1
  99. package/dist/tools/coordinator/__tests__/a-failed-worker-is-not-an-answer.test.d.ts +2 -0
  100. package/dist/tools/coordinator/__tests__/a-failed-worker-is-not-an-answer.test.d.ts.map +1 -0
  101. package/dist/tools/coordinator/__tests__/a-failed-worker-is-not-an-answer.test.js +101 -0
  102. package/dist/tools/coordinator/__tests__/a-failed-worker-is-not-an-answer.test.js.map +1 -0
  103. package/dist/tools/coordinator/__tests__/completion-delivery.test.js +117 -0
  104. package/dist/tools/coordinator/__tests__/completion-delivery.test.js.map +1 -1
  105. package/dist/tools/coordinator/__tests__/task-list.test.js +57 -0
  106. package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -1
  107. package/dist/tools/coordinator/__tests__/the-plan-a-human-approves.test.d.ts +2 -0
  108. package/dist/tools/coordinator/__tests__/the-plan-a-human-approves.test.d.ts.map +1 -0
  109. package/dist/tools/coordinator/__tests__/the-plan-a-human-approves.test.js +99 -0
  110. package/dist/tools/coordinator/__tests__/the-plan-a-human-approves.test.js.map +1 -0
  111. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  112. package/dist/tools/coordinator/agent.js +17 -23
  113. package/dist/tools/coordinator/agent.js.map +1 -1
  114. package/dist/tools/coordinator/index.d.ts.map +1 -1
  115. package/dist/tools/coordinator/index.js +142 -14
  116. package/dist/tools/coordinator/index.js.map +1 -1
  117. package/dist/tools/coordinator/outcome.d.ts +60 -0
  118. package/dist/tools/coordinator/outcome.d.ts.map +1 -0
  119. package/dist/tools/coordinator/outcome.js +68 -0
  120. package/dist/tools/coordinator/outcome.js.map +1 -0
  121. package/dist/tools/untrusted-envelope.d.ts.map +1 -1
  122. package/dist/tools/untrusted-envelope.js +9 -1
  123. package/dist/tools/untrusted-envelope.js.map +1 -1
  124. package/dist/types/agent/gateway.d.ts +37 -2
  125. package/dist/types/agent/gateway.d.ts.map +1 -1
  126. package/dist/types/agent/supervisor.d.ts +38 -1
  127. package/dist/types/agent/supervisor.d.ts.map +1 -1
  128. package/dist/types/plan/index.d.ts +26 -0
  129. package/dist/types/plan/index.d.ts.map +1 -1
  130. package/dist/types/provider/interface.d.ts +35 -0
  131. package/dist/types/provider/interface.d.ts.map +1 -1
  132. package/dist/types/run/entity.d.ts +16 -0
  133. package/dist/types/run/entity.d.ts.map +1 -1
  134. package/dist/types/run/events.d.ts +92 -0
  135. package/dist/types/run/events.d.ts.map +1 -1
  136. package/dist/types/run/events.js.map +1 -1
  137. package/dist/types/run/prepare-step.d.ts +17 -2
  138. package/dist/types/run/prepare-step.d.ts.map +1 -1
  139. package/dist/types/task/index.d.ts +19 -1
  140. package/dist/types/task/index.d.ts.map +1 -1
  141. package/dist/types/task/index.js +10 -1
  142. package/dist/types/task/index.js.map +1 -1
  143. package/dist/types/verification/index.d.ts +98 -0
  144. package/dist/types/verification/index.d.ts.map +1 -1
  145. package/dist/types/verification/index.js +10 -0
  146. package/dist/types/verification/index.js.map +1 -1
  147. package/dist/verification/__tests__/argument-pattern.test.d.ts +2 -0
  148. package/dist/verification/__tests__/argument-pattern.test.d.ts.map +1 -0
  149. package/dist/verification/__tests__/argument-pattern.test.js +122 -0
  150. package/dist/verification/__tests__/argument-pattern.test.js.map +1 -0
  151. package/dist/verification/gate.d.ts.map +1 -1
  152. package/dist/verification/gate.js +44 -0
  153. package/dist/verification/gate.js.map +1 -1
  154. package/dist/verification/rules.d.ts.map +1 -1
  155. package/dist/verification/rules.js +27 -0
  156. package/dist/verification/rules.js.map +1 -1
  157. package/package.json +1 -1
  158. package/src/agents/SupervisorAgent.ts +189 -167
  159. package/src/agents/__tests__/a-policy-nobody-can-select.test.ts +202 -0
  160. package/src/agents/__tests__/supervisor-inbox-scope.test.ts +149 -0
  161. package/src/bridge/a2a/mapper.ts +4 -0
  162. package/src/bridge/sse/mapper.ts +25 -0
  163. package/src/contracts/api.ts +1 -0
  164. package/src/gateway/__tests__/a-knob-nobody-can-turn.test.ts +139 -0
  165. package/src/gateway/__tests__/completion-inbox.test.ts +348 -2
  166. package/src/gateway/completion-inbox.ts +248 -16
  167. package/src/gateway/local.ts +19 -7
  168. package/src/manager/agent/__tests__/lifecycle.test.ts +74 -0
  169. package/src/manager/agent/lifecycle.ts +72 -42
  170. package/src/manager/plan/__tests__/a-plan-that-succeeded-is-not-failed.test.ts +88 -0
  171. package/src/manager/plan/lifecycle.ts +61 -0
  172. package/src/manager/run/persistence.ts +12 -0
  173. package/src/run/reporter.ts +12 -0
  174. package/src/runtime/query/__tests__/completion-does-not-erase-the-answer.test.ts +163 -0
  175. package/src/runtime/query/__tests__/completion-notification.test.ts +486 -34
  176. package/src/runtime/query/__tests__/context-size-on-the-wire.test.ts +122 -0
  177. package/src/runtime/query/__tests__/the-plan-graph-reaches-a-host.test.ts +79 -0
  178. package/src/runtime/query/events.ts +5 -0
  179. package/src/runtime/query/guard.ts +32 -0
  180. package/src/runtime/query/iteration/__tests__/settle-grace.test.ts +265 -0
  181. package/src/runtime/query/iteration/index.ts +902 -636
  182. package/src/runtime/query/iteration/phases/__tests__/compaction-declined.test.ts +124 -0
  183. package/src/runtime/query/iteration/phases/compaction.ts +83 -10
  184. package/src/store/task/disk.ts +15 -0
  185. package/src/store/task/memory.ts +15 -0
  186. package/src/tools/__tests__/untrusted-envelope.test.ts +23 -0
  187. package/src/tools/coordinator/__tests__/a-failed-worker-is-not-an-answer.test.ts +133 -0
  188. package/src/tools/coordinator/__tests__/completion-delivery.test.ts +147 -0
  189. package/src/tools/coordinator/__tests__/task-list.test.ts +72 -0
  190. package/src/tools/coordinator/__tests__/the-plan-a-human-approves.test.ts +121 -0
  191. package/src/tools/coordinator/agent.ts +17 -25
  192. package/src/tools/coordinator/index.ts +155 -18
  193. package/src/tools/coordinator/outcome.ts +70 -0
  194. package/src/tools/untrusted-envelope.ts +9 -1
  195. package/src/types/agent/gateway.ts +37 -2
  196. package/src/types/agent/supervisor.ts +40 -1
  197. package/src/types/plan/index.ts +29 -0
  198. package/src/types/provider/interface.ts +39 -0
  199. package/src/types/run/entity.ts +17 -0
  200. package/src/types/run/events.ts +92 -0
  201. package/src/types/run/prepare-step.ts +17 -2
  202. package/src/types/task/index.ts +22 -3
  203. package/src/types/verification/index.ts +61 -0
  204. package/src/verification/__tests__/argument-pattern.test.ts +158 -0
  205. package/src/verification/gate.ts +45 -0
  206. package/src/verification/rules.ts +28 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,657 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 16dc634: A failed worker is reported as a failure, and a plan task can say it failed.
8
+
9
+ **`create_task` reported a failed worker as a success.** Two layers can disagree
10
+ about whether a delegated run succeeded: the gateway's `TaskHandle.state`, and
11
+ the run's own `BaseAgentResult.status`. The kernel's `finalizeChild` always calls
12
+ `markCompleted`, so `state === 'completed'` holds for a child that ran and
13
+ returned `status: 'failed'` — and `create_task` asked only that layer. The model
14
+ received the failure text as an answer, the tool result carried
15
+ `isError: false`, and the plan task was written closed as though the work had
16
+ been done.
17
+
18
+ The correct two-authority predicate was already written, twenty lines away, in
19
+ the canonical `Agent` tool — put there because a review caught it on that site,
20
+ and nothing carried the answer to the other one. It now lives in one place both
21
+ reach.
22
+
23
+ **`TaskStatus` gains `failed`, and that is the breaking part.** A unit that did
24
+ not succeed had nowhere to say so, which is why a failed delegation was recorded
25
+ as `completed` with the failure encoded as prose in `description`: a reader
26
+ scanning statuses saw work that had been done, and a dependent unit had no way
27
+ to tell at all.
28
+
29
+ If you switch exhaustively over `TaskStatus`, or hold a `Record<TaskStatus, T>`,
30
+ you need a `failed` arm. `isTerminalTaskStatus` now returns `true` for it —
31
+ terminal means "will not change on its own", not "succeeded", and a unit blocked
32
+ on something that failed would otherwise wait forever for a status that will
33
+ never arrive. In the store's transition ranking `failed` sits alongside
34
+ `completed` rather than after it, so `in_progress → failed` is allowed and
35
+ `completed → failed` is not.
36
+
37
+ **Two smaller repairs ride along.** A background launch refused for want of a
38
+ completion inbox now marks its plan task failed rather than leaving it in
39
+ progress with no worker behind it — nothing later closes a task whose launch
40
+ never happened. And the `Agent` tool passes `parentSpan` when creating its
41
+ child, so a delegated run joins the turn that asked for it instead of starting
42
+ its own root trace; `create_task` has done this all along.
43
+
44
+ - a743c7e: A delegated run is built with the config its caller asked for, and a supervisor can select its sibling-failure policy.
45
+
46
+ Two capabilities were declared, documented, typed, and unreachable. Both are the
47
+ same defect: a knob wired to nothing, which reads to a caller as a knob that
48
+ works.
49
+
50
+ **`CreateTaskOptions.configOverrides` was accepted and dropped.**
51
+ `LocalTaskGateway.createTask` built its own `configOverrides` object out of
52
+ `parentSpan` alone and never read the field, so a caller pinning a delegated run
53
+ to a cheaper model, or capping its iterations, got the agent's defaults and no
54
+ indication anything had been ignored. It is forwarded now. A caller who sets
55
+ both the field and the dedicated `parentSpan` option gets the dedicated one for
56
+ the span — that is the specific field for the job — and keeps every other
57
+ override alongside it.
58
+
59
+ **`siblingFailurePolicy` could not be selected by any host.**
60
+ `LocalTaskGateway` has honoured it since it was written and the cancellation
61
+ machinery behind `'cancel-siblings'` is complete — but it was the fifth
62
+ constructor argument of a gateway `SupervisorAgent` builds itself, and the
63
+ supervisor passed four. Every host in existence ran `'continue'`, and the only
64
+ route to the other value was to construct the gateway by hand and pass it as
65
+ `config.gateway`. It is now `SupervisorAgentConfig.siblingFailurePolicy`.
66
+
67
+ `'continue'` remains the default and deliberately so: partial results are
68
+ usually worth having, and tearing down healthy siblings on any failure lets one
69
+ flaky child waste four good ones. `'cancel-siblings'` is for a fan-out whose
70
+ parts only mean something together. The choice is now expressible; the answer
71
+ has not changed. The field is ignored when the host supplies its own `gateway`,
72
+ which owns its policy.
73
+
74
+ **Breaking:** `CreateTaskOptions.configOverrides` is now typed
75
+ `Partial<BaseAgentConfig>` instead of `Record<string, unknown>`. It lands on
76
+ `SendMessageOptions.configOverrides`, which is already that shape, and the loose
77
+ type let a misspelled key type-check and then silently do nothing — the same
78
+ silence the field was already producing. If you pass a key that is not on
79
+ `BaseAgentConfig`, it will now fail to compile; that key was never being applied.
80
+
81
+ **Also:** the two-authority failure check in `LocalTaskGateway` moves to
82
+ `taskFailed` in `tools/coordinator/outcome.ts`, next to `taskSucceeded`. It is
83
+ deliberately _not_ the negation of that predicate — a task that is still running
84
+ satisfies neither, and cancelling a fan-out on `!taskSucceeded` would tear down
85
+ siblings the moment the first child had merely not finished yet. The gateway's
86
+ copy was correct; a rule each caller has to remember is one a caller eventually
87
+ forgets, which is what happened to `taskSucceeded` before it was consolidated.
88
+
89
+ - 529b343: `PlanManager.completePlan` refuses an unreported step instead of scoring it a failure.
90
+
91
+ **A plan that fully succeeded was reported as failed.** `completePlan` asked one
92
+ question — "is every step `completed` or `skipped`?" — and everything that was
93
+ not fell to the same branch. A step still `pending` therefore produced
94
+ `status: 'failed'`, indistinguishable from a step that genuinely failed. Since
95
+ `addStep` defaults every step to `pending`, a host that added steps, did the
96
+ work, and settled the plan without calling `updateStepStatus` on each one got
97
+ `failed` for a plan where nothing had gone wrong. That is the path of least
98
+ effort through this API, not an unusual one.
99
+
100
+ The two cases are different facts and deserve different answers. A step that
101
+ FAILED is an outcome: report the plan failed. A step nobody reported on is not
102
+ an outcome at all — it says the caller and the plan disagree about whether the
103
+ work is over, and answering `failed` settles that disagreement by inventing a
104
+ result.
105
+
106
+ **What changes for you.** `completePlan()` now throws when any step is still
107
+ `pending` or `running`. The message names the unfinished steps and both ways
108
+ forward, because a caller in this position either forgot to report progress or
109
+ called too early, and only they know which:
110
+
111
+ - report each step with `updateStepStatus` — `'skipped'` is a valid outcome for
112
+ work that was planned and then not needed; or
113
+ - call `failPlan` if the plan is being abandoned, which marks unfinished steps
114
+ `skipped` and settles the plan as failed.
115
+
116
+ Behaviour is unchanged once every step has reported: all `completed` or
117
+ `skipped` still yields `completed`, and any `failed` still yields `failed`.
118
+ No code in this repository called `completePlan`, so nothing inside the kernel
119
+ changes behaviour; the affected callers are hosts.
120
+
121
+ **`PlanManager` now says which half of it the kernel drives.** The kernel builds
122
+ a plan, gates it, translates its events, and settles it on failure — it never
123
+ reports a step outcome and never settles a plan that succeeded. That is a
124
+ deliberate split, since `drainQuery` hands the manager to the host through
125
+ `onContextCreated` for exactly this purpose, and a search for callers inside the
126
+ package finds none because the callers are outside it. The absence had already
127
+ been read once as a dead layer and proposed for deletion; what that would have
128
+ deleted is a working human-in-the-loop approval gate. It is written down now.
129
+
130
+ ### Minor Changes
131
+
132
+ - e355049: The plan a human approves names the agent the model chose.
133
+
134
+ `approve_plan` asks the model for an `agent_id` per step — "which agent handles
135
+ this" — and reduced the answer to a boolean. The step got
136
+ `toolName: 'create_task'` when any agent was named and nothing when not, so the
137
+ name was dropped between the model saying it and the human being shown the plan.
138
+
139
+ The approval is the one moment where that difference can still be acted on.
140
+ Approving "delegate this step" is not the same as approving "delegate this step
141
+ to the agent with shell access", and a reviewer who cannot see which agent was
142
+ chosen cannot withhold approval from the wrong one. Two delegated steps reached
143
+ the approver identical in every field.
144
+
145
+ `PlanStep` gains `agentId?: string`, populated by `approve_plan` from the
146
+ model's choice. A host rendering a plan approval can show it directly. Absent
147
+ still means the step is the orchestrator's own work, which is what omitting
148
+ `agent_id` says — so absent stays absent rather than becoming a placeholder.
149
+
150
+ Typed rather than folded into the existing `estimatedInput`, which is `unknown`:
151
+ an approval gate's whole job is being readable, and a field a host must cast
152
+ before it can render is one a host renders wrong or not at all. `estimatedInput`
153
+ is now documented as having no producer and no reader, since that is what it
154
+ has, and it is left in place because it is on the published typings.
155
+
156
+ - 16dc634: A host can see the fan-out gate, and the plan graph the model is already keeping.
157
+
158
+ **`SupervisorAgentConfig` gains `maxToolConcurrency`.** The kernel has honoured
159
+ it all along and `ReactiveAgent` forwards it — it was missing on the one agent
160
+ whose entire job is delegation. So the agent that fans out could not set the gate
161
+ that bounds a fan-out, while the agent that does not fan out could, and a host
162
+ wanting a narrower one had to reach past the supervisor to `drainQuery`.
163
+
164
+ Note what it bounds: how many delegated children run **concurrently**, not how
165
+ many a turn may launch. A model emitting twenty `create_task` blocks still
166
+ launches twenty; they queue.
167
+
168
+ **`task_created` and `task_updated` carry `blockedBy`.** The task store
169
+ maintains a full dependency graph — `blocks` and `blockedBy` mirrored on both
170
+ ends, written under a lock, deadlock-avoided — and none of it reached the wire.
171
+ A host could draw a flat list of units and nothing about their order, while the
172
+ model was already maintaining the order.
173
+
174
+ Absent rather than empty when a unit depends on nothing, so a reader can tell
175
+ "no dependencies" from an emitter that predates the field.
176
+
177
+ **And `block()` announced nothing at all.** Both stores wrote the edge and
178
+ emitted no event, so the graph was observable only by polling: a listener saw a
179
+ unit created and never learned that something now waits on it. Both stores now
180
+ announce **both ends**, because both changed — a host tracking one side would
181
+ draw half the edge. The disk store announces only when something actually
182
+ changed, so re-establishing an existing edge stays silent.
183
+
184
+ That second half is the one worth knowing about if you consume these events: the
185
+ field alone would have been useless, because the moment a dependency is created
186
+ was never on the wire in the first place.
187
+
188
+ ### Patch Changes
189
+
190
+ - 16dc634: A concurrent fan-out no longer allocates more budget than the parent has.
191
+
192
+ `sendMessage` read the parent's remaining budget at the top and debited it after
193
+ `provisionSpawn` — putting the two halves of a read-modify-write on either side
194
+ of an await, with the only critical section in between. So siblings launched
195
+ from one assistant turn all read the same undebited number and each took a
196
+ fraction of it. Measured: \*\*four concurrent children were handed 50 000 + 50 000
197
+
198
+ - 50 000 + 50 000 from a pool of 100 000.\*\*
199
+
200
+ `create_task`'s own description instructs exactly the shape that triggers it —
201
+ _"'fan out 8 specialists' is one assistant message with 8 create_task blocks"_ —
202
+ so the documented usage was the reproduction.
203
+
204
+ The read, the refusal when an allocation floors to zero, and the debit now all
205
+ happen inside the per-parent spawn lock. That keeps the property the debit's
206
+ placement was chosen for — a spawn this call rejects burns no allocation — while
207
+ closing the race that placement opened. It was introduced by a correct fix to a
208
+ different bug: moving the debit after the provisioning put it outside the lock.
209
+
210
+ **Nothing pinned it**, and the reason is worth knowing if you write tests here:
211
+ the existing concurrency test builds a fresh context per call, so each spawn got
212
+ its own tracker — it measures width, not budget. The sequential tests pass
213
+ because a refund makes the arithmetic close. The regression test holds its
214
+ children open, because a settled child refunds and the refund restores a
215
+ plausible number; a test that measures after settle sees a healthy total and
216
+ reports nothing.
217
+
218
+ ## 8.0.0
219
+
220
+ ### Major Changes
221
+
222
+ - 9ac8dd4: A delegate's output is framed as untrusted material on every path the model reads it, and it can no longer end the frame early
223
+
224
+ Blocking `create_task` and `wait_for_task` wrap a worker's text in the
225
+ `<namzu-untrusted>` envelope. Two other paths carried the same bytes and did
226
+ not: the completion notification injected into the transcript, and
227
+ `agent_task_list`'s rendered output. So whether a worker's words arrived as
228
+ material or as the parent's own reasoning depended on how the model happened to
229
+ fetch them — and the two unframed paths are the ones reached when a wait was
230
+ abandoned, which is when a run is already off its expected course.
231
+
232
+ Worse, the notification's own delimiter was forgeable. Measured: worker output
233
+ containing `</task-notification>` produced two closing tags in one message, with
234
+ attacker-controlled text sitting outside the first — reading as ordinary
235
+ transcript rather than as a delegate's material.
236
+
237
+ **What changed on the wire the model sees.**
238
+
239
+ - The notification now nests a `<namzu-untrusted kind="agent-result">` block
240
+ inside `<task-notification>`. Kernel metadata (`task_id`, `agent`, `state`,
241
+ `duration_ms`) stays OUTSIDE it — framing this kernel's own statements as
242
+ untrusted would tell the model to discount the only part of the message it
243
+ can rely on — and so does the truncation notice, which is an instruction
244
+ about how to fetch the rest.
245
+ - `agent_task_list` wraps each finished task's output the same way, with the
246
+ same `agent` and `task` attributes the blocking path uses.
247
+ - Both delimiters are defanged inside worker text, case-insensitively. The
248
+ replacements (`task_notification`, `namzu_untrusted`) share no substring with
249
+ the tokens they replace — a replacement that still contains the token is found
250
+ again by a second pass or by any looser matcher downstream.
251
+ - A notification is 257 characters longer than before — measured, both for a
252
+ five-character result and for a truncated 4 kB one, so the cost is fixed
253
+ rather than proportional to the output. It grows only with the length of the
254
+ agent id and task id, which appear in the envelope's attributes.
255
+
256
+ `data.result` on both tools is unchanged, so a host reading results
257
+ programmatically is unaffected. If you match on the model-facing text of either
258
+ tool, expect the envelope.
259
+
260
+ - 9ac8dd4: A completion inbox hears only about the tasks its own run launched, and a supervisor releases the gateway it borrowed
261
+
262
+ `TaskGateway.onTaskCompleted` is a broadcast and `TaskHandle` carries no run id,
263
+ so every inbox attached to a gateway was handed every completion on it.
264
+ Measured: two inboxes on one gateway, one run launches a task, and the OTHER
265
+ run drains it — it would have been told "a task you launched has finished", a
266
+ false statement, over another run's worker output. A shared gateway is not an
267
+ abuse of the API: `SupervisorAgentConfig.gateway` takes one, and a host that
268
+ owns a gateway reuses it.
269
+
270
+ Separately, nothing ever called `CompletionInbox.close()`. Three sequential
271
+ `SupervisorAgent` runs against one host gateway left three live subscriptions,
272
+ each still holding its run's handles, and the set only grew.
273
+
274
+ **Breaking, and what to do.**
275
+
276
+ - `CompletionInbox` now ignores a completion for a task it was not told about.
277
+ If you drive `buildCoordinatorTools` there is nothing to do — `create_task`
278
+ declares every launch, blocking and background alike. If you launch tasks
279
+ some other way and expect notifications, call `inbox.launched(taskId)` after
280
+ the launch. `inbox.expect(taskId)` already implies it.
281
+ - `SupervisorAgent` closes the inbox it created when the run ends, including
282
+ when setup throws. An inbox you construct yourself is still yours to close.
283
+ - `close()` now clears what the inbox owned and claimed as well as what it
284
+ queued, so a closed inbox cannot be re-armed through a stale reference.
285
+
286
+ The ordering that would otherwise turn this into lost results is handled in
287
+ two layers. `gateway.createTask` resolves one microtask before its caller can
288
+ say who owns the task, so a worker that finishes inside that window is
289
+ announced first. An unowned announcement is therefore BUFFERED rather than
290
+ dropped, and ownership may be claimed retroactively; the buffer is bounded at
291
+ 32 entries so that on a shared gateway it cannot accumulate every other run's
292
+ worker output, and an eviction is logged at WARN so a dropped completion is
293
+ never inferable only from an absence. Where the buffer could not hold an entry,
294
+ `launched()` also asks `gateway.getTask` — an assumption that a just-settled
295
+ task is still findable, now stated on `TaskGateway.getTask` itself so a host
296
+ that cannot meet it knows it is the one paying.
297
+
298
+ - 9ac8dd4: `create_task` offers `background: true` only when there is somewhere for the result to arrive
299
+
300
+ A background launch returns a task id and tells the model its result will come
301
+ "later, as a task notification". The `CompletionInbox` is the only thing that
302
+ delivers one — it holds the run open for the outstanding worker and puts the
303
+ completion into the transcript. `buildCoordinatorTools` mounted the parameter
304
+ whether or not it was given an inbox, so a host without one had a tool
305
+ advertising a channel that did not exist. Nothing failed loudly, because the
306
+ launch itself succeeded; the result simply never arrived.
307
+
308
+ Without a `completionInbox`, `create_task` no longer declares `background` and
309
+ its description no longer mentions it. Everything else is unchanged: the
310
+ blocking path, `wait_for_task`, `cancel_task` and `agent_task_list` are all
311
+ still mounted. Pass a `completionInbox` — to `buildCoordinatorTools` **and** to
312
+ `drainQuery` — to get background launching back. `SupervisorAgent` does both
313
+ already, so a host using it sees no change.
314
+
315
+ A `background: true` that reaches `execute` some other way — a directly
316
+ constructed definition — is REFUSED, naming the missing piece, rather than
317
+ quietly turned into a blocking call: the caller asked for something that
318
+ returns immediately, and giving them a different thing is accepting work whose
319
+ stated terms cannot be met. The abandoned-wait messages on `create_task` and
320
+ `wait_for_task` no longer promise a notification either, and
321
+ `agent_task_list` stops telling the model to avoid the listing when the
322
+ listing is the only route left.
323
+
324
+ The parameter is withheld rather than refused per call, and rather than thrown
325
+ at construction. A parameter the model is never shown costs nothing; one it is shown and then
326
+ denied costs prompt-prefix tokens plus an iteration per attempt. And a throw
327
+ would break a caller doing something legitimate — an inbox-less coordinator
328
+ surface is a supported configuration. This is the same reasoning that made an
329
+ empty roster withhold `create_task` rather than refuse to build.
330
+
331
+ ### Minor Changes
332
+
333
+ - a39c2ed: A compaction pass now reports both of its outcomes.
334
+
335
+ Two gaps, in opposite directions, in the same function.
336
+
337
+ **A compaction that sheds nothing was invisible to everyone.** All three decline
338
+ paths — the reducer throws, it returns no fewer messages than it was given, or
339
+ its result splits a `tool_use` from its `tool_result` and is refused wholesale —
340
+ reached a log line and stopped there. A host that silences its logger, which
341
+ every command-line entry point does, made a failed compaction invisible to the
342
+ user, to the host _and_ to the model at once. The run then continued at full
343
+ context toward a provider rejection several turns later that named none of this.
344
+ A shed that did not happen is exactly as consequential as one that did, and only
345
+ one of them was on the wire.
346
+
347
+ New `compaction_failed` event (wire: `compaction.failed`) carrying `cause`
348
+ (`reducer_threw` | `shed_nothing` | `split_tool_pair`), the unchanged message
349
+ count, and the reducer's error where there was one. The cause is on the event
350
+ because the three want different responses: one may succeed next pass, one will
351
+ decline identically every time, and one is a reducer bug that `findSafeTrimIndex`
352
+ exists to prevent.
353
+
354
+ **And a compaction that succeeded was invisible on the path most hosts take.**
355
+ `compaction_completed` was emitted only from the structured working-state path.
356
+ The reducer path — taken by any host-supplied `contextReducer` and by
357
+ `strategy: 'sliding-window'` — emitted nothing at all, so the event whose own
358
+ documentation says it exists because "a host could not show the user that context
359
+ was dropped" never reached the hosts most likely to need it. It is emitted from
360
+ both paths now.
361
+
362
+ That second one was found by a test written for the first: asserting that a
363
+ successful compaction does _not_ report a failure is what showed it reported
364
+ nothing.
365
+
366
+ **If you switch exhaustively over `RunEvent`, you need a case for
367
+ `compaction_failed`.** Nothing else changes: no existing event's shape moved, and
368
+ a host that ignores unknown events is unaffected. The A2A bridge deliberately
369
+ does not forward either compaction event — a peer models a task lifecycle and
370
+ cannot act on how this runtime manages its own context.
371
+
372
+ - f6e0594: `token_usage_updated` now carries the current context size and the window it is measured against.
373
+
374
+ A host built a context indicator, and it could not have been right. The event
375
+ carried `usage` — **cumulative run spend**, summed over every turn, monotonically
376
+ increasing and untouched by compaction — and nothing about the size of the
377
+ conversation being sent. So the host divided cumulative spend by a context
378
+ window guessed from a substring of the model name, and rendered the result as an
379
+ unqualified percentage, continuously.
380
+
381
+ Both terms were wrong, and the numerator was the worse of the two. A guessed
382
+ window is wrong by a bounded factor. Cumulative spend has three properties that
383
+ make it not merely imprecise but actively misleading:
384
+
385
+ - It **never decreases**, by explicit design — the accumulator is documented as
386
+ monotone so it can never under-report a bill. Compaction does not reduce it.
387
+ - It grows **superlinearly in turn count**, because every turn re-sends the whole
388
+ history and counts those prompt tokens again. Ten turns over a 50k context
389
+ accumulate roughly 500k.
390
+ - It measures **spend**, which is the right quantity for cost and the wrong one
391
+ for occupancy.
392
+
393
+ So an indicator built on it saturates at full long before the context is, and it
394
+ is **anti-correlated with what it claims in exactly the regime a user cares
395
+ about**: a long conversation reads FULL while the real context may be a fifth of
396
+ the window. That alarms people into compacting or restarting when they have
397
+ room — worse than showing nothing, because silence does not tell you something
398
+ false in red. In the other direction, a driver that reports no usage shows 0%
399
+ for a conversation that is really there.
400
+
401
+ The kernel already computed the right numbers on every iteration and kept them
402
+ to itself. `measureContext()` is now exported, and the event carries four new
403
+ optional fields: `contextTokens`, `contextMeasuredBy` (`'provider' | 'estimate'`),
404
+ `contextWindowTokens` and `windowSource` (`'config' | 'model-table' | 'default'`).
405
+ They are named apart from the cumulative figures beside them deliberately —
406
+ reaching for the wrong one should be a visible mistake, not a plausible guess.
407
+
408
+ **They are absent when the run has no compaction configuration**, because nothing
409
+ then resolves a window and inventing one would be the guess this replaces. A
410
+ surface should show what it can name rather than a fraction it cannot ground.
411
+
412
+ **A fraction is only as honest as the weaker of its terms.** `contextMeasuredBy`
413
+ and `windowSource` exist so a surface can pass that on rather than presenting an
414
+ estimate as a measurement. Nothing existing changes: `usage` and `cost` are
415
+ untouched, and the new fields are additive and optional.
416
+
417
+ - a39c2ed: A verification rule can name one tool and one argument.
418
+
419
+ Every pattern rule an operator could write was one of two wrong things.
420
+
421
+ `custom_pattern` carries no tool scope, so a rule written about `bash` decided
422
+ `edit` calls as well — `target: 'both'` prefixes the tool name to the subject
423
+ rather than requiring it, which is not a scope. And `target: 'args'` tests
424
+ `JSON.stringify(toolInput)`, so the subject is the JSON _text_ of the whole
425
+ argument object: the natural, anchored thing to write, `^git push`, is tested
426
+ against `{"command":"git push origin main"}` and can never match. The rule then
427
+ decides nothing, silently. Pinning the tool cost the anchor; anchoring cost the
428
+ tool scope.
429
+
430
+ New `argument_pattern` rule — `toolNames`, `argument`, `pattern`, `decision` —
431
+ whose subject is the named argument's own value, so an anchored pattern means
432
+ what it looks like it means. The refusal names the argument as well as the
433
+ pattern, which is what tells a model whether a different value could get through.
434
+
435
+ It deliberately decides nothing in three cases: the tool was not called, the
436
+ argument is absent, or the argument holds an object or an array. No string a
437
+ pattern could match says anything true about a structured value, and serialising
438
+ one to try would put this rule back where `custom_pattern` already is. To refuse
439
+ a tool over the _shape_ of its input, deny it by name. Numbers and booleans are
440
+ matched rather than skipped — they render unambiguously, and a rule about a
441
+ numeric argument is a reasonable thing to write.
442
+
443
+ `custom_pattern` is unchanged and not deprecated: matching anywhere in the
444
+ serialised input without caring where is a real use, and it is now documented as
445
+ being that rather than reading as something it never was. The trap was the name,
446
+ not the behaviour.
447
+
448
+ - 9ac8dd4: A run that ends any way other than a plain final answer no longer throws away a finished worker's output
449
+
450
+ The iteration loop consulted its completion inbox at exactly one place: the
451
+ branch where the model stops calling tools and answers. It leaves by eight other
452
+ routes, and three of them are ordinary ways for a run to END — a tool the author
453
+ marked `terminal`, a captured `structured_output`, and the host's `stopWhen`.
454
+ A background or abandoned worker that finished while any of those was deciding
455
+ had its result dropped: the gateway held it, the run closed, nothing read it.
456
+ Measured before the fix — terminal-tool exit and `stopWhen` exit both delivered
457
+ nothing; the final-answer exit delivered in 44 ms.
458
+
459
+ Delivery now happens in a `finally` around the loop, so it does not depend on
460
+ each exit remembering — including the two `return`s and a generator abandoned by
461
+ its consumer, which no post-loop statement reaches.
462
+
463
+ **What you may observe.** On those exits `Run.messages` can now end with a
464
+ `task-notification` user message after the assistant's last message. The answer
465
+ is on `Run.result`, as before. If you were reading the answer off the last
466
+ element of `Run.messages`, that assumption was already unsafe whenever a
467
+ notification landed mid-run; it is now unsafe in three more places.
468
+
469
+ **Which exits wait, and which only deliver.** A hold buys the model a turn in
470
+ which to use a result, so it is only worth paying where a turn can still
471
+ happen. A terminal tool and a captured `structured_output` have decided the
472
+ answer, so those deliver what arrived and stop. `stopWhen` is a programmable
473
+ halt that says nothing about whether the answer is complete, so it now HOLDS
474
+ like the ordinary final-answer exit — a precedence rule chosen here, not
475
+ something `stopWhen` implies — and costs exactly one extra turn, after which
476
+ the predicate fires again with nothing pending.
477
+
478
+ The stop reason survives that extra turn. `stopWhen` is consulted only after a
479
+ tool batch, so when the extra turn is prose the predicate is never asked again
480
+ and the run leaves by the ordinary route — which would have reported
481
+ `stopReason: 'end_turn'`, naming the shape of the last message rather than the
482
+ host's decision. A run that ends because a host said stop now reports
483
+ `'stop_condition'` whether or not a delegated result delayed it by a turn. If
484
+ the extra turn instead runs more tools, the predicate is asked again and
485
+ answers for itself.
486
+
487
+ A run that ends with a worker still running now says so on
488
+ `Run.abandonedTaskIds` rather than leaving the impression the result arrived.
489
+
490
+ - 9ac8dd4: A run that ends over a still-running worker says so, and the untrusted envelope's own label can no longer close it
491
+
492
+ Three things an adversarial review of the completion path found.
493
+
494
+ **`Run.abandonedTaskIds`.** A run can settle while a worker it launched is
495
+ still going — the model answered, a terminal tool decided the result, a
496
+ `stopWhen` fired. Until now nothing said so, which left the impression the
497
+ worker's result had been delivered. The run now names those task ids.
498
+
499
+ They are **named, not cancelled**, and that is the decision: giving up on a
500
+ wait is a statement about the waiter, not about the work — the rule this
501
+ subsystem already applies to `wait_for_task` — and "the parent answered early"
502
+ is a weaker warrant for killing a child than "the clock ran out", not a
503
+ stronger one. A worker mid-write is not the kernel's to judge. A host that
504
+ wants the work stopped has `cancel_task` and the run's abort controller, and
505
+ now has the ids to use them on.
506
+
507
+ **`wrapUntrusted` neutralises its own delimiter inside `provenance`.** The body
508
+ was defanged and the attributes escaped; the provenance line was interpolated
509
+ raw, and every caller in the SDK builds it from a value it did not author — an
510
+ agent id from a roster, a server name from a connector manifest. A provenance
511
+ carrying `</namzu-untrusted>` ended the block before the content it was
512
+ introducing. This affects the blocking `create_task`, `wait_for_task` and the
513
+ `Agent` tool as well as the two paths framed in this release.
514
+
515
+ **`background: true` with no inbox is refused, not silently made blocking**,
516
+ and the sentences match. The abandoned-wait messages on `create_task` and
517
+ `wait_for_task` promised "its result will arrive separately as a task
518
+ notification" unconditionally — false with no inbox, and a model told to expect
519
+ a message waits for it. They now say where the result actually is. The
520
+ `agent_task_list` description likewise stops telling the model not to use the
521
+ listing when, without an inbox, the listing is the only route left to an
522
+ abandoned launch's output.
523
+
524
+ `CompletionInbox` gains `outstandingTaskIds`, which reads the ids and cancels
525
+ nothing.
526
+
527
+ - 9ac8dd4: A run holding for a background worker waits a share of its own budget, not a fixed two minutes
528
+
529
+ `BACKGROUND_TASK_GRACE_MS = 120_000` was unrelated to the run it bounded, and
530
+ wrong in both directions at once. Measured: a run configured `timeoutMs: 20_000`
531
+ was held open for **120,267 ms** — six times its own budget — because the hold
532
+ sits inside an iteration and the run guard only checks between them, so nothing
533
+ could interrupt it. In the other direction, on a run with hours left the same
534
+ two minutes abandoned delegated workers observed at 4m21s, 5m58s and 8m04s, all
535
+ comfortably inside the hour `DELEGATION_TIMEOUT_MS` already declares.
536
+
537
+ The hold is now `min(remainingBeforeFinalize × 0.5, DELEGATION_TIMEOUT_MS)`,
538
+ where `remainingBeforeFinalize` is the time left before the run guard stops
539
+ asking for more work and asks for a closing summary (90% of `timeoutMs`), less
540
+ what the run has spent — carried across a resume, so a checkpointed run sizes
541
+ the hold from what is left of the RUN rather than of the process now hosting
542
+ it, and read when the wait starts rather than at the top of the iteration.
543
+
544
+ - **Half, not all.** The hold exists to put a worker's result where the model
545
+ can read it, and reading it costs a turn. Spending everything remaining would
546
+ deliver a notification into a run with no turn left to act on it — the same
547
+ failure the mechanism exists to prevent.
548
+ - **Bounded against the boundary that binds.** Measuring to the DEADLINE was
549
+ the first attempt and it looked safe: a hold cannot outlive the deadline
550
+ either way. But half of the time-to-deadline, started just under the warning
551
+ threshold, ends at 95% of the budget — so the slice the guard keeps for the
552
+ run to produce a closing answer is half spent waiting for the result that
553
+ answer was supposed to use. Against the finalize point the hold cannot reach
554
+ that reserve at all, which is what makes the guard's inability to interrupt
555
+ a hold a non-issue rather than a smaller issue.
556
+ - **A floor of zero, deliberately.** A run with no time left before it must
557
+ start finishing has no turn in which to read a notification. Nothing is
558
+ dropped by it: the wait returns before it looks at its timer when a
559
+ completion is already in hand.
560
+
561
+ **What changes for you.** A run with a short `timeoutMs` finishes when it said
562
+ it would instead of overrunning by minutes. A run with a long one keeps its
563
+ worker instead of abandoning it. If you were relying on a fixed two-minute
564
+ settle regardless of run configuration, set `timeoutMs` to about four and a half minutes to
565
+ get the same hold.
566
+
567
+ - 585a592: A caller can ask which effort levels a model accepts.
568
+
569
+ The answer existed, was modelled carefully, and was reachable only from inside
570
+ one driver. That matters because effort is **refused, not clamped**: a level a
571
+ model does not have makes the vendor reject the request, so a control offering
572
+ the wrong one produces a run that fails at the start rather than a quieter one.
573
+
574
+ Every option open to a caller without the answer was bad. Offering all five
575
+ breaks some models. Offering the intersection hides `xhigh` and `max` from every
576
+ model that has them, which is most of the reason to build such a control. And
577
+ copying the table looks fine and is worst: the ceiling has moved twice already,
578
+ so a copy goes stale on the next model and goes stale **silently**, surfacing as
579
+ a vendor rejection rather than a failing build.
580
+
581
+ **New optional `LLMProvider.effortLevelsFor(model, thinking?)`.** Three states,
582
+ each meaning something different: the method absent means the driver has no
583
+ effort concept at all and setting one will be refused; an empty array means the
584
+ driver implements effort and this model has none; a non-empty array is the set
585
+ to offer.
586
+
587
+ **`thinking` is a parameter, and that is the point.** At least one model family
588
+ accepts a narrower set while thinking is disabled than while it is on — so an
589
+ API returning two sibling arrays invites a caller to render a picker from one
590
+ and send the other, a combination the vendor rejects, on exactly one family.
591
+ Passing the configuration you will actually send makes that unspellable: there
592
+ is one answer and it is the one for your request.
593
+
594
+ The driver's implementation shares the same two resolution steps the request
595
+ path uses, so a caller's picker and the request it produces cannot disagree.
596
+
597
+ `@namzu/anthropic` also now exports `resolveThinkingCapability`,
598
+ `resolveThinkingBody`, `resolveEffort` and their types, for a caller that needs
599
+ the fuller picture — whether thinking can be switched off at all, not only which
600
+ effort levels apply. Prefer `effortLevelsFor` where it suffices: it is
601
+ provider-agnostic and cannot return the wrong one of the two sets.
602
+
603
+ Separately, the live wire-contract suite now retries a transient status rather
604
+ than reporting it as a contract failure. A 529 says the service is busy and
605
+ answers nothing about whether a schema is expressible — so a test named "every
606
+ shipped tool is expressible on this wire" was claiming something the run had not
607
+ established. That cost two manual re-runs in one day to discover the wire had no
608
+ opinion.
609
+
610
+ ### Patch Changes
611
+
612
+ - 9ac8dd4: A background task whose completion arrived early no longer holds the run open forever
613
+
614
+ `CompletionInbox.drain()` handed the completion over and marked it claimed, but
615
+ left the task on the OUTSTANDING set. That set is meant to hold ids that are
616
+ still running, and only the gateway's completion listener takes an id off it —
617
+ so if the listener ran BEFORE the launching call said `expect()`, the id was
618
+ added to a set nothing would ever clear.
619
+
620
+ That order is reachable rather than theoretical: `expect()` runs one microtask
621
+ after `gateway.createTask()` resolves, and a worker that finishes fast is
622
+ announced in between. The result of it was `hasPendingWork === true` for the
623
+ rest of the run, with an empty inbox — so every attempt to settle waited out the
624
+ full background grace period for a result that was already in the transcript,
625
+ and did it again on the next turn, and the next.
626
+
627
+ Nothing to do on upgrade. If you were seeing runs pause for two minutes before
628
+ their final answer with no background work outstanding, this was why.
629
+
630
+ - 3d4315e: `PrepareStepResult.activeTools` documented the opposite of what it does.
631
+
632
+ Its comment promised that unregistered names are dropped so a phase list
633
+ outliving a tool rename would "narrow the surface, not kill the agent mid-run".
634
+ Since the list began bounding what may RUN rather than only what the model is
635
+ shown, dropping every name leaves the step able to call nothing — so the code
636
+ and its own documentation had said different things.
637
+
638
+ **The behaviour is right and the comment was wrong.** This list means "only
639
+ these": when a rename outlives it, the only set satisfying "only the tools that
640
+ no longer exist" is the empty one. Widening back to the run's list would grant
641
+ precisely the tools the caller asked to exclude, on the grounds that their own
642
+ list failed — a control that stops applying because it was aged out, which is
643
+ worse than a step that answers from what it already has. The run continues
644
+ either way; nothing crashes.
645
+
646
+ The warning now distinguishes the two cases, because they have different
647
+ consequences: some names dropped narrows the step, and all of them dropped
648
+ leaves it unable to call anything. "Ignoring them" was accurate for the first
649
+ and misleading for the second.
650
+
651
+ **Worth knowing if you rely on this:** the warning goes to the logger, so a host
652
+ that silences its logger sees a phase quietly stop doing anything. That is a real
653
+ gap and it is named here rather than papered over.
654
+
3
655
  ## 7.0.0
4
656
 
5
657
  ### Major Changes
@@ -1 +1 @@
1
- {"version":3,"file":"SupervisorAgent.d.ts","sourceRoot":"","sources":["../../src/agents/SupervisorAgent.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACxE,OAAO,KAAK,EACX,UAAU,EACV,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,KAAK,EAAW,KAAK,EAAU,MAAM,uBAAuB,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACpC,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,KAAK,EAAE,KAAK,EACZ,GAAG,GAAE,MAAmB,GACtB,eAAe,EAAE,CAcnB;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAEnF;AAED,qBAAa,eAAgB,SAAQ,aAAa,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IAC/F,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAS;gBAEzB,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,CAAC;IAalE;;;;;;;;OAQG;IACG,GAAG,CACR,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,qBAAqB,CAAC;YAMnB,YAAY;CAwQ1B"}
1
+ {"version":3,"file":"SupervisorAgent.d.ts","sourceRoot":"","sources":["../../src/agents/SupervisorAgent.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACxE,OAAO,KAAK,EACX,UAAU,EACV,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,KAAK,EAAW,KAAK,EAAU,MAAM,uBAAuB,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACpC,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,KAAK,EAAE,KAAK,EACZ,GAAG,GAAE,MAAmB,GACtB,eAAe,EAAE,CAcnB;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAEnF;AAED,qBAAa,eAAgB,SAAQ,aAAa,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IAC/F,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAS;gBAEzB,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,CAAC;IAalE;;;;;;;;OAQG;IACG,GAAG,CACR,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,qBAAqB,CAAC;YAMnB,YAAY;CA8R1B"}