@namzu/sdk 61.3.0 → 62.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 (307) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +2 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/bridge/acp/permission.d.ts +8 -0
  6. package/dist/bridge/acp/permission.d.ts.map +1 -1
  7. package/dist/bridge/acp/permission.js.map +1 -1
  8. package/dist/bridge/acp/server.d.ts.map +1 -1
  9. package/dist/bridge/acp/server.js +8 -1
  10. package/dist/bridge/acp/server.js.map +1 -1
  11. package/dist/constants/tools/index.d.ts +2 -0
  12. package/dist/constants/tools/index.d.ts.map +1 -1
  13. package/dist/constants/tools/index.js +23 -0
  14. package/dist/constants/tools/index.js.map +1 -1
  15. package/dist/eval/harness-protection.js +1 -1
  16. package/dist/eval/harness-protection.js.map +1 -1
  17. package/dist/learning/cycle.d.ts +5 -3
  18. package/dist/learning/cycle.d.ts.map +1 -1
  19. package/dist/learning/cycle.js +11 -13
  20. package/dist/learning/cycle.js.map +1 -1
  21. package/dist/learning/guidance.d.ts +74 -0
  22. package/dist/learning/guidance.d.ts.map +1 -0
  23. package/dist/learning/guidance.js +259 -0
  24. package/dist/learning/guidance.js.map +1 -0
  25. package/dist/learning/skill.d.ts +2 -2
  26. package/dist/learning/skill.d.ts.map +1 -1
  27. package/dist/learning/skill.js +6 -6
  28. package/dist/learning/skill.js.map +1 -1
  29. package/dist/learning/store.d.ts.map +1 -1
  30. package/dist/learning/store.js +6 -6
  31. package/dist/learning/store.js.map +1 -1
  32. package/dist/pals/communication/ingress-schema.d.ts +8 -8
  33. package/dist/pals/communication/schema.d.ts +2 -2
  34. package/dist/peers/client.d.ts +2 -3
  35. package/dist/peers/client.d.ts.map +1 -1
  36. package/dist/peers/client.js +2 -3
  37. package/dist/peers/client.js.map +1 -1
  38. package/dist/peers/endpoint.d.ts +1 -2
  39. package/dist/peers/endpoint.d.ts.map +1 -1
  40. package/dist/peers/endpoint.js +1 -2
  41. package/dist/peers/endpoint.js.map +1 -1
  42. package/dist/peers/protocol.d.ts.map +1 -1
  43. package/dist/peers/protocol.js +1 -0
  44. package/dist/peers/protocol.js.map +1 -1
  45. package/dist/prompt/coding-agent-doctrine.d.ts +4 -4
  46. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  47. package/dist/prompt/coding-agent-doctrine.js +5 -3
  48. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  49. package/dist/prompt/index.d.ts +0 -4
  50. package/dist/prompt/index.d.ts.map +1 -1
  51. package/dist/prompt/index.js +0 -2
  52. package/dist/prompt/index.js.map +1 -1
  53. package/dist/provider/fallback.d.ts.map +1 -1
  54. package/dist/provider/fallback.js +3 -0
  55. package/dist/provider/fallback.js.map +1 -1
  56. package/dist/provider/idle-timeout.d.ts.map +1 -1
  57. package/dist/provider/idle-timeout.js +5 -0
  58. package/dist/provider/idle-timeout.js.map +1 -1
  59. package/dist/provider/model-window-suffix.d.ts.map +1 -1
  60. package/dist/provider/model-window-suffix.js +1 -0
  61. package/dist/provider/model-window-suffix.js.map +1 -1
  62. package/dist/provider/retry.d.ts.map +1 -1
  63. package/dist/provider/retry.js +5 -0
  64. package/dist/provider/retry.js.map +1 -1
  65. package/dist/provider/step-stopped.d.ts +19 -0
  66. package/dist/provider/step-stopped.d.ts.map +1 -0
  67. package/dist/provider/step-stopped.js +25 -0
  68. package/dist/provider/step-stopped.js.map +1 -0
  69. package/dist/provider/token-budget.d.ts.map +1 -1
  70. package/dist/provider/token-budget.js +6 -1
  71. package/dist/provider/token-budget.js.map +1 -1
  72. package/dist/public-runtime.d.ts +4 -15
  73. package/dist/public-runtime.d.ts.map +1 -1
  74. package/dist/public-runtime.js +3 -14
  75. package/dist/public-runtime.js.map +1 -1
  76. package/dist/public-tools.d.ts +0 -2
  77. package/dist/public-tools.d.ts.map +1 -1
  78. package/dist/public-tools.js +0 -2
  79. package/dist/public-tools.js.map +1 -1
  80. package/dist/public-types.d.ts +3 -21
  81. package/dist/public-types.d.ts.map +1 -1
  82. package/dist/runtime/query/iteration/index.d.ts +17 -0
  83. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  84. package/dist/runtime/query/iteration/index.js +168 -5
  85. package/dist/runtime/query/iteration/index.js.map +1 -1
  86. package/dist/runtime/query/iteration/phases/per-call-review.d.ts.map +1 -1
  87. package/dist/runtime/query/iteration/phases/per-call-review.js +4 -5
  88. package/dist/runtime/query/iteration/phases/per-call-review.js.map +1 -1
  89. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  90. package/dist/runtime/query/iteration/phases/tool-review.js +6 -3
  91. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  92. package/dist/runtime/query/iteration/stream-turn.d.ts +9 -1
  93. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  94. package/dist/runtime/query/iteration/stream-turn.js +48 -2
  95. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  96. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  97. package/dist/runtime/query/resume-pending.js +7 -4
  98. package/dist/runtime/query/resume-pending.js.map +1 -1
  99. package/dist/runtime/query/review-policy.d.ts +40 -8
  100. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  101. package/dist/runtime/query/review-policy.js +139 -24
  102. package/dist/runtime/query/review-policy.js.map +1 -1
  103. package/dist/runtime/query/turn/completion-audit.d.ts +32 -0
  104. package/dist/runtime/query/turn/completion-audit.d.ts.map +1 -0
  105. package/dist/runtime/query/turn/completion-audit.js +56 -0
  106. package/dist/runtime/query/turn/completion-audit.js.map +1 -0
  107. package/dist/runtime/query/turn/stall-monitor.d.ts +78 -0
  108. package/dist/runtime/query/turn/stall-monitor.d.ts.map +1 -0
  109. package/dist/runtime/query/turn/stall-monitor.js +101 -0
  110. package/dist/runtime/query/turn/stall-monitor.js.map +1 -0
  111. package/dist/session/paths.d.ts +1 -2
  112. package/dist/session/paths.d.ts.map +1 -1
  113. package/dist/session/paths.js +1 -4
  114. package/dist/session/paths.js.map +1 -1
  115. package/dist/tools/builtins/bash.d.ts +2 -1
  116. package/dist/tools/builtins/bash.d.ts.map +1 -1
  117. package/dist/tools/builtins/bash.js +5 -2
  118. package/dist/tools/builtins/bash.js.map +1 -1
  119. package/dist/tools/builtins/computer-use.d.ts +80 -0
  120. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  121. package/dist/tools/builtins/computer-use.js +172 -9
  122. package/dist/tools/builtins/computer-use.js.map +1 -1
  123. package/dist/tools/builtins/content-fingerprint.d.ts +13 -0
  124. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  125. package/dist/tools/builtins/content-fingerprint.js +25 -0
  126. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  127. package/dist/tools/builtins/edit.d.ts.map +1 -1
  128. package/dist/tools/builtins/edit.js +7 -2
  129. package/dist/tools/builtins/edit.js.map +1 -1
  130. package/dist/tools/builtins/shell-read-credit.d.ts +15 -0
  131. package/dist/tools/builtins/shell-read-credit.d.ts.map +1 -0
  132. package/dist/tools/builtins/shell-read-credit.js +161 -0
  133. package/dist/tools/builtins/shell-read-credit.js.map +1 -0
  134. package/dist/tools/paths.d.ts +15 -2
  135. package/dist/tools/paths.d.ts.map +1 -1
  136. package/dist/tools/paths.js +17 -2
  137. package/dist/tools/paths.js.map +1 -1
  138. package/dist/turn/evidence-recall.d.ts +1 -1
  139. package/dist/turn/evidence-recall.d.ts.map +1 -1
  140. package/dist/turn/evidence-recall.js +5 -14
  141. package/dist/turn/evidence-recall.js.map +1 -1
  142. package/dist/types/acp/index.d.ts +6 -0
  143. package/dist/types/acp/index.d.ts.map +1 -1
  144. package/dist/types/agent/base.d.ts +4 -0
  145. package/dist/types/agent/base.d.ts.map +1 -1
  146. package/dist/types/hitl/index.d.ts +8 -0
  147. package/dist/types/hitl/index.d.ts.map +1 -1
  148. package/dist/types/hitl/index.js.map +1 -1
  149. package/dist/types/ids/index.d.ts +1 -1
  150. package/dist/types/message/index.d.ts +1 -1
  151. package/dist/types/message/index.d.ts.map +1 -1
  152. package/dist/types/message/index.js +1 -0
  153. package/dist/types/message/index.js.map +1 -1
  154. package/dist/types/provider/interface.d.ts +12 -0
  155. package/dist/types/provider/interface.d.ts.map +1 -1
  156. package/dist/types/session/config.d.ts +25 -0
  157. package/dist/types/session/config.d.ts.map +1 -1
  158. package/dist/types/session/records.d.ts.map +1 -1
  159. package/dist/types/session/records.js +2 -0
  160. package/dist/types/session/records.js.map +1 -1
  161. package/dist/types/session/turn.d.ts +1 -1
  162. package/dist/types/session/turn.d.ts.map +1 -1
  163. package/dist/types/session/turn.js.map +1 -1
  164. package/package.json +1 -1
  165. package/src/agents/QueryAgent.ts +2 -0
  166. package/src/bridge/acp/permission.ts +8 -0
  167. package/src/bridge/acp/server.ts +7 -1
  168. package/src/constants/tools/index.ts +27 -0
  169. package/src/eval/harness-protection.ts +1 -1
  170. package/src/learning/cycle.ts +15 -16
  171. package/src/learning/guidance.ts +339 -0
  172. package/src/learning/skill.ts +7 -7
  173. package/src/learning/store.ts +5 -6
  174. package/src/peers/client.ts +2 -3
  175. package/src/peers/endpoint.ts +1 -2
  176. package/src/peers/protocol.ts +1 -0
  177. package/src/prompt/coding-agent-doctrine.ts +5 -3
  178. package/src/prompt/index.ts +0 -4
  179. package/src/provider/fallback.ts +7 -0
  180. package/src/provider/idle-timeout.ts +8 -0
  181. package/src/provider/model-window-suffix.ts +1 -0
  182. package/src/provider/retry.ts +8 -0
  183. package/src/provider/step-stopped.ts +27 -0
  184. package/src/provider/token-budget.ts +6 -1
  185. package/src/public-runtime.ts +10 -28
  186. package/src/public-tools.ts +0 -3
  187. package/src/public-types.ts +5 -137
  188. package/src/runtime/query/iteration/index.ts +207 -5
  189. package/src/runtime/query/iteration/phases/per-call-review.ts +7 -5
  190. package/src/runtime/query/iteration/phases/tool-review.ts +5 -3
  191. package/src/runtime/query/iteration/stream-turn.ts +51 -1
  192. package/src/runtime/query/resume-pending.ts +11 -4
  193. package/src/runtime/query/review-policy.ts +157 -24
  194. package/src/runtime/query/turn/completion-audit.ts +71 -0
  195. package/src/runtime/query/turn/stall-monitor.ts +158 -0
  196. package/src/session/paths.ts +2 -6
  197. package/src/tools/builtins/bash.ts +6 -2
  198. package/src/tools/builtins/computer-use.ts +245 -7
  199. package/src/tools/builtins/content-fingerprint.ts +25 -0
  200. package/src/tools/builtins/edit.ts +11 -2
  201. package/src/tools/builtins/shell-read-credit.ts +159 -0
  202. package/src/tools/paths.ts +18 -2
  203. package/src/turn/evidence-recall.ts +7 -15
  204. package/src/types/acp/index.ts +6 -0
  205. package/src/types/agent/base.ts +4 -0
  206. package/src/types/hitl/index.ts +8 -0
  207. package/src/types/ids/index.ts +1 -1
  208. package/src/types/message/index.ts +1 -0
  209. package/src/types/provider/interface.ts +13 -0
  210. package/src/types/session/config.ts +27 -0
  211. package/src/types/session/records.ts +2 -0
  212. package/src/types/session/turn.ts +2 -1
  213. package/dist/learning/deprecated.d.ts +0 -86
  214. package/dist/learning/deprecated.d.ts.map +0 -1
  215. package/dist/learning/deprecated.js +0 -22
  216. package/dist/learning/deprecated.js.map +0 -1
  217. package/dist/manager/resident/activity.d.ts +0 -74
  218. package/dist/manager/resident/activity.d.ts.map +0 -1
  219. package/dist/manager/resident/activity.js +0 -125
  220. package/dist/manager/resident/activity.js.map +0 -1
  221. package/dist/manager/resident/agenda.d.ts +0 -167
  222. package/dist/manager/resident/agenda.d.ts.map +0 -1
  223. package/dist/manager/resident/agenda.js +0 -588
  224. package/dist/manager/resident/agenda.js.map +0 -1
  225. package/dist/manager/resident/consumption.d.ts +0 -98
  226. package/dist/manager/resident/consumption.d.ts.map +0 -1
  227. package/dist/manager/resident/consumption.js +0 -237
  228. package/dist/manager/resident/consumption.js.map +0 -1
  229. package/dist/manager/resident/delivery-window.d.ts +0 -28
  230. package/dist/manager/resident/delivery-window.d.ts.map +0 -1
  231. package/dist/manager/resident/delivery-window.js +0 -80
  232. package/dist/manager/resident/delivery-window.js.map +0 -1
  233. package/dist/manager/resident/evidence-recall.d.ts +0 -30
  234. package/dist/manager/resident/evidence-recall.d.ts.map +0 -1
  235. package/dist/manager/resident/evidence-recall.js +0 -297
  236. package/dist/manager/resident/evidence-recall.js.map +0 -1
  237. package/dist/manager/resident/history-disk.d.ts +0 -10
  238. package/dist/manager/resident/history-disk.d.ts.map +0 -1
  239. package/dist/manager/resident/history-disk.js +0 -50
  240. package/dist/manager/resident/history-disk.js.map +0 -1
  241. package/dist/manager/resident/history.d.ts +0 -115
  242. package/dist/manager/resident/history.d.ts.map +0 -1
  243. package/dist/manager/resident/history.js +0 -203
  244. package/dist/manager/resident/history.js.map +0 -1
  245. package/dist/manager/resident/host.d.ts +0 -112
  246. package/dist/manager/resident/host.d.ts.map +0 -1
  247. package/dist/manager/resident/host.js +0 -254
  248. package/dist/manager/resident/host.js.map +0 -1
  249. package/dist/manager/resident/initiative.d.ts +0 -86
  250. package/dist/manager/resident/initiative.d.ts.map +0 -1
  251. package/dist/manager/resident/initiative.js +0 -131
  252. package/dist/manager/resident/initiative.js.map +0 -1
  253. package/dist/manager/resident/loop.d.ts +0 -49
  254. package/dist/manager/resident/loop.d.ts.map +0 -1
  255. package/dist/manager/resident/loop.js +0 -75
  256. package/dist/manager/resident/loop.js.map +0 -1
  257. package/dist/manager/resident/outbox.d.ts +0 -124
  258. package/dist/manager/resident/outbox.d.ts.map +0 -1
  259. package/dist/manager/resident/outbox.js +0 -216
  260. package/dist/manager/resident/outbox.js.map +0 -1
  261. package/dist/manager/resident/proposal.d.ts +0 -65
  262. package/dist/manager/resident/proposal.d.ts.map +0 -1
  263. package/dist/manager/resident/proposal.js +0 -77
  264. package/dist/manager/resident/proposal.js.map +0 -1
  265. package/dist/manager/resident/store.d.ts +0 -108
  266. package/dist/manager/resident/store.d.ts.map +0 -1
  267. package/dist/manager/resident/store.js +0 -205
  268. package/dist/manager/resident/store.js.map +0 -1
  269. package/dist/manager/resident/tool-evidence.d.ts +0 -112
  270. package/dist/manager/resident/tool-evidence.d.ts.map +0 -1
  271. package/dist/manager/resident/tool-evidence.js +0 -289
  272. package/dist/manager/resident/tool-evidence.js.map +0 -1
  273. package/dist/prompt/resident-learning.d.ts +0 -29
  274. package/dist/prompt/resident-learning.d.ts.map +0 -1
  275. package/dist/prompt/resident-learning.js +0 -126
  276. package/dist/prompt/resident-learning.js.map +0 -1
  277. package/dist/prompt/resident-step.d.ts +0 -42
  278. package/dist/prompt/resident-step.d.ts.map +0 -1
  279. package/dist/prompt/resident-step.js +0 -137
  280. package/dist/prompt/resident-step.js.map +0 -1
  281. package/dist/tools/resident-history.d.ts +0 -12
  282. package/dist/tools/resident-history.d.ts.map +0 -1
  283. package/dist/tools/resident-history.js +0 -82
  284. package/dist/tools/resident-history.js.map +0 -1
  285. package/dist/tools/resident-tool-evidence.d.ts +0 -9
  286. package/dist/tools/resident-tool-evidence.d.ts.map +0 -1
  287. package/dist/tools/resident-tool-evidence.js +0 -61
  288. package/dist/tools/resident-tool-evidence.js.map +0 -1
  289. package/src/learning/deprecated.ts +0 -124
  290. package/src/manager/resident/activity.ts +0 -211
  291. package/src/manager/resident/agenda.ts +0 -937
  292. package/src/manager/resident/consumption.ts +0 -335
  293. package/src/manager/resident/delivery-window.ts +0 -99
  294. package/src/manager/resident/evidence-recall.ts +0 -369
  295. package/src/manager/resident/history-disk.ts +0 -63
  296. package/src/manager/resident/history.ts +0 -348
  297. package/src/manager/resident/host.ts +0 -390
  298. package/src/manager/resident/initiative.ts +0 -231
  299. package/src/manager/resident/loop.ts +0 -119
  300. package/src/manager/resident/outbox.ts +0 -341
  301. package/src/manager/resident/proposal.ts +0 -119
  302. package/src/manager/resident/store.ts +0 -279
  303. package/src/manager/resident/tool-evidence.ts +0 -455
  304. package/src/prompt/resident-learning.ts +0 -152
  305. package/src/prompt/resident-step.ts +0 -182
  306. package/src/tools/resident-history.ts +0 -88
  307. package/src/tools/resident-tool-evidence.ts +0 -72
@@ -384,6 +384,14 @@ export function withProviderRetry(
384
384
  supportsImageInputFor: (model: string) => provider.supportsImageInputFor?.(model) ?? true,
385
385
  }
386
386
  : {}),
387
+ ...(provider.acceptsForcedToolChoiceFor
388
+ ? {
389
+ acceptsForcedToolChoiceFor: (
390
+ model: string,
391
+ thinking?: Parameters<NonNullable<LLMProvider['acceptsForcedToolChoiceFor']>>[1],
392
+ ) => provider.acceptsForcedToolChoiceFor?.(model, thinking) ?? true,
393
+ }
394
+ : {}),
387
395
  ...(provider.reasoningEffortLevelsFor
388
396
  ? {
389
397
  reasoningEffortLevelsFor: (
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The reason a step is stopped on purpose, by the loop that is reading it.
3
+ *
4
+ * A step that has spent its whole reasoning allowance is cut short: the loop
5
+ * stops reading the stream and aborts the request it opened. That abort is the
6
+ * loop's own decision, not a caller's stop and not a transport failure, so it
7
+ * carries this class as its reason. A provider wrapper that sees an aborted
8
+ * request can tell the two apart with {@link isStepStopped} and must not retry
9
+ * it, fail over from it, or report it as an error.
10
+ */
11
+ export class StepStopped extends Error {
12
+ override readonly name = 'StepStopped'
13
+
14
+ constructor() {
15
+ super('the step was stopped by the loop that was reading it')
16
+ }
17
+ }
18
+
19
+ /** Whether an abort reason, or an error that carries one, is a deliberate step stop. */
20
+ export function isStepStopped(value: unknown): value is StepStopped {
21
+ return value instanceof StepStopped
22
+ }
23
+
24
+ /** Whether a request signal was aborted by a deliberate step stop. */
25
+ export function stepWasStopped(signal: AbortSignal | undefined): boolean {
26
+ return signal?.aborted === true && isStepStopped(signal.reason)
27
+ }
@@ -6,6 +6,7 @@ import { ProviderError, classifyProviderError } from '../types/provider/errors.j
6
6
  import type { LLMProvider, StreamChunk } from '../types/provider/index.js'
7
7
  import { cancelCauseOf } from '../types/session/cancel-cause.js'
8
8
  import { isProviderRequestError } from './errors.js'
9
+ import { isStepStopped } from './step-stopped.js'
9
10
 
10
11
  export class TokenBudgetAdmissionError extends ProviderError {
11
12
  constructor() {
@@ -117,6 +118,7 @@ export function withTokenBudget(provider: LLMProvider, budget: SessionTokenBudge
117
118
  }
118
119
  if (Object.keys(chunk.delta).length > 0) outputObserved = true
119
120
  if (typeof chunk.delta.content === 'string') streamedChars += chunk.delta.content.length
121
+ streamedChars += chunk.delta.reasoning?.text?.length ?? 0
120
122
  if (chunk.error) throw new Error(chunk.error)
121
123
  yield chunk
122
124
  }
@@ -161,10 +163,13 @@ export function withTokenBudget(provider: LLMProvider, budget: SessionTokenBudge
161
163
  // because of work somebody chose to stop. The estimate can under-count
162
164
  // what the provider billed for the part it never reported; a failure
163
165
  // that was not a cancellation keeps its marker as before.
166
+ //
167
+ // A step the loop stopped on purpose (it reached its reasoning cap) is
168
+ // closed the same way: nobody chose to lose track of that spend.
164
169
  if (
165
170
  usage === undefined &&
166
171
  signal?.aborted === true &&
167
- cancelCauseOf(signal.reason) === 'user' &&
172
+ (cancelCauseOf(signal.reason) === 'user' || isStepStopped(signal.reason)) &&
168
173
  !uncertainRetry
169
174
  ) {
170
175
  const prompt = estimateMessagesTokens(params.messages)
@@ -1498,6 +1498,7 @@ export {
1498
1498
  isReviewExempt,
1499
1499
  isReviewMode,
1500
1500
  modeReviewsAllowedCalls,
1501
+ mustAskPerson,
1501
1502
  } from './runtime/query/review-policy.js'
1502
1503
  // The model that decides, in the `classify` review mode, what a person would
1503
1504
  // otherwise be asked. See `runtime/query/auto-review/`.
@@ -1529,8 +1530,6 @@ export {
1529
1530
  PromptContributionRegistry,
1530
1531
  SKILLS_CONTRIBUTION_ID,
1531
1532
  codingAgentDoctrineContribution,
1532
- createResidentStepContributions,
1533
- createResidentStepContext,
1534
1533
  skillsContribution,
1535
1534
  } from './prompt/index.js'
1536
1535
 
@@ -1671,6 +1670,10 @@ export type { SingleHandoffDeps } from './session/handoff/single.js'
1671
1670
  export type { InterventionChainLoader } from './session/intervention/prev-artifact.js'
1672
1671
  export type {
1673
1672
  ActionInput,
1673
+ ComputerUseAdvice,
1674
+ ComputerUseAdvisor,
1675
+ ComputerUseAdvisorCandidate,
1676
+ ComputerUseAdvisorRequest,
1674
1677
  ComputerUseTool,
1675
1678
  ComputerUseToolOptions,
1676
1679
  ImageSize,
@@ -1690,23 +1693,13 @@ export {
1690
1693
  diffRequestContext,
1691
1694
  } from './runtime/query/request-context.js'
1692
1695
 
1693
- export {
1694
- DiskResidentStore,
1695
- ResidentConflictError,
1696
- } from './manager/resident/store.js'
1697
- export { runResident, stepResident } from './manager/resident/loop.js'
1698
- export { DiskResidentAgenda } from './manager/resident/agenda.js'
1699
- export { inspectResidentConsumption } from './manager/resident/consumption.js'
1700
- export { ResidentHost } from './manager/resident/host.js'
1701
-
1702
- export { createResidentSelector } from './manager/resident/initiative.js'
1703
- export { validateResidentProposal } from './manager/resident/proposal.js'
1704
-
1705
- export { deliverResidentMessage } from './manager/resident/outbox.js'
1706
- export { createResidentDeliveryWindow } from './manager/resident/delivery-window.js'
1707
-
1708
1696
  export { hashLearningSkill, projectLearning } from './learning/skill.js'
1709
1697
  export { runLearningCycle } from './learning/cycle.js'
1698
+ export {
1699
+ DiskLearningGuidance,
1700
+ LearningGuidanceConflictError,
1701
+ earlierGuidanceDirectory,
1702
+ } from './learning/guidance.js'
1710
1703
  export {
1711
1704
  LEARNING_FLOOR_COMPONENTS,
1712
1705
  LEARNING_SURFACE_KINDS,
@@ -1740,23 +1733,12 @@ export {
1740
1733
  classifyEvidenceSource,
1741
1734
  EVIDENCE_RECORD_GUIDANCE,
1742
1735
  } from './store/evidence/source-kind.js'
1743
- export { createResidentToolEvidenceSource } from './manager/resident/tool-evidence.js'
1744
-
1745
- export { createResidentEvidenceRecallStep } from './manager/resident/evidence-recall.js'
1746
1736
 
1747
1737
  export {
1748
1738
  SqliteLearningStore,
1749
1739
  runStoredLearningCycle,
1750
1740
  runStoredLearningFromObservations,
1751
1741
  } from './learning/store.js'
1752
- export {
1753
- SqliteResidentLearningStore,
1754
- hashResidentSkill,
1755
- projectResidentLearning,
1756
- runResidentLearningCycle,
1757
- runStoredResidentLearningCycle,
1758
- runStoredResidentLearningFromObservations,
1759
- } from './learning/deprecated.js'
1760
1742
 
1761
1743
  // ─── sessions, turns and the session log ─────────────────────────────────
1762
1744
  //
@@ -41,7 +41,6 @@ export type { UntrustedEnvelope, WrapUntrustedOptions } from './tools/untrusted-
41
41
  // ─── Built-in tools ──────────────────────────────────────────────────────
42
42
 
43
43
  export { getBuiltinTools } from './tools/builtins/index.js'
44
- export { buildResidentHistoryTools } from './tools/resident-history.js'
45
44
  // A host compiling operator permissions has a tool name and needs an argument
46
45
  // to attach a pattern to. Without one it can only match against the serialised
47
46
  // input, which is how an `allow` for `git status*` came to approve
@@ -270,7 +269,5 @@ export {
270
269
  seedObservationLedger,
271
270
  } from './runtime/query/file-evidence-seed.js'
272
271
 
273
- export { buildResidentToolEvidenceTools } from './tools/resident-tool-evidence.js'
274
-
275
272
  export { createPalMessagingTools } from './pals/communication/tools.js'
276
273
  export { createPalOperatorMessagingTools } from './pals/communication/operator.js'
@@ -457,6 +457,7 @@ export type {
457
457
  } from './runtime/query/auto-review/index.js'
458
458
 
459
459
  export type {
460
+ MustAskCall,
460
461
  ReviewExemption,
461
462
  ReviewMode,
462
463
  ReviewPolicyOptions,
@@ -471,9 +472,6 @@ export type {
471
472
  PromptContribution,
472
473
  PromptContributionContext,
473
474
  PromptPlacement,
474
- ResidentStepPromptOptions,
475
- ResidentStepContextOptions,
476
- ResidentStepContextBundle,
477
475
  } from './prompt/index.js'
478
476
 
479
477
  export type {
@@ -532,89 +530,6 @@ export type {
532
530
  RequestContextChange,
533
531
  } from './runtime/query/request-context.js'
534
532
 
535
- export type {
536
- ResidentDecision,
537
- ResidentState,
538
- ResidentStore,
539
- } from './manager/resident/store.js'
540
- export type {
541
- ResidentActivityScope,
542
- ResidentAdmission,
543
- ResidentSettlement,
544
- ResidentActivityOptions,
545
- ResidentActivityPage,
546
- ResidentActivitySource,
547
- } from './manager/resident/activity.js'
548
- export type {
549
- ResidentConsumptionReceipt,
550
- ResidentConsumptionResolver,
551
- ResidentConsumptionOptions,
552
- ResidentConsumptionAttempt,
553
- ResidentConsumptionReport,
554
- } from './manager/resident/consumption.js'
555
- export type {
556
- ResidentHistoryScope,
557
- ResidentHistoryAddress,
558
- ResidentHistoryMatch,
559
- ResidentHistorySearchOptions,
560
- ResidentHistorySearchResult,
561
- ResidentHistoryReadOptions,
562
- ResidentHistoryText,
563
- ResidentHistoryReadResult,
564
- ResidentHistorySource,
565
- } from './manager/resident/history.js'
566
- export type {
567
- ResidentStep,
568
- ResidentStepResult,
569
- ResidentLoopOptions,
570
- } from './manager/resident/loop.js'
571
- export type { ResidentExecutionStore } from './manager/resident/store.js'
572
- export type {
573
- ResidentAgendaState,
574
- ResidentAgendaStore,
575
- ResidentPursuit,
576
- } from './manager/resident/agenda.js'
577
- export type {
578
- ResidentHostResult,
579
- ResidentHostRunOptions,
580
- ResidentPursuitStep,
581
- } from './manager/resident/host.js'
582
-
583
- export type {
584
- ResidentObservation,
585
- ResidentFeedback,
586
- ResidentSelectionConfig,
587
- ResidentCandidate,
588
- ResidentSelection,
589
- ResidentSelector,
590
- } from './manager/resident/initiative.js'
591
- export type {
592
- ResidentHostOptions,
593
- ResidentObserver,
594
- } from './manager/resident/host.js'
595
- export type {
596
- ResidentProposal,
597
- ResidentProposalLimits,
598
- ResidentProposalOrigin,
599
- } from './manager/resident/proposal.js'
600
-
601
- export type { ResidentMessageFactory } from './manager/resident/host.js'
602
- export type {
603
- ResidentMessageInput,
604
- ResidentOutboxMessage,
605
- ResidentDeliveryOutcome,
606
- ResidentOutboxStore,
607
- ResidentDeliveryGate,
608
- ResidentMessageTransport,
609
- ResidentDeliveryOptions,
610
- ResidentDeliveryResult,
611
- } from './manager/resident/outbox.js'
612
- export type { ResidentDeliveryWindowConfig } from './manager/resident/delivery-window.js'
613
-
614
- export type {
615
- ResidentStepContext,
616
- ResidentContextualStep,
617
- } from './manager/resident/host.js'
618
533
  export type {
619
534
  LearningEvidence,
620
535
  LearningSkillCandidate,
@@ -639,6 +554,10 @@ export type {
639
554
  LearningCycleOptions,
640
555
  LearningCycleResult,
641
556
  } from './learning/cycle.js'
557
+ export type {
558
+ DiskLearningGuidanceOptions,
559
+ LearningGuidanceState,
560
+ } from './learning/guidance.js'
642
561
 
643
562
  export type {
644
563
  LearningModel,
@@ -669,13 +588,6 @@ export type {
669
588
  LearningLeakSubject,
670
589
  } from './learning/leak-tripwire.js'
671
590
 
672
- export type {
673
- ResidentArchiveRequest,
674
- ResidentArchiveEntry,
675
- ResidentArchivePage,
676
- ResidentArchiveListOptions,
677
- } from './manager/resident/agenda.js'
678
-
679
591
  export type {
680
592
  SessionEvidenceScope,
681
593
  SessionEvidenceSourceOptions,
@@ -692,18 +604,7 @@ export type {
692
604
  SessionTextEvidenceSource,
693
605
  } from './store/evidence/types.js'
694
606
  export type { EvidenceRecordKind } from './store/evidence/source-kind.js'
695
- export type {
696
- ResidentToolEvidenceScope,
697
- ResidentSettledInvocation,
698
- ResidentToolEvidenceOptions,
699
- ResidentToolEvidenceSearchOptions,
700
- ResidentToolEvidenceSearchResult,
701
- ResidentToolEvidenceReadOptions,
702
- ResidentToolEvidenceReadResult,
703
- ResidentToolEvidenceSource,
704
- } from './manager/resident/tool-evidence.js'
705
607
 
706
- export type { ResidentEvidenceRecallOptions } from './manager/resident/evidence-recall.js'
707
608
  export type {
708
609
  JsonClaimValue,
709
610
  JsonClaimRequirement,
@@ -871,36 +772,3 @@ export type {
871
772
  } from './pals/activity/subscription-policy.js'
872
773
  export type { PalActivitySubscriptionRunnerOptions } from './pals/activity/subscriptions.js'
873
774
  export type { PalActivityCausalityOptions } from './pals/activity/causality.js'
874
-
875
- // Deprecated resident-prefixed names, kept until the next major.
876
- export type {
877
- ResidentLearningEvidence,
878
- ResidentSkillCandidate,
879
- ResidentLearnedSkill,
880
- ResidentLearningState,
881
- ResidentProfileUpdate,
882
- ResidentSkillEvaluation,
883
- ResidentLearningProjectionOptions,
884
- ResidentLearningSource,
885
- ResidentLearningProjection,
886
- ResidentLearningStage,
887
- ResidentLearningReceipt,
888
- ResidentLearningConsumption,
889
- ResidentLearningCycleEvent,
890
- ResidentLearningCycleContext,
891
- ResidentLearningGenerationContext,
892
- ResidentLearningExplorationContext,
893
- ResidentLearningExploration,
894
- ResidentLearningEvaluationContext,
895
- ResidentLearningCycleOptions,
896
- ResidentLearningCycleResult,
897
- SqliteResidentLearningStoreOptions,
898
- ResidentLearningArtifact,
899
- ResidentLearningRecordedUsage,
900
- ResidentLearningCycleSummary,
901
- ResidentLearningDiscoveryOptions,
902
- ResidentLearningDiscoveryResult,
903
- ResidentLearningObservation,
904
- ResidentLearningObservationRecord,
905
- ResidentLearningTarget,
906
- } from './learning/deprecated.js'
@@ -42,7 +42,7 @@ import {
42
42
  createToolMessage,
43
43
  } from '../../../types/message/index.js'
44
44
  import { classifyProviderError, readOutputOverflow } from '../../../types/provider/errors.js'
45
- import type { ChatCompletionResponse } from '../../../types/provider/index.js'
45
+ import type { ChatCompletionResponse, ReasoningEffort } from '../../../types/provider/index.js'
46
46
  import type { AnswerReview, AnswerReviewContext } from '../../../types/session/answer-review.js'
47
47
  import type {
48
48
  SessionEvent,
@@ -75,6 +75,15 @@ import {
75
75
  } from '../request-rich-content.js'
76
76
  import { withoutVoidAssistantTurns } from '../request-void-turns.js'
77
77
  import { formatSteeringNote, isOperatorUserMessage } from '../steering.js'
78
+ import { COMPLETION_AUDIT_MESSAGE, CompletionAudit } from '../turn/completion-audit.js'
79
+ import {
80
+ FORCED_STEP_MESSAGE,
81
+ REASONING_CAPPED_MESSAGE,
82
+ REASONING_ONLY_MESSAGE,
83
+ StallMonitor,
84
+ callChangedState,
85
+ reasoningCapChars,
86
+ } from '../turn/stall-monitor.js'
78
87
  import { effortForModel } from './effort-clamp.js'
79
88
  import { parseNativeCandidate } from './native-output.js'
80
89
  import {
@@ -119,6 +128,9 @@ import {
119
128
  } from './step-shaping.js'
120
129
  import { streamProviderTurn } from './stream-turn.js'
121
130
 
131
+ /** Capped steps in a row after which the per-step cap stops applying for the turn's next step. */
132
+ const MAX_CAPPED_STREAK = 3
133
+
122
134
  type ReviewRequest = Pick<AnswerReviewContext, 'requestMessages' | 'latestUserMessage'>
123
135
 
124
136
  /** A host reviewer is not the model transport, even when its cause is an HTTP failure. */
@@ -299,6 +311,16 @@ export class IterationOrchestrator {
299
311
  private stopDeferredForOutstandingWork = false
300
312
  /** Nudges sent since the model last finished a call without hitting its output cap. */
301
313
  private outputContinuations = 0
314
+ /** Watches the turn's steps for reasoning that piles up while nothing changes. */
315
+ private readonly stallMonitor?: StallMonitor
316
+ /** Holds the turn back once when it changed files and checked nothing afterwards. */
317
+ private readonly completionAudit?: CompletionAudit
318
+ /** The next step must call a tool, at low effort: set by the stall monitor or a capped step. */
319
+ private forceActionNextStep = false
320
+ /** Steps in a row cut at the per-step reasoning cap; past a few the cap stops applying. */
321
+ private cappedStreak = 0
322
+ /** A reply that held only reasoning was already answered with one more request. */
323
+ private reasoningOnlyRetried = false
302
324
  /**
303
325
  * The model a refused request moved to. Once set it replaces the model the
304
326
  * step asked for, for the rest of the turn: the request that was refused
@@ -393,6 +415,10 @@ export class IterationOrchestrator {
393
415
  this.rememberUserMessage(createRuntimeContextMessage(text, 'steering'))
394
416
  },
395
417
  }
418
+ if (ctx.turnConfig.completionAudit === true) this.completionAudit = new CompletionAudit()
419
+ if (ctx.turnConfig.reasoning?.stall !== false) {
420
+ this.stallMonitor = new StallMonitor(ctx.turnConfig.reasoning?.stall || undefined)
421
+ }
396
422
  ctx.checkpointMgr.setLatestUserMessageSource(() => this.latestUserMessage)
397
423
  ctx.checkpointMgr.setAnswerReviewAttemptsSource?.(() => this.answerReviewAttempts)
398
424
  ctx.checkpointMgr.setStructuredReviewAttemptsSource?.(() => this.structuredReviewAttempts)
@@ -1063,6 +1089,11 @@ export class IterationOrchestrator {
1063
1089
  // died part-way has already emitted both `message_started` and
1064
1090
  // `message_completed` under this id, which is the trail a
1065
1091
  // reader wants most on exactly that turn.
1092
+ // A step the loop forces to act runs at low effort with a tool call
1093
+ // required, for this step only. Low is used only where the model
1094
+ // is known to offer it; effort is refused, not clamped.
1095
+ const forcedAction = this.forceActionNextStep && !forceFinalize && llmTools.length > 0
1096
+ this.forceActionNextStep = false
1066
1097
  stepMessageId = generateMessageId()
1067
1098
  const requestedMember = this.ctx.servingMember?.() ?? {
1068
1099
  index: 0,
@@ -1092,12 +1123,30 @@ export class IterationOrchestrator {
1092
1123
  })
1093
1124
  }
1094
1125
  }
1126
+ // A forced action step runs at low effort, where the model is known
1127
+ // to offer it; effort is refused, not clamped.
1128
+ const offeredEfforts = this.ctx.provider.reasoningEffortLevelsFor?.(stepModel)
1129
+ const stepEffort: ReasoningEffort | undefined =
1130
+ forcedAction && requestEffort && (offeredEfforts?.includes('low') ?? false)
1131
+ ? 'low'
1132
+ : requestEffort
1133
+ // A provider that refuses a forced tool choice (some models do, and
1134
+ // so does manual extended thinking) is not sent one: the step keeps
1135
+ // its low effort and asks in words, on this request only.
1136
+ const forcedChoiceRefused =
1137
+ forcedAction &&
1138
+ this.ctx.provider.acceptsForcedToolChoiceFor?.(stepModel, turnConfig.thinking) === false
1095
1139
  const operatorInputAtDispatch = this.latestUserMessage
1096
1140
  const latestReviewUserMessage =
1097
1141
  (this.ctx.reviewAnswer || this.ctx.structuredOutput?.review) && operatorInputAtDispatch
1098
1142
  ? structuredClone(operatorInputAtDispatch)
1099
1143
  : undefined
1100
- const { response, messageId, requestMessages } = yield* streamProviderTurn(
1144
+ const {
1145
+ response,
1146
+ messageId,
1147
+ requestMessages,
1148
+ reasoningCapped = false,
1149
+ } = yield* streamProviderTurn(
1101
1150
  this.ctx.provider,
1102
1151
  {
1103
1152
  model: stepModel,
@@ -1114,7 +1163,9 @@ export class IterationOrchestrator {
1114
1163
  }
1115
1164
  : {}),
1116
1165
  providerRoute: requestedRoute,
1117
- messages,
1166
+ messages: forcedChoiceRefused
1167
+ ? [...messages, createRuntimeContextMessage(FORCED_STEP_MESSAGE, 'progress-nudge')]
1168
+ : messages,
1118
1169
  tools: llmTools.length > 0 ? llmTools : undefined,
1119
1170
  ...(enforceToolInputSchema ? { enforceToolInputSchema } : {}),
1120
1171
  // The forced-final turn wins: a step that asked to force a
@@ -1126,14 +1177,16 @@ export class IterationOrchestrator {
1126
1177
  forceFinalize && llmTools.length > 0
1127
1178
  ? 'none'
1128
1179
  : llmTools.length > 0
1129
- ? step.toolChoice
1180
+ ? forcedAction && !forcedChoiceRefused
1181
+ ? (step.toolChoice ?? 'required')
1182
+ : step.toolChoice
1130
1183
  : undefined,
1131
1184
  temperature: step.temperature ?? turnConfig.temperature,
1132
1185
  maxTokens:
1133
1186
  outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
1134
1187
  cacheControl: { type: 'auto' },
1135
1188
  ...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
1136
- ...(requestEffort ? { effort: requestEffort } : {}),
1189
+ ...(stepEffort ? { effort: stepEffort } : {}),
1137
1190
  ...(!forceFinalize && turnConfig.webSearch
1138
1191
  ? { webSearch: turnConfig.webSearch }
1139
1192
  : {}),
@@ -1159,8 +1212,15 @@ export class IterationOrchestrator {
1159
1212
  this.ctx.reviewAnswer || this.ctx.structuredOutput?.review || this.ctx.advisoryCtx,
1160
1213
  ),
1161
1214
  recorder.sessionId,
1215
+ // Applies only to a step that can act. Stops after a few capped
1216
+ // steps in a row, so a model that cannot act within the allowance
1217
+ // still gets to finish.
1218
+ this.cappedStreak >= MAX_CAPPED_STREAK || forceFinalize || llmTools.length === 0
1219
+ ? undefined
1220
+ : reasoningCapChars(turnConfig.reasoning, stepEffort),
1162
1221
  )
1163
1222
  stepResponse = response
1223
+ this.cappedStreak = reasoningCapped ? this.cappedStreak + 1 : 0
1164
1224
  const reviewRequest: ReviewRequest = {
1165
1225
  ...(requestMessages ? { requestMessages } : {}),
1166
1226
  ...(latestReviewUserMessage ? { latestUserMessage: latestReviewUserMessage } : {}),
@@ -1461,6 +1521,28 @@ export class IterationOrchestrator {
1461
1521
  costBefore,
1462
1522
  })
1463
1523
 
1524
+ // The step spent its whole reasoning allowance before acting. Tell
1525
+ // the model so and make the next step an action step.
1526
+ if (reasoningCapped && !forceFinalize) {
1527
+ this.ctx.log.info('Reasoning reached the per-step cap — asking the model to act', {
1528
+ [NAMZU.TURN_ID]: recorder.turnId,
1529
+ [NAMZU.ITERATION]: iterationNum,
1530
+ })
1531
+ recorder.pushMessage(
1532
+ createRuntimeContextMessage(REASONING_CAPPED_MESSAGE, 'progress-nudge'),
1533
+ )
1534
+ this.forceActionNextStep = true
1535
+ this.observeStall(response, [])
1536
+ await this.ctx.emitEvent({
1537
+ type: 'iteration_completed',
1538
+ turnId: recorder.turnId,
1539
+ iteration: iterationNum,
1540
+ hasToolCalls: false,
1541
+ })
1542
+ yield* this.ctx.drainPending()
1543
+ continue
1544
+ }
1545
+
1464
1546
  if (this.ctx.structuredOutput?.mode === 'native') {
1465
1547
  const candidate = await parseNativeCandidate(
1466
1548
  this.ctx.structuredOutput.schema,
@@ -1745,6 +1827,28 @@ export class IterationOrchestrator {
1745
1827
  let closingStopReason: StopReason | undefined = forceFinalize
1746
1828
  ? guardResult.stopReason
1747
1829
  : undefined
1830
+ const reasoningOnly = !hasContent && (response.message.reasoning?.length ?? 0) > 0
1831
+ if (hasContent) this.reasoningOnlyRetried = false
1832
+ // A reply of reasoning alone is not an end: ask once more, plainly,
1833
+ // before the closing request below.
1834
+ if (reasoningOnly && !forceFinalize && !this.reasoningOnlyRetried) {
1835
+ this.reasoningOnlyRetried = true
1836
+ this.ctx.log.warn('Reply held reasoning only — asking once more', {
1837
+ [NAMZU.TURN_ID]: recorder.turnId,
1838
+ [NAMZU.ITERATION]: iterationNum,
1839
+ })
1840
+ recorder.pushMessage(
1841
+ createRuntimeContextMessage(REASONING_ONLY_MESSAGE, 'progress-nudge'),
1842
+ )
1843
+ await this.ctx.emitEvent({
1844
+ type: 'iteration_completed',
1845
+ turnId: recorder.turnId,
1846
+ iteration: iterationNum,
1847
+ hasToolCalls: false,
1848
+ })
1849
+ yield* this.ctx.drainPending()
1850
+ continue
1851
+ }
1748
1852
  if (!hasContent && !forceFinalize) {
1749
1853
  this.ctx.log.warn('Empty completion detected — requesting final summary', {
1750
1854
  [NAMZU.ITERATION]: iterationNum,
@@ -1755,6 +1859,23 @@ export class IterationOrchestrator {
1755
1859
  'end_turn',
1756
1860
  )
1757
1861
  yield* this.ctx.drainPending()
1862
+ // Never end silently: if even the closing request produced nothing,
1863
+ // say so in plain words and how to go on.
1864
+ if (
1865
+ reasoningOnly &&
1866
+ closingStopReason === undefined &&
1867
+ !(
1868
+ (
1869
+ [...recorder.messages].reverse().find((m) => m.role === 'assistant') as
1870
+ | { content?: string | null }
1871
+ | undefined
1872
+ )?.content ?? ''
1873
+ ).length
1874
+ ) {
1875
+ recorder.setLastError(
1876
+ 'The model stopped after thinking without giving an answer or taking an action, even when asked again. Say "continue" and it will pick the work up from here.',
1877
+ )
1878
+ }
1758
1879
  }
1759
1880
 
1760
1881
  await this.ctx.emitEvent({
@@ -1787,6 +1908,21 @@ export class IterationOrchestrator {
1787
1908
  // The model is done and has no tool calls. A `turn_end` (or
1788
1909
  // `subagent_stop`) hook may refuse to let it be: its reason goes
1789
1910
  // back to the model and the turn goes on.
1911
+ // Files were changed and nothing checked them afterwards: ask once
1912
+ // for a check before the turn is allowed to end.
1913
+ if (
1914
+ !forceFinalize &&
1915
+ closingStopReason === undefined &&
1916
+ this.completionAudit?.takeBlock()
1917
+ ) {
1918
+ this.ctx.log.info('Files changed and nothing checked them — asking for a check', {
1919
+ [NAMZU.TURN_ID]: recorder.turnId,
1920
+ })
1921
+ recorder.pushMessage(
1922
+ createRuntimeContextMessage(COMPLETION_AUDIT_MESSAGE, 'progress-nudge'),
1923
+ )
1924
+ continue
1925
+ }
1790
1926
  if (!forceFinalize && closingStopReason === undefined) {
1791
1927
  const feedback = yield* this.askStopHooks()
1792
1928
  const stoppedByHook = this.ctx.toolExecutor.takeHalt?.()
@@ -1833,6 +1969,9 @@ export class IterationOrchestrator {
1833
1969
  usageBefore,
1834
1970
  costBefore,
1835
1971
  })
1972
+ this.reasoningOnlyRetried = false
1973
+ this.observeStall(response, reviewOutcome.results)
1974
+ this.observeAudit(response, reviewOutcome.results)
1836
1975
 
1837
1976
  if (reviewOutcome.decision === 'stop') {
1838
1977
  return
@@ -3118,6 +3257,69 @@ export class IterationOrchestrator {
3118
3257
  }
3119
3258
  }
3120
3259
 
3260
+ /** Feed the completion audit the calls of one finished step. */
3261
+ private observeAudit(
3262
+ response: ChatCompletionResponse,
3263
+ results: readonly ToolCallOutcome[],
3264
+ ): void {
3265
+ const audit = this.completionAudit
3266
+ if (!audit) return
3267
+ const calls = new Map((response.message.toolCalls ?? []).map((c) => [c.id, c]))
3268
+ audit.observe(
3269
+ results.map((result) => {
3270
+ let input: unknown
3271
+ try {
3272
+ const call = calls.get(result.toolCallId)
3273
+ input = call ? JSON.parse(call.function.arguments) : undefined
3274
+ } catch {
3275
+ input = undefined
3276
+ }
3277
+ return { toolName: result.toolName, input, isError: result.isError }
3278
+ }),
3279
+ )
3280
+ }
3281
+
3282
+ /**
3283
+ * Feed the stall monitor one finished step and act on its verdict: a nudge
3284
+ * is recorded as a message, a force makes the next step an action step.
3285
+ */
3286
+ private observeStall(
3287
+ response: ChatCompletionResponse,
3288
+ results: readonly ToolCallOutcome[],
3289
+ ): void {
3290
+ const monitor = this.stallMonitor
3291
+ if (!monitor) return
3292
+ const calls = new Map((response.message.toolCalls ?? []).map((c) => [c.id, c]))
3293
+ let changedState = false
3294
+ for (const result of results) {
3295
+ const call = calls.get(result.toolCallId)
3296
+ let readOnly: boolean | undefined
3297
+ try {
3298
+ const input = call ? JSON.parse(call.function.arguments) : undefined
3299
+ readOnly = this.ctx.tools.get(result.toolName)?.isReadOnly?.(input)
3300
+ } catch {
3301
+ readOnly = undefined
3302
+ }
3303
+ if (callChangedState(result.toolName, readOnly, result.isError)) changedState = true
3304
+ }
3305
+ const reasoningChars = (response.message.reasoning ?? []).reduce(
3306
+ (n, block) => n + (block.text?.length ?? 0),
3307
+ 0,
3308
+ )
3309
+ const directive = monitor.observe({ reasoningChars, changedState })
3310
+ if (directive.kind === 'nudge') {
3311
+ this.ctx.log.info('Many steps without a change — nudging the model to act', {
3312
+ [NAMZU.TURN_ID]: this.ctx.recorder.turnId,
3313
+ })
3314
+ this.ctx.recorder.pushMessage(createRuntimeContextMessage(directive.text, 'progress-nudge'))
3315
+ } else if (directive.kind === 'force') {
3316
+ this.ctx.log.info('Still no change after the nudge — forcing an action step', {
3317
+ [NAMZU.TURN_ID]: this.ctx.recorder.turnId,
3318
+ })
3319
+ this.forceActionNextStep = true
3320
+ }
3321
+ }
3322
+
3121
3323
  private async requestFinalResponse(
3122
3324
  model: string,
3123
3325
  reason: StopReason,