@namzu/sdk 36.0.0 → 37.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 (308) hide show
  1. package/CHANGELOG.md +180 -0
  2. package/README.md +68 -41
  3. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  4. package/dist/bridge/a2a/mapper.js +2 -0
  5. package/dist/bridge/a2a/mapper.js.map +1 -1
  6. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  7. package/dist/bridge/sse/mapper.js +2 -0
  8. package/dist/bridge/sse/mapper.js.map +1 -1
  9. package/dist/compaction/extractor.d.ts +1 -1
  10. package/dist/compaction/extractor.d.ts.map +1 -1
  11. package/dist/compaction/extractor.js +15 -7
  12. package/dist/compaction/extractor.js.map +1 -1
  13. package/dist/compaction/manager.d.ts +2 -0
  14. package/dist/compaction/manager.d.ts.map +1 -1
  15. package/dist/compaction/manager.js +12 -1
  16. package/dist/compaction/manager.js.map +1 -1
  17. package/dist/compaction/verifier.d.ts.map +1 -1
  18. package/dist/compaction/verifier.js +82 -15
  19. package/dist/compaction/verifier.js.map +1 -1
  20. package/dist/contracts/schemas.d.ts +10 -10
  21. package/dist/eval/harness-verification.d.ts +63 -0
  22. package/dist/eval/harness-verification.d.ts.map +1 -0
  23. package/dist/eval/harness-verification.js +165 -0
  24. package/dist/eval/harness-verification.js.map +1 -0
  25. package/dist/eval/index.d.ts +2 -0
  26. package/dist/eval/index.d.ts.map +1 -1
  27. package/dist/eval/index.js +1 -0
  28. package/dist/eval/index.js.map +1 -1
  29. package/dist/execution/code-runtime/json-value.d.ts +10 -0
  30. package/dist/execution/code-runtime/json-value.d.ts.map +1 -0
  31. package/dist/execution/code-runtime/json-value.js +89 -0
  32. package/dist/execution/code-runtime/json-value.js.map +1 -0
  33. package/dist/execution/code-runtime/worker-program.d.ts +8 -0
  34. package/dist/execution/code-runtime/worker-program.d.ts.map +1 -0
  35. package/dist/execution/code-runtime/worker-program.js +240 -0
  36. package/dist/execution/code-runtime/worker-program.js.map +1 -0
  37. package/dist/execution/code-runtime/worker.d.ts +22 -0
  38. package/dist/execution/code-runtime/worker.d.ts.map +1 -1
  39. package/dist/execution/code-runtime/worker.js +148 -111
  40. package/dist/execution/code-runtime/worker.js.map +1 -1
  41. package/dist/manager/agent/lifecycle.d.ts +12 -0
  42. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  43. package/dist/manager/agent/lifecycle.js +306 -37
  44. package/dist/manager/agent/lifecycle.js.map +1 -1
  45. package/dist/manager/run/persistence.d.ts +2 -0
  46. package/dist/manager/run/persistence.d.ts.map +1 -1
  47. package/dist/manager/run/persistence.js +6 -0
  48. package/dist/manager/run/persistence.js.map +1 -1
  49. package/dist/provider/capabilities.d.ts +3 -1
  50. package/dist/provider/capabilities.d.ts.map +1 -1
  51. package/dist/provider/capabilities.js +13 -0
  52. package/dist/provider/capabilities.js.map +1 -1
  53. package/dist/provider/fallback.d.ts.map +1 -1
  54. package/dist/provider/fallback.js +2 -0
  55. package/dist/provider/fallback.js.map +1 -1
  56. package/dist/public-runtime.d.ts +3 -1
  57. package/dist/public-runtime.d.ts.map +1 -1
  58. package/dist/public-runtime.js +2 -1
  59. package/dist/public-runtime.js.map +1 -1
  60. package/dist/public-tools.d.ts +1 -1
  61. package/dist/public-tools.d.ts.map +1 -1
  62. package/dist/public-tools.js +1 -1
  63. package/dist/public-tools.js.map +1 -1
  64. package/dist/public-types.d.ts +1 -0
  65. package/dist/public-types.d.ts.map +1 -1
  66. package/dist/registry/tool/execute.d.ts +3 -0
  67. package/dist/registry/tool/execute.d.ts.map +1 -1
  68. package/dist/registry/tool/execute.js +11 -80
  69. package/dist/registry/tool/execute.js.map +1 -1
  70. package/dist/run/command-gate.d.ts.map +1 -1
  71. package/dist/run/command-gate.js +65 -12
  72. package/dist/run/command-gate.js.map +1 -1
  73. package/dist/run/index.d.ts +2 -0
  74. package/dist/run/index.d.ts.map +1 -1
  75. package/dist/run/index.js +1 -0
  76. package/dist/run/index.js.map +1 -1
  77. package/dist/run/memory-promoter.d.ts +3 -35
  78. package/dist/run/memory-promoter.d.ts.map +1 -1
  79. package/dist/run/memory-promoter.js +32 -40
  80. package/dist/run/memory-promoter.js.map +1 -1
  81. package/dist/run/memory-recall.d.ts +25 -0
  82. package/dist/run/memory-recall.d.ts.map +1 -0
  83. package/dist/run/memory-recall.js +228 -0
  84. package/dist/run/memory-recall.js.map +1 -0
  85. package/dist/run/reporter.d.ts.map +1 -1
  86. package/dist/run/reporter.js +1 -0
  87. package/dist/run/reporter.js.map +1 -1
  88. package/dist/run/token-budget.d.ts +2 -0
  89. package/dist/run/token-budget.d.ts.map +1 -1
  90. package/dist/run/token-budget.js +4 -0
  91. package/dist/run/token-budget.js.map +1 -1
  92. package/dist/run/workspace-fingerprint.d.ts +10 -3
  93. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  94. package/dist/run/workspace-fingerprint.js +17 -8
  95. package/dist/run/workspace-fingerprint.js.map +1 -1
  96. package/dist/runtime/query/checkpoint.d.ts +14 -0
  97. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  98. package/dist/runtime/query/checkpoint.js +82 -1
  99. package/dist/runtime/query/checkpoint.js.map +1 -1
  100. package/dist/runtime/query/executor.d.ts +5 -0
  101. package/dist/runtime/query/executor.d.ts.map +1 -1
  102. package/dist/runtime/query/executor.js +70 -14
  103. package/dist/runtime/query/executor.js.map +1 -1
  104. package/dist/runtime/query/index.d.ts +5 -8
  105. package/dist/runtime/query/index.d.ts.map +1 -1
  106. package/dist/runtime/query/index.js +54 -7
  107. package/dist/runtime/query/index.js.map +1 -1
  108. package/dist/runtime/query/iteration/index.d.ts +10 -0
  109. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  110. package/dist/runtime/query/iteration/index.js +325 -36
  111. package/dist/runtime/query/iteration/index.js.map +1 -1
  112. package/dist/runtime/query/iteration/native-output.d.ts +10 -0
  113. package/dist/runtime/query/iteration/native-output.d.ts.map +1 -0
  114. package/dist/runtime/query/iteration/native-output.js +32 -0
  115. package/dist/runtime/query/iteration/native-output.js.map +1 -0
  116. package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
  117. package/dist/runtime/query/iteration/phases/advisory.js +18 -13
  118. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  119. package/dist/runtime/query/iteration/phases/compaction.d.ts +2 -0
  120. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  121. package/dist/runtime/query/iteration/phases/compaction.js +32 -11
  122. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  123. package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
  124. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  125. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  126. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  127. package/dist/runtime/query/iteration/phases/tool-review.js +1 -1
  128. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  129. package/dist/runtime/query/iteration/phases/working-memory.d.ts +1 -1
  130. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
  131. package/dist/runtime/query/iteration/phases/working-memory.js +23 -6
  132. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
  133. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  134. package/dist/runtime/query/iteration/stream-turn.js +2 -0
  135. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  136. package/dist/runtime/query/steering.d.ts +6 -2
  137. package/dist/runtime/query/steering.d.ts.map +1 -1
  138. package/dist/runtime/query/steering.js +17 -2
  139. package/dist/runtime/query/steering.js.map +1 -1
  140. package/dist/runtime/query/tool-call-budget.d.ts +19 -0
  141. package/dist/runtime/query/tool-call-budget.d.ts.map +1 -0
  142. package/dist/runtime/query/tool-call-budget.js +110 -0
  143. package/dist/runtime/query/tool-call-budget.js.map +1 -0
  144. package/dist/runtime/query/tooling.d.ts +2 -0
  145. package/dist/runtime/query/tooling.d.ts.map +1 -1
  146. package/dist/runtime/query/tooling.js +4 -0
  147. package/dist/runtime/query/tooling.js.map +1 -1
  148. package/dist/scheduler/local.d.ts.map +1 -1
  149. package/dist/scheduler/local.js +1 -0
  150. package/dist/scheduler/local.js.map +1 -1
  151. package/dist/session/handoff/capacity.d.ts +2 -2
  152. package/dist/session/handoff/capacity.d.ts.map +1 -1
  153. package/dist/session/handoff/capacity.js +3 -2
  154. package/dist/session/handoff/capacity.js.map +1 -1
  155. package/dist/store/memory/disk.d.ts +6 -2
  156. package/dist/store/memory/disk.d.ts.map +1 -1
  157. package/dist/store/memory/disk.js +42 -25
  158. package/dist/store/memory/disk.js.map +1 -1
  159. package/dist/store/memory/index.d.ts +4 -0
  160. package/dist/store/memory/index.d.ts.map +1 -1
  161. package/dist/store/memory/index.js +55 -19
  162. package/dist/store/memory/index.js.map +1 -1
  163. package/dist/store/memory/memory.d.ts +3 -2
  164. package/dist/store/memory/memory.d.ts.map +1 -1
  165. package/dist/store/memory/memory.js +11 -2
  166. package/dist/store/memory/memory.js.map +1 -1
  167. package/dist/store/memory/operation-lock.d.ts +11 -0
  168. package/dist/store/memory/operation-lock.d.ts.map +1 -0
  169. package/dist/store/memory/operation-lock.js +94 -0
  170. package/dist/store/memory/operation-lock.js.map +1 -0
  171. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  172. package/dist/tools/builtins/computer-use.js +60 -10
  173. package/dist/tools/builtins/computer-use.js.map +1 -1
  174. package/dist/tools/builtins/run-code.d.ts +6 -0
  175. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  176. package/dist/tools/builtins/run-code.js +15 -4
  177. package/dist/tools/builtins/run-code.js.map +1 -1
  178. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  179. package/dist/tools/builtins/search-tools.js +14 -2
  180. package/dist/tools/builtins/search-tools.js.map +1 -1
  181. package/dist/tools/defineTool.d.ts +2 -0
  182. package/dist/tools/defineTool.d.ts.map +1 -1
  183. package/dist/tools/defineTool.js +3 -0
  184. package/dist/tools/defineTool.js.map +1 -1
  185. package/dist/tools/memory/delete.d.ts +4 -0
  186. package/dist/tools/memory/delete.d.ts.map +1 -0
  187. package/dist/tools/memory/delete.js +26 -0
  188. package/dist/tools/memory/delete.js.map +1 -0
  189. package/dist/tools/memory/index.d.ts +2 -0
  190. package/dist/tools/memory/index.d.ts.map +1 -1
  191. package/dist/tools/memory/index.js +6 -0
  192. package/dist/tools/memory/index.js.map +1 -1
  193. package/dist/tools/memory/save.d.ts.map +1 -1
  194. package/dist/tools/memory/save.js +3 -2
  195. package/dist/tools/memory/save.js.map +1 -1
  196. package/dist/tools/memory/search.d.ts.map +1 -1
  197. package/dist/tools/memory/search.js +12 -5
  198. package/dist/tools/memory/search.js.map +1 -1
  199. package/dist/tools/memory/update.d.ts +4 -0
  200. package/dist/tools/memory/update.d.ts.map +1 -0
  201. package/dist/tools/memory/update.js +47 -0
  202. package/dist/tools/memory/update.js.map +1 -0
  203. package/dist/types/agent/scheduler.d.ts +5 -0
  204. package/dist/types/agent/scheduler.d.ts.map +1 -1
  205. package/dist/types/agent/task.d.ts +10 -0
  206. package/dist/types/agent/task.d.ts.map +1 -1
  207. package/dist/types/computer-use/index.d.ts +6 -0
  208. package/dist/types/computer-use/index.d.ts.map +1 -1
  209. package/dist/types/computer-use/index.js.map +1 -1
  210. package/dist/types/errors/catalog.js +1 -1
  211. package/dist/types/errors/catalog.js.map +1 -1
  212. package/dist/types/hitl/index.d.ts +11 -1
  213. package/dist/types/hitl/index.d.ts.map +1 -1
  214. package/dist/types/hitl/index.js.map +1 -1
  215. package/dist/types/memory/index.d.ts +14 -1
  216. package/dist/types/memory/index.d.ts.map +1 -1
  217. package/dist/types/project/entity.d.ts +1 -0
  218. package/dist/types/project/entity.d.ts.map +1 -1
  219. package/dist/types/provider/config.d.ts +2 -0
  220. package/dist/types/provider/config.d.ts.map +1 -1
  221. package/dist/types/provider/model.d.ts +4 -0
  222. package/dist/types/provider/model.d.ts.map +1 -1
  223. package/dist/types/run/answer-review.d.ts +2 -0
  224. package/dist/types/run/answer-review.d.ts.map +1 -1
  225. package/dist/types/run/events.d.ts +9 -0
  226. package/dist/types/run/events.d.ts.map +1 -1
  227. package/dist/types/run/events.js.map +1 -1
  228. package/dist/types/run/prepare-step.d.ts +19 -1
  229. package/dist/types/run/prepare-step.d.ts.map +1 -1
  230. package/dist/types/structured-output/index.d.ts +8 -2
  231. package/dist/types/structured-output/index.d.ts.map +1 -1
  232. package/dist/types/tool/index.d.ts +15 -0
  233. package/dist/types/tool/index.d.ts.map +1 -1
  234. package/dist/types/tool/index.js.map +1 -1
  235. package/dist/utils/json-snapshot.d.ts +3 -0
  236. package/dist/utils/json-snapshot.d.ts.map +1 -0
  237. package/dist/utils/json-snapshot.js +79 -0
  238. package/dist/utils/json-snapshot.js.map +1 -0
  239. package/package.json +4 -2
  240. package/src/bridge/a2a/mapper.ts +2 -0
  241. package/src/bridge/sse/mapper.ts +2 -0
  242. package/src/compaction/extractor.ts +21 -8
  243. package/src/compaction/manager.ts +11 -1
  244. package/src/compaction/verifier.ts +80 -18
  245. package/src/eval/harness-verification.ts +270 -0
  246. package/src/eval/index.ts +10 -0
  247. package/src/execution/code-runtime/json-value.ts +82 -0
  248. package/src/execution/code-runtime/worker-program.ts +239 -0
  249. package/src/execution/code-runtime/worker.ts +164 -111
  250. package/src/manager/agent/lifecycle.ts +340 -45
  251. package/src/manager/run/persistence.ts +7 -0
  252. package/src/provider/capabilities.ts +25 -1
  253. package/src/provider/fallback.ts +2 -0
  254. package/src/public-runtime.ts +4 -0
  255. package/src/public-tools.ts +5 -1
  256. package/src/public-types.ts +1 -0
  257. package/src/registry/tool/execute.ts +17 -79
  258. package/src/run/command-gate.ts +61 -13
  259. package/src/run/index.ts +2 -0
  260. package/src/run/memory-promoter.ts +32 -40
  261. package/src/run/memory-recall.ts +254 -0
  262. package/src/run/reporter.ts +1 -0
  263. package/src/run/token-budget.ts +4 -0
  264. package/src/run/workspace-fingerprint.ts +17 -8
  265. package/src/runtime/query/checkpoint.ts +96 -2
  266. package/src/runtime/query/executor.ts +77 -14
  267. package/src/runtime/query/index.ts +73 -16
  268. package/src/runtime/query/iteration/index.ts +377 -44
  269. package/src/runtime/query/iteration/native-output.ts +35 -0
  270. package/src/runtime/query/iteration/phases/advisory.ts +21 -11
  271. package/src/runtime/query/iteration/phases/compaction.ts +36 -14
  272. package/src/runtime/query/iteration/phases/context.ts +9 -0
  273. package/src/runtime/query/iteration/phases/tool-review.ts +5 -1
  274. package/src/runtime/query/iteration/phases/working-memory.ts +23 -5
  275. package/src/runtime/query/iteration/stream-turn.ts +2 -0
  276. package/src/runtime/query/steering.ts +19 -2
  277. package/src/runtime/query/tool-call-budget.ts +115 -0
  278. package/src/runtime/query/tooling.ts +6 -0
  279. package/src/scheduler/local.ts +1 -0
  280. package/src/session/handoff/capacity.ts +7 -3
  281. package/src/store/memory/disk.ts +52 -30
  282. package/src/store/memory/index.ts +75 -27
  283. package/src/store/memory/memory.ts +18 -6
  284. package/src/store/memory/operation-lock.ts +99 -0
  285. package/src/tools/builtins/computer-use.ts +61 -7
  286. package/src/tools/builtins/run-code.ts +24 -5
  287. package/src/tools/builtins/search-tools.ts +18 -3
  288. package/src/tools/defineTool.ts +5 -0
  289. package/src/tools/memory/delete.ts +29 -0
  290. package/src/tools/memory/index.ts +6 -0
  291. package/src/tools/memory/save.ts +4 -2
  292. package/src/tools/memory/search.ts +12 -5
  293. package/src/tools/memory/update.ts +50 -0
  294. package/src/types/agent/scheduler.ts +6 -0
  295. package/src/types/agent/task.ts +13 -0
  296. package/src/types/computer-use/index.ts +6 -0
  297. package/src/types/errors/catalog.ts +1 -1
  298. package/src/types/hitl/index.ts +11 -1
  299. package/src/types/memory/index.ts +16 -1
  300. package/src/types/project/entity.ts +1 -0
  301. package/src/types/provider/config.ts +2 -0
  302. package/src/types/provider/model.ts +4 -0
  303. package/src/types/run/answer-review.ts +2 -0
  304. package/src/types/run/events.ts +10 -0
  305. package/src/types/run/prepare-step.ts +19 -1
  306. package/src/types/structured-output/index.ts +15 -2
  307. package/src/types/tool/index.ts +16 -0
  308. package/src/utils/json-snapshot.ts +76 -0
@@ -1,11 +1,13 @@
1
1
  import { SpanStatusCode } from '@opentelemetry/api';
2
2
  import { resolveContextWindow } from '../../../compaction/context-window.js';
3
- import { extractFromAssistantMessage } from '../../../compaction/extractor.js';
3
+ import { extractFromAssistantMessage, extractFromUserMessage, } from '../../../compaction/extractor.js';
4
+ import { estimateMessageTokens } from '../../../compaction/token-estimate.js';
4
5
  import { AUTO_CONTINUATION_USER_MESSAGE } from '../../../constants/continuation.js';
5
6
  import { DEFAULT_STRUCTURED_OUTPUT_RETRIES, STRUCTURED_OUTPUT_REPROMPT, } from '../../../constants/tools/index.js';
6
7
  import { renderSkillsSection } from '../../../persona/assembler.js';
7
8
  import { resolveProviderCapabilities } from '../../../provider/capabilities.js';
8
9
  import { collectChatCompletion } from '../../../provider/collect-chat-completion.js';
10
+ import { renderToolSchema } from '../../../registry/tool/schema.js';
9
11
  import { formatCompletionNotification } from '../../../scheduler/completion-inbox.js';
10
12
  import { GENAI, NAMZU, agentIterationSpanName, parentContext, } from '../../../telemetry/attributes.js';
11
13
  import { getTracer } from '../../../telemetry/runtime-accessors.js';
@@ -19,10 +21,11 @@ import { stableDigest } from '../../../utils/hash.js';
19
21
  import { generateMessageId } from '../../../utils/id.js';
20
22
  import { applyLifecycleHookResults } from '../plugin-hooks.js';
21
23
  import { DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES, markProviderRejectedImage, projectRequestRichContent, } from '../request-rich-content.js';
22
- import { formatSteeringNote } from '../steering.js';
24
+ import { formatSteeringNote, isOperatorUserMessage } from '../steering.js';
25
+ import { parseNativeCandidate } from './native-output.js';
23
26
  import { runAdvisoryPhase } from './phases/advisory.js';
24
27
  import { runIterationCheckpoint } from './phases/checkpoint.js';
25
- import { measureContext, relieveOverflow, runCompactionCheck } from './phases/compaction.js';
28
+ import { activeContextWindow, measureContext, relieveOverflow, runCompactionCheck, } from './phases/compaction.js';
26
29
  import { runPlanGate } from './phases/plan.js';
27
30
  import { runToolReview } from './phases/tool-review.js';
28
31
  import { refreshWorkingMemory } from './phases/working-memory.js';
@@ -121,8 +124,29 @@ export class IterationOrchestrator {
121
124
  * sets it.
122
125
  */
123
126
  stopDeferredForOutstandingWork = false;
127
+ /** One current input, independent of the compactable history array. */
128
+ latestUserMessage;
124
129
  constructor(ctx) {
125
- this.ctx = ctx;
130
+ this.ctx = {
131
+ ...ctx,
132
+ onSteeringDelivered: (text) => {
133
+ this.rememberUserMessage(createRuntimeContextMessage(text, 'steering'));
134
+ },
135
+ };
136
+ ctx.checkpointMgr.setLatestUserMessageSource(() => this.latestUserMessage);
137
+ ctx.checkpointMgr.setStructuredReviewAttemptsSource?.(() => this.structuredReviewAttempts);
138
+ ctx.checkpointMgr.setNativeStructuredAttemptsSource?.(() => this.nativeStructuredAttempts);
139
+ if (ctx.structuredOutput?.mode === 'native') {
140
+ const limit = ctx.structuredOutput.maxRetries;
141
+ if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 0))
142
+ throw new RangeError('Native structuredOutput.maxRetries must be a nonnegative safe integer');
143
+ }
144
+ if (ctx.structuredOutput?.mode !== undefined &&
145
+ !['tool', 'native'].includes(ctx.structuredOutput.mode))
146
+ throw new RangeError('Unknown structuredOutput.mode');
147
+ const maxReviews = ctx.structuredOutput?.maxReviews;
148
+ if (maxReviews !== undefined && (!Number.isSafeInteger(maxReviews) || maxReviews < 0))
149
+ throw new RangeError('structuredOutput.maxReviews must be a nonnegative safe integer');
126
150
  }
127
151
  /**
128
152
  * Check the exact post-budget request for tool-result shapes the active driver
@@ -198,6 +222,19 @@ export class IterationOrchestrator {
198
222
  const { runConfig, runMgr } = this.ctx;
199
223
  const { model } = runConfig;
200
224
  const tracer = getTracer();
225
+ // Resume hydration happens after construction, before the loop starts.
226
+ this.latestUserMessage = this.ctx.checkpointMgr.restoredLatestUserMessage;
227
+ this.structuredReviewAttempts = this.ctx.checkpointMgr.restoredStructuredReviewAttempts ?? 0;
228
+ this.nativeStructuredAttempts = this.ctx.checkpointMgr.restoredNativeStructuredAttempts ?? 0;
229
+ if (!this.latestUserMessage) {
230
+ for (const message of runMgr.messages)
231
+ this.rememberUserMessage(message, false);
232
+ }
233
+ // The restored field can outlive the historical message it describes.
234
+ // Only known post-checkpoint arrivals may supersede it, never an old
235
+ // retained user turn encountered while scanning restored history.
236
+ for (const message of this.ctx.resumedInput ?? [])
237
+ this.rememberUserMessage(message);
201
238
  // One context-overflow relief per *stuck point*, not per run.
202
239
  //
203
240
  // The latch exists so that a second overflow immediately after a
@@ -222,6 +259,22 @@ export class IterationOrchestrator {
222
259
  // no post-loop block reaches.
223
260
  try {
224
261
  while (true) {
262
+ if (this.ctx.abortController.signal.aborted) {
263
+ runMgr.setStopReason('cancelled');
264
+ runMgr.markCancelled();
265
+ break;
266
+ }
267
+ if (this.ctx.structuredOutput?.mode === 'native' &&
268
+ this.nativeStructuredAttempts > this.structuredOutputRetryLimit()) {
269
+ runMgr.setStopReason('structured_output_failed');
270
+ break;
271
+ }
272
+ if (this.ctx.structuredOutput?.review &&
273
+ this.structuredReviewAttempts >
274
+ (this.ctx.structuredOutput.maxReviews ?? DEFAULT_ANSWER_REVIEW_LIMIT)) {
275
+ runMgr.setStopReason('answer_rejected');
276
+ break;
277
+ }
225
278
  // Read AND clear, in that order, in this one place.
226
279
  //
227
280
  // The flag is set by the previous iteration and read by this
@@ -265,6 +318,14 @@ export class IterationOrchestrator {
265
318
  // run and been paid for; this is the seam a host with a live
266
319
  // rate limit or a revoked tenant actually needs.
267
320
  const veto = await this.beforeStep(runMgr.currentIteration + 1);
321
+ // The hook may settle because its run signal was aborted. Stop
322
+ // before interpreting that settlement as a policy refusal or
323
+ // counting an iteration that will never reach the provider.
324
+ if (this.ctx.abortController.signal.aborted) {
325
+ runMgr.setStopReason('cancelled');
326
+ runMgr.markCancelled();
327
+ break;
328
+ }
268
329
  if (veto) {
269
330
  // Namespaced. The un-namespaced keys elsewhere in this file are
270
331
  // the frozen inventory LOG-22 exists to drain; a new call site
@@ -354,10 +415,6 @@ export class IterationOrchestrator {
354
415
  // compaction preserves). No-op when no provider is configured.
355
416
  await refreshWorkingMemory(this.ctx);
356
417
  await runCompactionCheck(this.ctx);
357
- // A context edit is a host-visible state transition, not a prelude
358
- // whose events may wait behind the next network call. In particular,
359
- // a slow or failing provider must not leave the host displaying the
360
- // pre-compaction context after Run.messages already shrank.
361
418
  yield* this.ctx.drainPending();
362
419
  // Cache discipline: keep the tools param byte-stable even on the
363
420
  // forced-final iteration and forbid tool use via tool_choice
@@ -373,9 +430,22 @@ export class IterationOrchestrator {
373
430
  // Shape this step before calling the model. `stopWhen` decides
374
431
  // whether to keep going; this decides HOW. No-op when the host
375
432
  // supplied no hook.
433
+ const contextModelBeforePreparation = this.ctx.contextModel ?? model;
376
434
  const step = await this.prepareStep(iterationNum);
435
+ stepModel = step.model ?? model;
436
+ await this.selectContextModel(stepModel);
437
+ // Preserve post-compaction preparation/recall semantics. A changed
438
+ // model needs a second check against its own window; never replay
439
+ // host preparation effects merely to rebuild its request guidance.
440
+ if (stepModel !== contextModelBeforePreparation)
441
+ await runCompactionCheck(this.ctx);
442
+ // Publish context edits before entering a possibly slow provider.
443
+ yield* this.ctx.drainPending();
377
444
  const stepAllowedTools = step.allowedTools ?? this.ctx.allowedTools;
378
- const llmTools = this.ctx.tools.toLLMTools(stepAllowedTools);
445
+ const llmTools = this.ctx.tools
446
+ .toLLMTools(stepAllowedTools)
447
+ .filter((tool) => this.ctx.structuredOutput?.mode !== 'native' ||
448
+ tool.function.name !== STRUCTURED_OUTPUT_TOOL_NAME);
379
449
  // The same list the request was built from now also bounds what
380
450
  // may run. Narrowing only the request left the restriction
381
451
  // presentational — the model was shown fewer tools and could
@@ -531,6 +601,18 @@ export class IterationOrchestrator {
531
601
  };
532
602
  const { response, messageId } = yield* streamProviderTurn(this.ctx.provider, {
533
603
  model: stepModel,
604
+ ...(this.ctx.structuredOutput?.mode === 'native'
605
+ ? {
606
+ responseFormat: {
607
+ type: 'json_schema',
608
+ json_schema: {
609
+ name: 'structured_output',
610
+ schema: renderToolSchema(this.ctx.structuredOutput.schema),
611
+ strict: true,
612
+ },
613
+ },
614
+ }
615
+ : {}),
534
616
  providerRoute: requestedRoute,
535
617
  messages,
536
618
  tools: llmTools.length > 0 ? llmTools : undefined,
@@ -646,7 +728,7 @@ export class IterationOrchestrator {
646
728
  const contextFigures = this.ctx.compactionConfig
647
729
  ? (() => {
648
730
  const measured = measureContext(this.ctx);
649
- const window = resolveContextWindow(this.ctx.compactionConfig?.contextWindowTokens, runConfig.model, this.ctx.providerContextWindow);
731
+ const window = activeContextWindow(this.ctx);
650
732
  return {
651
733
  contextTokens: measured.tokens,
652
734
  contextMeasuredBy: measured.source,
@@ -780,6 +862,60 @@ export class IterationOrchestrator {
780
862
  usageBefore,
781
863
  costBefore,
782
864
  });
865
+ if (this.ctx.structuredOutput?.mode === 'native') {
866
+ const candidate = await parseNativeCandidate(this.ctx.structuredOutput.schema, response, this.ctx.abortController.signal);
867
+ let outcome;
868
+ if (candidate.success)
869
+ outcome = await this.reviewStructuredOutput(candidate.value);
870
+ else {
871
+ this.nativeStructuredAttempts++;
872
+ runMgr.pushMessage(createRuntimeContextMessage('Return a complete JSON value matching the supplied response schema. Do not continue a partial JSON fragment.', 'structured-output'));
873
+ const checkpoint = await this.ctx.checkpointMgr.create(runMgr, iterationNum);
874
+ await this.ctx.emitEvent({
875
+ type: 'checkpoint_created',
876
+ runId: runMgr.id,
877
+ checkpointId: checkpoint.id,
878
+ iteration: iterationNum,
879
+ });
880
+ outcome =
881
+ this.nativeStructuredAttempts > this.structuredOutputRetryLimit()
882
+ ? 'exhausted'
883
+ : 'retry';
884
+ }
885
+ await this.ctx.emitEvent({
886
+ type: 'iteration_completed',
887
+ runId: runMgr.id,
888
+ iteration: iterationNum,
889
+ hasToolCalls: false,
890
+ });
891
+ yield* this.ctx.drainPending();
892
+ if (this.ctx.abortController.signal.aborted || outcome === 'cancelled') {
893
+ runMgr.setStopReason('cancelled');
894
+ runMgr.markCancelled();
895
+ break;
896
+ }
897
+ if (outcome === 'accepted') {
898
+ if (!forceFinalize) {
899
+ const changed = yield* this.holdForOutstandingWork(iterationNum, false);
900
+ const inbound = this.deliverInbound();
901
+ if (changed || inbound > 0)
902
+ continue;
903
+ }
904
+ if (this.ctx.abortController.signal.aborted) {
905
+ runMgr.setStopReason('cancelled');
906
+ runMgr.markCancelled();
907
+ break;
908
+ }
909
+ this.publishStructuredOutput();
910
+ runMgr.setStopReason('end_turn');
911
+ break;
912
+ }
913
+ if (outcome === 'exhausted') {
914
+ runMgr.setStopReason(candidate.success ? 'answer_rejected' : 'structured_output_failed');
915
+ break;
916
+ }
917
+ continue;
918
+ }
783
919
  const hasContent = response.message.content !== null && response.message.content.length > 0;
784
920
  // Auto-continuation on `stop_reason: max_tokens`. The
785
921
  // model hit its per-call output cap mid-text (NOT
@@ -862,6 +998,11 @@ export class IterationOrchestrator {
862
998
  // a loud stop rather than a loop.
863
999
  if (!forceFinalize && this.ctx.reviewAnswer) {
864
1000
  const review = await this.reviewAnswer(response.message.content ?? '');
1001
+ if (this.ctx.abortController.signal.aborted) {
1002
+ runMgr.setStopReason('cancelled');
1003
+ runMgr.markCancelled();
1004
+ break;
1005
+ }
865
1006
  if (review && !review.accept) {
866
1007
  const attempt = ++this.answerReviewAttempts;
867
1008
  const limit = this.ctx.maxAnswerReviews ?? DEFAULT_ANSWER_REVIEW_LIMIT;
@@ -988,12 +1129,34 @@ export class IterationOrchestrator {
988
1129
  // run ends here rather than paying for another turn whose only
989
1130
  // job would be to restate it — unless it shared its turn with
990
1131
  // other calls, which relays instead. See the method.
991
- if (this.captureStructuredOutput(reviewOutcome.results, response)) {
1132
+ const structuredOutcome = await this.captureStructuredOutput(reviewOutcome.results, response);
1133
+ if (structuredOutcome === 'retry' ||
1134
+ structuredOutcome === 'exhausted' ||
1135
+ structuredOutcome === 'cancelled') {
1136
+ await this.ctx.emitEvent({
1137
+ type: 'iteration_completed',
1138
+ runId: runMgr.id,
1139
+ iteration: iterationNum,
1140
+ hasToolCalls: true,
1141
+ });
1142
+ yield* this.ctx.drainPending();
1143
+ if (this.ctx.abortController.signal.aborted) {
1144
+ runMgr.setStopReason('cancelled');
1145
+ runMgr.markCancelled();
1146
+ break;
1147
+ }
1148
+ if (structuredOutcome === 'retry')
1149
+ continue;
1150
+ runMgr.setStopReason(structuredOutcome === 'cancelled' ? 'cancelled' : 'answer_rejected');
1151
+ if (structuredOutcome === 'cancelled')
1152
+ runMgr.markCancelled();
1153
+ break;
1154
+ }
1155
+ if (structuredOutcome === 'accepted') {
992
1156
  this.ctx.log.info('Structured output produced — ending run', {
993
1157
  [NAMZU.RUN_ID]: runMgr.id,
994
1158
  [NAMZU.ITERATION]: iterationNum,
995
1159
  });
996
- runMgr.setStopReason('end_turn');
997
1160
  await this.ctx.emitEvent({
998
1161
  type: 'iteration_completed',
999
1162
  runId: runMgr.id,
@@ -1001,6 +1164,13 @@ export class IterationOrchestrator {
1001
1164
  hasToolCalls: true,
1002
1165
  });
1003
1166
  yield* this.ctx.drainPending();
1167
+ if (this.ctx.abortController.signal.aborted) {
1168
+ runMgr.setStopReason('cancelled');
1169
+ runMgr.markCancelled();
1170
+ break;
1171
+ }
1172
+ this.publishStructuredOutput();
1173
+ runMgr.setStopReason('end_turn');
1004
1174
  break;
1005
1175
  }
1006
1176
  // A tool the author declared terminal settles the run with its
@@ -1406,18 +1576,39 @@ export class IterationOrchestrator {
1406
1576
  * message becomes the reason, so an operator is not left with a run
1407
1577
  * that stopped and no account of it.
1408
1578
  */
1579
+ stepContext(stepNumber, prepared) {
1580
+ const model = prepared.model ?? this.ctx.runConfig.model;
1581
+ const window = resolveContextWindow(this.ctx.compactionConfig?.contextWindowTokens, model, model === this.ctx.runConfig.model
1582
+ ? this.ctx.providerContextWindow
1583
+ : model === this.ctx.contextModel
1584
+ ? this.ctx.activeProviderContextWindow
1585
+ : undefined);
1586
+ const skills = prepared.skills ? renderSkillsSection([...prepared.skills]) : null;
1587
+ const preamble = [prepared.system, skills].filter(Boolean).join('\n\n');
1588
+ const preparedTokens = preamble ? estimateMessageTokens(createSystemMessage(preamble)) : 0;
1589
+ const responseReserve = Math.min(prepared.maxResponseTokens ??
1590
+ this.ctx.runConfig.maxResponseTokens ??
1591
+ Math.floor(window.tokens / 4), Math.floor(window.tokens / 4));
1592
+ return {
1593
+ runId: this.ctx.runMgr.id,
1594
+ stepNumber,
1595
+ messages: this.ctx.runMgr.messages,
1596
+ ...(this.latestUserMessage ? { latestUserMessage: this.latestUserMessage } : {}),
1597
+ signal: this.ctx.abortController.signal,
1598
+ contextBudget: {
1599
+ windowTokens: window.tokens,
1600
+ remainingTokens: Math.max(0, Math.floor(window.tokens - measureContext(this.ctx).tokens - preparedTokens - responseReserve)),
1601
+ },
1602
+ steps: this.steps,
1603
+ prepared,
1604
+ };
1605
+ }
1409
1606
  async beforeStep(stepNumber) {
1410
1607
  const configured = this.ctx.beforeStep;
1411
1608
  if (!configured)
1412
1609
  return undefined;
1413
1610
  try {
1414
- return ((await configured({
1415
- runId: this.ctx.runMgr.id,
1416
- stepNumber,
1417
- messages: this.ctx.runMgr.messages,
1418
- steps: this.steps,
1419
- prepared: {},
1420
- })) ?? undefined);
1611
+ return (await configured(this.stepContext(stepNumber, {}))) ?? undefined;
1421
1612
  }
1422
1613
  catch (err) {
1423
1614
  return { reason: `beforeStep threw: ${toErrorMessage(err)}` };
@@ -1435,15 +1626,10 @@ export class IterationOrchestrator {
1435
1626
  let result = {};
1436
1627
  for (const stage of stages) {
1437
1628
  try {
1438
- const decided = await stage({
1439
- runId: this.ctx.runMgr.id,
1440
- stepNumber,
1441
- messages: this.ctx.runMgr.messages,
1442
- steps: this.steps,
1443
- prepared: result,
1444
- });
1629
+ const decided = await stage(this.stepContext(stepNumber, result));
1445
1630
  if (decided)
1446
1631
  result = { ...result, ...decided };
1632
+ await this.selectContextModel(result.model ?? this.ctx.runConfig.model);
1447
1633
  }
1448
1634
  catch (err) {
1449
1635
  // Skipped, and the rest still run: one broken concern must
@@ -1498,6 +1684,17 @@ export class IterationOrchestrator {
1498
1684
  }
1499
1685
  return prepared;
1500
1686
  }
1687
+ async selectContextModel(model) {
1688
+ if (model !== (this.ctx.contextModel ?? this.ctx.runConfig.model)) {
1689
+ // A measurement from another tokenizer cannot price the new request.
1690
+ this.ctx.runMgr.clearLastPromptTokens();
1691
+ }
1692
+ this.ctx.contextModel = model;
1693
+ this.ctx.activeProviderContextWindow =
1694
+ model && model !== this.ctx.runConfig.model && !this.ctx.compactionConfig?.contextWindowTokens
1695
+ ? await this.ctx.resolveModelContextWindow?.(model)
1696
+ : undefined;
1697
+ }
1501
1698
  /** Steps completed so far, exposed on the returned `Run`. */
1502
1699
  steps = [];
1503
1700
  getSteps() {
@@ -1524,16 +1721,32 @@ export class IterationOrchestrator {
1524
1721
  * empty drain must change nothing at all: a `continue` on nothing queued
1525
1722
  * spends an iteration and a model call to say the same thing again.
1526
1723
  */
1724
+ rememberUserMessage(message, arriving = true) {
1725
+ if (!isOperatorUserMessage(message))
1726
+ return;
1727
+ this.latestUserMessage = message;
1728
+ // The hook field alone does not reach the model. Preserve arrivals in
1729
+ // the compaction state too, before their original message or attached
1730
+ // tool result can be shed. Initial history was already extracted at seed.
1731
+ const manager = this.ctx.workingStateManager;
1732
+ if (arriving && manager) {
1733
+ extractFromUserMessage(manager, message.content, !manager.getState().task);
1734
+ }
1735
+ }
1527
1736
  deliverInbound() {
1528
1737
  const queued = this.ctx.inboundMessages?.() ?? [];
1529
- for (const message of queued)
1738
+ for (const message of queued) {
1530
1739
  this.ctx.runMgr.pushMessage(message);
1740
+ this.rememberUserMessage(message);
1741
+ }
1531
1742
  // The steering channel's remainder. `attachSteering` already took
1532
1743
  // what it could carry on a tool result; anything still pending is
1533
1744
  // guidance from a turn that had no result to attach it to.
1534
1745
  const stranded = this.ctx.steering?.drain();
1535
1746
  if (stranded) {
1536
- this.ctx.runMgr.pushMessage(createRuntimeContextMessage(formatSteeringNote(stranded), 'steering'));
1747
+ const message = createRuntimeContextMessage(formatSteeringNote(stranded), 'steering');
1748
+ this.ctx.runMgr.pushMessage(message);
1749
+ this.rememberUserMessage(createRuntimeContextMessage(stranded, 'steering'));
1537
1750
  }
1538
1751
  return queued.length + (stranded ? 1 : 0);
1539
1752
  }
@@ -1611,7 +1824,10 @@ export class IterationOrchestrator {
1611
1824
  }
1612
1825
  /** Turns spent asking the model again for a valid structured output. */
1613
1826
  structuredOutputAttempts = 0;
1827
+ nativeStructuredAttempts = 0;
1614
1828
  structuredOutputDone = false;
1829
+ pendingStructuredOutput;
1830
+ structuredReviewAttempts = 0;
1615
1831
  structuredOutputRetryLimit() {
1616
1832
  return this.ctx.structuredOutput?.maxRetries ?? DEFAULT_STRUCTURED_OUTPUT_RETRIES;
1617
1833
  }
@@ -1632,6 +1848,8 @@ export class IterationOrchestrator {
1632
1848
  * avoid.
1633
1849
  */
1634
1850
  terminalToolOutput(results, response) {
1851
+ if (this.ctx.structuredOutput?.mode === 'native')
1852
+ return undefined;
1635
1853
  const terminal = results.filter((r) => this.ctx.tools.get(r.toolName)?.terminal === true);
1636
1854
  if (terminal.length === 0)
1637
1855
  return undefined;
@@ -1698,30 +1916,98 @@ export class IterationOrchestrator {
1698
1916
  * doing work, and it is the bound the neighbour relies on for the
1699
1917
  * identical pathology.
1700
1918
  */
1701
- captureStructuredOutput(results, response) {
1702
- if (!this.needsStructuredOutput())
1703
- return false;
1919
+ async captureStructuredOutput(results, response) {
1920
+ if (!this.needsStructuredOutput() || this.ctx.structuredOutput?.mode === 'native')
1921
+ return 'absent';
1704
1922
  const hit = results.find((r) => r.toolName === STRUCTURED_OUTPUT_TOOL_NAME && !r.isError);
1705
1923
  if (!hit)
1706
- return false;
1924
+ return 'absent';
1707
1925
  const callCount = response.message.toolCalls?.length ?? 0;
1708
1926
  if (callCount > 1) {
1709
1927
  this.ctx.log.info('Structured output shared its turn — relaying instead of settling', {
1710
1928
  [NAMZU.RUN_ID]: this.ctx.runMgr.id,
1711
1929
  'namzu.runtime.calls_in_turn': callCount,
1712
1930
  });
1713
- return false;
1931
+ return 'absent';
1714
1932
  }
1933
+ let parsed;
1715
1934
  try {
1716
- this.ctx.runMgr.setStructuredOutput(JSON.parse(hit.output));
1935
+ parsed = JSON.parse(hit.output);
1717
1936
  }
1718
1937
  catch {
1719
1938
  // The tool serializes its own validated input, so this is
1720
1939
  // unreachable in practice; keep the raw text rather than losing it.
1721
- this.ctx.runMgr.setStructuredOutput(hit.output);
1940
+ if (this.ctx.structuredOutput?.review)
1941
+ throw new Error('Structured review requires an intact JSON tool result; check tool-output limits and result transformations');
1942
+ parsed = hit.output;
1722
1943
  }
1944
+ return this.reviewStructuredOutput(parsed);
1945
+ }
1946
+ publishStructuredOutput() {
1947
+ this.ctx.runMgr.setStructuredOutput(this.pendingStructuredOutput);
1723
1948
  this.structuredOutputDone = true;
1724
- return true;
1949
+ }
1950
+ async reviewStructuredOutput(parsed) {
1951
+ if (this.ctx.abortController.signal.aborted)
1952
+ return 'cancelled';
1953
+ const reviewer = this.ctx.structuredOutput?.review;
1954
+ if (reviewer) {
1955
+ const signal = this.ctx.abortController.signal;
1956
+ if (signal.aborted)
1957
+ return 'cancelled';
1958
+ let onAbort = () => { };
1959
+ const aborted = new Promise((_resolve, reject) => {
1960
+ onAbort = () => reject(signal.reason ?? new Error('Structured review cancelled'));
1961
+ signal.addEventListener('abort', onAbort, { once: true });
1962
+ });
1963
+ let verdict;
1964
+ try {
1965
+ verdict = await Promise.race([
1966
+ Promise.resolve().then(() => reviewer(structuredClone(parsed), {
1967
+ runId: this.ctx.runMgr.id,
1968
+ iteration: this.ctx.runMgr.currentIteration,
1969
+ signal,
1970
+ messages: this.ctx.runMgr.messages,
1971
+ })),
1972
+ aborted,
1973
+ ]);
1974
+ }
1975
+ catch (error) {
1976
+ if (signal.aborted)
1977
+ return 'cancelled';
1978
+ throw error;
1979
+ }
1980
+ finally {
1981
+ signal.removeEventListener('abort', onAbort);
1982
+ }
1983
+ if (signal.aborted)
1984
+ return 'cancelled';
1985
+ if (!verdict || typeof verdict.accept !== 'boolean')
1986
+ throw new Error('Structured reviewer returned an invalid verdict');
1987
+ if (!verdict.accept) {
1988
+ if (typeof verdict.feedback !== 'string' || verdict.feedback.trim().length === 0)
1989
+ throw new Error('Structured reviewer rejection requires feedback');
1990
+ this.structuredReviewAttempts++;
1991
+ this.ctx.runMgr.pushMessage(createRuntimeContextMessage(verdict.feedback, 'answer-review'));
1992
+ // Persist both the feedback and its consumed allowance before the
1993
+ // next request. This checkpoint is not an approval/park boundary.
1994
+ const checkpoint = await this.ctx.checkpointMgr.create(this.ctx.runMgr, this.ctx.runMgr.currentIteration);
1995
+ await this.ctx.emitEvent({
1996
+ type: 'checkpoint_created',
1997
+ runId: this.ctx.runMgr.id,
1998
+ checkpointId: checkpoint.id,
1999
+ iteration: this.ctx.runMgr.currentIteration,
2000
+ });
2001
+ if (signal.aborted)
2002
+ return 'cancelled';
2003
+ return this.structuredReviewAttempts >
2004
+ (this.ctx.structuredOutput?.maxReviews ?? DEFAULT_ANSWER_REVIEW_LIMIT)
2005
+ ? 'exhausted'
2006
+ : 'retry';
2007
+ }
2008
+ }
2009
+ this.pendingStructuredOutput = parsed;
2010
+ return 'accepted';
1725
2011
  }
1726
2012
  /**
1727
2013
  * Ask the host whether this answer is good enough.
@@ -1742,6 +2028,7 @@ export class IterationOrchestrator {
1742
2028
  return await this.ctx.reviewAnswer(answer, {
1743
2029
  runId: this.ctx.runMgr.id,
1744
2030
  iteration: this.ctx.runMgr.currentIteration,
2031
+ signal: this.ctx.abortController.signal,
1745
2032
  messages: this.ctx.runMgr.messages,
1746
2033
  });
1747
2034
  }
@@ -1801,6 +2088,8 @@ export class IterationOrchestrator {
1801
2088
  });
1802
2089
  }
1803
2090
  async requestFinalResponse(model, reason) {
2091
+ if (this.ctx.structuredOutput?.mode === 'native')
2092
+ return 'structured_output_failed';
1804
2093
  const lastAssistant = [...this.ctx.runMgr.messages]
1805
2094
  .reverse()
1806
2095
  .find((m) => m.role === 'assistant');