agent-nuvira 3.3.1 → 3.3.4

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 (466) hide show
  1. package/README.md +18 -13
  2. package/dist/agent-sdk/src/agent.d.ts +2 -0
  3. package/dist/agent-sdk/src/agent.d.ts.map +1 -1
  4. package/dist/agent-sdk/src/define.d.ts +64 -0
  5. package/dist/agent-sdk/src/define.d.ts.map +1 -0
  6. package/dist/agent-sdk/src/define.js +76 -0
  7. package/dist/agent-sdk/src/define.js.map +1 -0
  8. package/dist/agent-sdk/src/index.d.ts +9 -0
  9. package/dist/agent-sdk/src/index.d.ts.map +1 -1
  10. package/dist/agent-sdk/src/index.js +9 -0
  11. package/dist/agent-sdk/src/index.js.map +1 -1
  12. package/dist/agent-sdk/src/scaffold.d.ts +11 -0
  13. package/dist/agent-sdk/src/scaffold.d.ts.map +1 -1
  14. package/dist/agent-sdk/src/scaffold.js +16 -6
  15. package/dist/agent-sdk/src/scaffold.js.map +1 -1
  16. package/dist/agents/agents/runner.d.ts +9 -0
  17. package/dist/agents/agents/runner.d.ts.map +1 -1
  18. package/dist/agents/agents/runner.js +34 -12
  19. package/dist/agents/agents/runner.js.map +1 -1
  20. package/dist/agents/agents/writer-tool-calling.d.ts.map +1 -1
  21. package/dist/agents/agents/writer-tool-calling.js +7 -2
  22. package/dist/agents/agents/writer-tool-calling.js.map +1 -1
  23. package/dist/agents/agents/writer.d.ts.map +1 -1
  24. package/dist/agents/agents/writer.js +7 -1
  25. package/dist/agents/agents/writer.js.map +1 -1
  26. package/dist/agents/artifact-verification.d.ts +102 -0
  27. package/dist/agents/artifact-verification.d.ts.map +1 -0
  28. package/dist/agents/artifact-verification.js +216 -0
  29. package/dist/agents/artifact-verification.js.map +1 -0
  30. package/dist/agents/checkpoint-store.d.ts +73 -0
  31. package/dist/agents/checkpoint-store.d.ts.map +1 -1
  32. package/dist/agents/checkpoint-store.js +161 -0
  33. package/dist/agents/checkpoint-store.js.map +1 -1
  34. package/dist/agents/credential-store.d.ts +120 -0
  35. package/dist/agents/credential-store.d.ts.map +1 -1
  36. package/dist/agents/credential-store.js +358 -19
  37. package/dist/agents/credential-store.js.map +1 -1
  38. package/dist/agents/long-form-plan.d.ts.map +1 -1
  39. package/dist/agents/long-form-plan.js +2 -1
  40. package/dist/agents/long-form-plan.js.map +1 -1
  41. package/dist/agents/orchestrator.d.ts +10 -1
  42. package/dist/agents/orchestrator.d.ts.map +1 -1
  43. package/dist/agents/orchestrator.js +239 -79
  44. package/dist/agents/orchestrator.js.map +1 -1
  45. package/dist/agents/phase-engine.d.ts +61 -0
  46. package/dist/agents/phase-engine.d.ts.map +1 -1
  47. package/dist/agents/phase-engine.js +68 -2
  48. package/dist/agents/phase-engine.js.map +1 -1
  49. package/dist/agents/prompt-assembly.d.ts +11 -0
  50. package/dist/agents/prompt-assembly.d.ts.map +1 -1
  51. package/dist/agents/prompt-assembly.js +17 -0
  52. package/dist/agents/prompt-assembly.js.map +1 -1
  53. package/dist/agents/release-preflight.d.ts +121 -0
  54. package/dist/agents/release-preflight.d.ts.map +1 -0
  55. package/dist/agents/release-preflight.js +259 -0
  56. package/dist/agents/release-preflight.js.map +1 -0
  57. package/dist/agents/release-runner.d.ts +157 -0
  58. package/dist/agents/release-runner.d.ts.map +1 -0
  59. package/dist/agents/release-runner.js +719 -0
  60. package/dist/agents/release-runner.js.map +1 -0
  61. package/dist/agents/step-handoff.d.ts +190 -0
  62. package/dist/agents/step-handoff.d.ts.map +1 -0
  63. package/dist/agents/step-handoff.js +443 -0
  64. package/dist/agents/step-handoff.js.map +1 -0
  65. package/dist/cli/agent.d.ts +2 -2
  66. package/dist/cli/agent.js +10 -10
  67. package/dist/cli/benchmark.d.ts.map +1 -1
  68. package/dist/cli/benchmark.js +11 -4
  69. package/dist/cli/benchmark.js.map +1 -1
  70. package/dist/cli/chat.d.ts +94 -0
  71. package/dist/cli/chat.d.ts.map +1 -1
  72. package/dist/cli/chat.js +379 -24
  73. package/dist/cli/chat.js.map +1 -1
  74. package/dist/cli/cli-program.d.ts.map +1 -1
  75. package/dist/cli/cli-program.js +8 -0
  76. package/dist/cli/cli-program.js.map +1 -1
  77. package/dist/cli/config.d.ts.map +1 -1
  78. package/dist/cli/config.js +9 -1
  79. package/dist/cli/config.js.map +1 -1
  80. package/dist/cli/credentials.d.ts +28 -0
  81. package/dist/cli/credentials.d.ts.map +1 -0
  82. package/dist/cli/credentials.js +213 -0
  83. package/dist/cli/credentials.js.map +1 -0
  84. package/dist/cli/doctor.d.ts.map +1 -1
  85. package/dist/cli/doctor.js +3 -2
  86. package/dist/cli/doctor.js.map +1 -1
  87. package/dist/cli/edit.js +2 -2
  88. package/dist/cli/eval.d.ts +17 -0
  89. package/dist/cli/eval.d.ts.map +1 -1
  90. package/dist/cli/eval.js +105 -2
  91. package/dist/cli/eval.js.map +1 -1
  92. package/dist/cli/execute.d.ts +16 -1
  93. package/dist/cli/execute.d.ts.map +1 -1
  94. package/dist/cli/execute.js +192 -22
  95. package/dist/cli/execute.js.map +1 -1
  96. package/dist/cli/failover-runner.d.ts.map +1 -1
  97. package/dist/cli/failover-runner.js +9 -3
  98. package/dist/cli/failover-runner.js.map +1 -1
  99. package/dist/cli/loop-executor.d.ts +55 -0
  100. package/dist/cli/loop-executor.d.ts.map +1 -1
  101. package/dist/cli/loop-executor.js +285 -19
  102. package/dist/cli/loop-executor.js.map +1 -1
  103. package/dist/cli/model.d.ts.map +1 -1
  104. package/dist/cli/model.js +9 -8
  105. package/dist/cli/model.js.map +1 -1
  106. package/dist/cli/models.d.ts +1 -1
  107. package/dist/cli/models.js +4 -4
  108. package/dist/cli/nlu.d.ts.map +1 -1
  109. package/dist/cli/nlu.js +8 -2
  110. package/dist/cli/nlu.js.map +1 -1
  111. package/dist/cli/parity.d.ts +85 -0
  112. package/dist/cli/parity.d.ts.map +1 -0
  113. package/dist/cli/parity.js +506 -0
  114. package/dist/cli/parity.js.map +1 -0
  115. package/dist/cli/plan.d.ts.map +1 -1
  116. package/dist/cli/plan.js +2 -1
  117. package/dist/cli/plan.js.map +1 -1
  118. package/dist/cli/publish.d.ts +22 -0
  119. package/dist/cli/publish.d.ts.map +1 -1
  120. package/dist/cli/publish.js +109 -13
  121. package/dist/cli/publish.js.map +1 -1
  122. package/dist/cli/retrieval.d.ts.map +1 -1
  123. package/dist/cli/retrieval.js +5 -4
  124. package/dist/cli/retrieval.js.map +1 -1
  125. package/dist/cli/sdk.js +4 -4
  126. package/dist/cli/sdk.js.map +1 -1
  127. package/dist/cli/trace.d.ts.map +1 -1
  128. package/dist/cli/trace.js +2 -1
  129. package/dist/cli/trace.js.map +1 -1
  130. package/dist/cli/workflow.js +2 -2
  131. package/dist/cli/workflow.js.map +1 -1
  132. package/dist/config/live-credentials.d.ts +57 -0
  133. package/dist/config/live-credentials.d.ts.map +1 -0
  134. package/dist/config/live-credentials.js +126 -0
  135. package/dist/config/live-credentials.js.map +1 -0
  136. package/dist/config/paths.d.ts +18 -0
  137. package/dist/config/paths.d.ts.map +1 -1
  138. package/dist/config/paths.js +25 -0
  139. package/dist/config/paths.js.map +1 -1
  140. package/dist/config/types.d.ts +44 -0
  141. package/dist/config/types.d.ts.map +1 -1
  142. package/dist/federation/a2a-types.js +1 -1
  143. package/dist/federation/a2a-types.js.map +1 -1
  144. package/dist/findings/verdicts.d.ts +241 -0
  145. package/dist/findings/verdicts.d.ts.map +1 -0
  146. package/dist/findings/verdicts.js +284 -0
  147. package/dist/findings/verdicts.js.map +1 -0
  148. package/dist/gateway/adapters.d.ts +65 -0
  149. package/dist/gateway/adapters.d.ts.map +1 -1
  150. package/dist/gateway/adapters.js +216 -10
  151. package/dist/gateway/adapters.js.map +1 -1
  152. package/dist/gateway/channel-directory.d.ts +31 -0
  153. package/dist/gateway/channel-directory.d.ts.map +1 -1
  154. package/dist/gateway/channel-directory.js +40 -0
  155. package/dist/gateway/channel-directory.js.map +1 -1
  156. package/dist/gateway/gateway-log.d.ts +1 -1
  157. package/dist/gateway/gateway-log.d.ts.map +1 -1
  158. package/dist/gateway/gateway-log.js.map +1 -1
  159. package/dist/gateway/hooks.d.ts +87 -19
  160. package/dist/gateway/hooks.d.ts.map +1 -1
  161. package/dist/gateway/hooks.js +62 -23
  162. package/dist/gateway/hooks.js.map +1 -1
  163. package/dist/gateway/inbound-media.d.ts +147 -0
  164. package/dist/gateway/inbound-media.d.ts.map +1 -0
  165. package/dist/gateway/inbound-media.js +317 -0
  166. package/dist/gateway/inbound-media.js.map +1 -0
  167. package/dist/gateway/inbox.d.ts +8 -1
  168. package/dist/gateway/inbox.d.ts.map +1 -1
  169. package/dist/gateway/inbox.js.map +1 -1
  170. package/dist/gateway/platform-config.d.ts +14 -0
  171. package/dist/gateway/platform-config.d.ts.map +1 -1
  172. package/dist/gateway/platform-config.js +26 -8
  173. package/dist/gateway/platform-config.js.map +1 -1
  174. package/dist/gateway/realtime.d.ts +114 -0
  175. package/dist/gateway/realtime.d.ts.map +1 -0
  176. package/dist/gateway/realtime.js +402 -0
  177. package/dist/gateway/realtime.js.map +1 -0
  178. package/dist/gateway/registry.d.ts +31 -0
  179. package/dist/gateway/registry.d.ts.map +1 -1
  180. package/dist/gateway/registry.js +224 -4
  181. package/dist/gateway/registry.js.map +1 -1
  182. package/dist/gateway/whatsapp/baileys-bridge.d.ts +11 -1
  183. package/dist/gateway/whatsapp/baileys-bridge.d.ts.map +1 -1
  184. package/dist/gateway/whatsapp/baileys-bridge.js +123 -4
  185. package/dist/gateway/whatsapp/baileys-bridge.js.map +1 -1
  186. package/dist/gateway/whatsapp/bridge.d.ts +6 -2
  187. package/dist/gateway/whatsapp/bridge.d.ts.map +1 -1
  188. package/dist/gateway/whatsapp/bridge.js.map +1 -1
  189. package/dist/index.js +8 -0
  190. package/dist/index.js.map +1 -1
  191. package/dist/inference/factory.d.ts +14 -0
  192. package/dist/inference/factory.d.ts.map +1 -1
  193. package/dist/inference/factory.js +17 -0
  194. package/dist/inference/factory.js.map +1 -1
  195. package/dist/inference/groq-adapter.d.ts +2 -0
  196. package/dist/inference/groq-adapter.d.ts.map +1 -1
  197. package/dist/inference/groq-adapter.js +16 -6
  198. package/dist/inference/groq-adapter.js.map +1 -1
  199. package/dist/inference/model-validator.d.ts +6 -1
  200. package/dist/inference/model-validator.d.ts.map +1 -1
  201. package/dist/inference/model-validator.js +7 -2
  202. package/dist/inference/model-validator.js.map +1 -1
  203. package/dist/inference/route-resolver.d.ts +116 -0
  204. package/dist/inference/route-resolver.d.ts.map +1 -0
  205. package/dist/inference/route-resolver.js +159 -0
  206. package/dist/inference/route-resolver.js.map +1 -0
  207. package/dist/inference/tools.d.ts.map +1 -1
  208. package/dist/inference/tools.js +29 -0
  209. package/dist/inference/tools.js.map +1 -1
  210. package/dist/learning/autonomy-policy.d.ts +18 -0
  211. package/dist/learning/autonomy-policy.d.ts.map +1 -1
  212. package/dist/learning/autonomy-policy.js +35 -0
  213. package/dist/learning/autonomy-policy.js.map +1 -1
  214. package/dist/learning/benchmark.d.ts.map +1 -1
  215. package/dist/learning/benchmark.js +3 -2
  216. package/dist/learning/benchmark.js.map +1 -1
  217. package/dist/learning/continuation.d.ts.map +1 -1
  218. package/dist/learning/continuation.js +2 -1
  219. package/dist/learning/continuation.js.map +1 -1
  220. package/dist/learning/cost-tracker.d.ts.map +1 -1
  221. package/dist/learning/cost-tracker.js +2 -1
  222. package/dist/learning/cost-tracker.js.map +1 -1
  223. package/dist/learning/deferred-task.d.ts.map +1 -1
  224. package/dist/learning/deferred-task.js +13 -4
  225. package/dist/learning/deferred-task.js.map +1 -1
  226. package/dist/learning/eval-framework.d.ts.map +1 -1
  227. package/dist/learning/eval-framework.js +2 -1
  228. package/dist/learning/eval-framework.js.map +1 -1
  229. package/dist/learning/long-form.d.ts.map +1 -1
  230. package/dist/learning/long-form.js +2 -1
  231. package/dist/learning/long-form.js.map +1 -1
  232. package/dist/learning/model-registry.d.ts.map +1 -1
  233. package/dist/learning/model-registry.js +2 -1
  234. package/dist/learning/model-registry.js.map +1 -1
  235. package/dist/learning/reasoning-cache.d.ts.map +1 -1
  236. package/dist/learning/reasoning-cache.js +2 -1
  237. package/dist/learning/reasoning-cache.js.map +1 -1
  238. package/dist/learning/reasoning-trace.d.ts +38 -2
  239. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  240. package/dist/learning/reasoning-trace.js +91 -1
  241. package/dist/learning/reasoning-trace.js.map +1 -1
  242. package/dist/learning/resilient-call.d.ts.map +1 -1
  243. package/dist/learning/resilient-call.js +31 -18
  244. package/dist/learning/resilient-call.js.map +1 -1
  245. package/dist/learning/retrieval.d.ts.map +1 -1
  246. package/dist/learning/retrieval.js +2 -1
  247. package/dist/learning/retrieval.js.map +1 -1
  248. package/dist/learning/seeded-benchmark.d.ts +160 -0
  249. package/dist/learning/seeded-benchmark.d.ts.map +1 -0
  250. package/dist/learning/seeded-benchmark.js +321 -0
  251. package/dist/learning/seeded-benchmark.js.map +1 -0
  252. package/dist/learning/seeded-bugs.d.ts +142 -0
  253. package/dist/learning/seeded-bugs.d.ts.map +1 -0
  254. package/dist/learning/seeded-bugs.js +535 -0
  255. package/dist/learning/seeded-bugs.js.map +1 -0
  256. package/dist/learning/skill-store.d.ts.map +1 -1
  257. package/dist/learning/skill-store.js +30 -9
  258. package/dist/learning/skill-store.js.map +1 -1
  259. package/dist/learning/step-checkpoint.d.ts +127 -0
  260. package/dist/learning/step-checkpoint.d.ts.map +1 -0
  261. package/dist/learning/step-checkpoint.js +244 -0
  262. package/dist/learning/step-checkpoint.js.map +1 -0
  263. package/dist/mcp/catalog.js +2 -3
  264. package/dist/mcp/catalog.js.map +1 -1
  265. package/dist/mcp/manager.d.ts.map +1 -1
  266. package/dist/mcp/manager.js +5 -3
  267. package/dist/mcp/manager.js.map +1 -1
  268. package/dist/nlu/intent-confirm.d.ts +23 -1
  269. package/dist/nlu/intent-confirm.d.ts.map +1 -1
  270. package/dist/nlu/intent-confirm.js +85 -2
  271. package/dist/nlu/intent-confirm.js.map +1 -1
  272. package/dist/nlu/learnings.d.ts +7 -0
  273. package/dist/nlu/learnings.d.ts.map +1 -1
  274. package/dist/nlu/learnings.js +7 -0
  275. package/dist/nlu/learnings.js.map +1 -1
  276. package/dist/observability/debug-log.d.ts +250 -0
  277. package/dist/observability/debug-log.d.ts.map +1 -0
  278. package/dist/observability/debug-log.js +500 -0
  279. package/dist/observability/debug-log.js.map +1 -0
  280. package/dist/observability/event-bus.d.ts.map +1 -1
  281. package/dist/observability/event-bus.js +4 -1
  282. package/dist/observability/event-bus.js.map +1 -1
  283. package/dist/observability/otel.d.ts +278 -0
  284. package/dist/observability/otel.d.ts.map +1 -0
  285. package/dist/observability/otel.js +590 -0
  286. package/dist/observability/otel.js.map +1 -0
  287. package/dist/parity/drivers.d.ts +99 -0
  288. package/dist/parity/drivers.d.ts.map +1 -0
  289. package/dist/parity/drivers.js +1362 -0
  290. package/dist/parity/drivers.js.map +1 -0
  291. package/dist/parity/graph.d.ts +73 -0
  292. package/dist/parity/graph.d.ts.map +1 -0
  293. package/dist/parity/graph.js +162 -0
  294. package/dist/parity/graph.js.map +1 -0
  295. package/dist/parity/matrix.d.ts +105 -0
  296. package/dist/parity/matrix.d.ts.map +1 -0
  297. package/dist/parity/matrix.js +352 -0
  298. package/dist/parity/matrix.js.map +1 -0
  299. package/dist/parity/observation.d.ts +444 -0
  300. package/dist/parity/observation.d.ts.map +1 -0
  301. package/dist/parity/observation.js +333 -0
  302. package/dist/parity/observation.js.map +1 -0
  303. package/dist/parity/scenarios.d.ts +229 -0
  304. package/dist/parity/scenarios.d.ts.map +1 -0
  305. package/dist/parity/scenarios.js +175 -0
  306. package/dist/parity/scenarios.js.map +1 -0
  307. package/dist/parity/surfaces.d.ts +122 -0
  308. package/dist/parity/surfaces.d.ts.map +1 -0
  309. package/dist/parity/surfaces.js +190 -0
  310. package/dist/parity/surfaces.js.map +1 -0
  311. package/dist/runtime/fault-injection.d.ts +173 -0
  312. package/dist/runtime/fault-injection.d.ts.map +1 -0
  313. package/dist/runtime/fault-injection.js +281 -0
  314. package/dist/runtime/fault-injection.js.map +1 -0
  315. package/dist/skills/secret-capture.d.ts.map +1 -1
  316. package/dist/skills/secret-capture.js +9 -4
  317. package/dist/skills/secret-capture.js.map +1 -1
  318. package/dist/tools/child-agent-entry.d.ts +23 -0
  319. package/dist/tools/child-agent-entry.d.ts.map +1 -0
  320. package/dist/tools/child-agent-entry.js +129 -0
  321. package/dist/tools/child-agent-entry.js.map +1 -0
  322. package/dist/tools/child-agent-runtime.d.ts +124 -0
  323. package/dist/tools/child-agent-runtime.d.ts.map +1 -0
  324. package/dist/tools/child-agent-runtime.js +704 -0
  325. package/dist/tools/child-agent-runtime.js.map +1 -0
  326. package/dist/tools/coding-tools.d.ts.map +1 -1
  327. package/dist/tools/coding-tools.js +82 -10
  328. package/dist/tools/coding-tools.js.map +1 -1
  329. package/dist/tools/credentials-tool.d.ts +24 -0
  330. package/dist/tools/credentials-tool.d.ts.map +1 -0
  331. package/dist/tools/credentials-tool.js +125 -0
  332. package/dist/tools/credentials-tool.js.map +1 -0
  333. package/dist/tools/delegation-system.d.ts +31 -0
  334. package/dist/tools/delegation-system.d.ts.map +1 -1
  335. package/dist/tools/delegation-system.js +70 -9
  336. package/dist/tools/delegation-system.js.map +1 -1
  337. package/dist/tools/extract/docx.d.ts +27 -0
  338. package/dist/tools/extract/docx.d.ts.map +1 -0
  339. package/dist/tools/extract/docx.js +48 -0
  340. package/dist/tools/extract/docx.js.map +1 -0
  341. package/dist/tools/extract/html-text.d.ts +27 -0
  342. package/dist/tools/extract/html-text.d.ts.map +1 -0
  343. package/dist/tools/extract/html-text.js +86 -0
  344. package/dist/tools/extract/html-text.js.map +1 -0
  345. package/dist/tools/extract/pdf-ocr.d.ts +58 -0
  346. package/dist/tools/extract/pdf-ocr.d.ts.map +1 -0
  347. package/dist/tools/extract/pdf-ocr.js +116 -0
  348. package/dist/tools/extract/pdf-ocr.js.map +1 -0
  349. package/dist/tools/extract/pdf.d.ts +65 -0
  350. package/dist/tools/extract/pdf.d.ts.map +1 -0
  351. package/dist/tools/extract/pdf.js +197 -0
  352. package/dist/tools/extract/pdf.js.map +1 -0
  353. package/dist/tools/extract/pptx.d.ts +32 -0
  354. package/dist/tools/extract/pptx.d.ts.map +1 -0
  355. package/dist/tools/extract/pptx.js +77 -0
  356. package/dist/tools/extract/pptx.js.map +1 -0
  357. package/dist/tools/extract/xlsx.d.ts +47 -0
  358. package/dist/tools/extract/xlsx.d.ts.map +1 -0
  359. package/dist/tools/extract/xlsx.js +111 -0
  360. package/dist/tools/extract/xlsx.js.map +1 -0
  361. package/dist/tools/finding-tool.d.ts +76 -0
  362. package/dist/tools/finding-tool.d.ts.map +1 -0
  363. package/dist/tools/finding-tool.js +125 -0
  364. package/dist/tools/finding-tool.js.map +1 -0
  365. package/dist/tools/git-tool.d.ts +25 -7
  366. package/dist/tools/git-tool.d.ts.map +1 -1
  367. package/dist/tools/git-tool.js +155 -15
  368. package/dist/tools/git-tool.js.map +1 -1
  369. package/dist/tools/loop-project-context.d.ts.map +1 -1
  370. package/dist/tools/loop-project-context.js +16 -0
  371. package/dist/tools/loop-project-context.js.map +1 -1
  372. package/dist/tools/loop-route-feed.d.ts +57 -0
  373. package/dist/tools/loop-route-feed.d.ts.map +1 -0
  374. package/dist/tools/loop-route-feed.js +101 -0
  375. package/dist/tools/loop-route-feed.js.map +1 -0
  376. package/dist/tools/messaging-tools.d.ts +41 -11
  377. package/dist/tools/messaging-tools.d.ts.map +1 -1
  378. package/dist/tools/messaging-tools.js +104 -55
  379. package/dist/tools/messaging-tools.js.map +1 -1
  380. package/dist/tools/neutts-synth.d.ts +27 -3
  381. package/dist/tools/neutts-synth.d.ts.map +1 -1
  382. package/dist/tools/neutts-synth.js +57 -13
  383. package/dist/tools/neutts-synth.js.map +1 -1
  384. package/dist/tools/pipeline-tool.d.ts +43 -1
  385. package/dist/tools/pipeline-tool.d.ts.map +1 -1
  386. package/dist/tools/pipeline-tool.js +21 -4
  387. package/dist/tools/pipeline-tool.js.map +1 -1
  388. package/dist/tools/publish-tool.d.ts.map +1 -1
  389. package/dist/tools/publish-tool.js +76 -10
  390. package/dist/tools/publish-tool.js.map +1 -1
  391. package/dist/tools/read-extract.d.ts +116 -45
  392. package/dist/tools/read-extract.d.ts.map +1 -1
  393. package/dist/tools/read-extract.js +494 -158
  394. package/dist/tools/read-extract.js.map +1 -1
  395. package/dist/tools/registry.d.ts +23 -3
  396. package/dist/tools/registry.d.ts.map +1 -1
  397. package/dist/tools/registry.js +196 -29
  398. package/dist/tools/registry.js.map +1 -1
  399. package/dist/tools/run-terminal.d.ts +9 -0
  400. package/dist/tools/run-terminal.d.ts.map +1 -1
  401. package/dist/tools/run-terminal.js +80 -2
  402. package/dist/tools/run-terminal.js.map +1 -1
  403. package/dist/tools/subagent-refusal.d.ts +15 -0
  404. package/dist/tools/subagent-refusal.d.ts.map +1 -0
  405. package/dist/tools/subagent-refusal.js +18 -0
  406. package/dist/tools/subagent-refusal.js.map +1 -0
  407. package/dist/tools/subagent-spawner.d.ts +122 -0
  408. package/dist/tools/subagent-spawner.d.ts.map +1 -1
  409. package/dist/tools/subagent-spawner.js +249 -28
  410. package/dist/tools/subagent-spawner.js.map +1 -1
  411. package/dist/tools/tool-hooks.d.ts +177 -0
  412. package/dist/tools/tool-hooks.d.ts.map +1 -0
  413. package/dist/tools/tool-hooks.js +427 -0
  414. package/dist/tools/tool-hooks.js.map +1 -0
  415. package/dist/tools/tool-loop.d.ts +94 -5
  416. package/dist/tools/tool-loop.d.ts.map +1 -1
  417. package/dist/tools/tool-loop.js +315 -9
  418. package/dist/tools/tool-loop.js.map +1 -1
  419. package/dist/tools/tool-refusal.d.ts +68 -0
  420. package/dist/tools/tool-refusal.d.ts.map +1 -0
  421. package/dist/tools/tool-refusal.js +78 -0
  422. package/dist/tools/tool-refusal.js.map +1 -0
  423. package/dist/tools/toolsets.d.ts +8 -0
  424. package/dist/tools/toolsets.d.ts.map +1 -1
  425. package/dist/tools/toolsets.js +14 -4
  426. package/dist/tools/toolsets.js.map +1 -1
  427. package/dist/tools/vision-tools.d.ts +88 -83
  428. package/dist/tools/vision-tools.d.ts.map +1 -1
  429. package/dist/tools/vision-tools.js +134 -103
  430. package/dist/tools/vision-tools.js.map +1 -1
  431. package/dist/tools/worktree.d.ts +210 -0
  432. package/dist/tools/worktree.d.ts.map +1 -0
  433. package/dist/tools/worktree.js +374 -0
  434. package/dist/tools/worktree.js.map +1 -0
  435. package/dist/utils/format.d.ts +3 -0
  436. package/dist/utils/format.d.ts.map +1 -0
  437. package/dist/utils/format.js +32 -0
  438. package/dist/utils/format.js.map +1 -0
  439. package/dist/web-dashboard/attachment-extract.d.ts +64 -0
  440. package/dist/web-dashboard/attachment-extract.d.ts.map +1 -0
  441. package/dist/web-dashboard/attachment-extract.js +154 -0
  442. package/dist/web-dashboard/attachment-extract.js.map +1 -0
  443. package/dist/web-dashboard/chat-console.d.ts +108 -1
  444. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  445. package/dist/web-dashboard/chat-console.js +36 -0
  446. package/dist/web-dashboard/chat-console.js.map +1 -1
  447. package/dist/web-dashboard/hub-data.d.ts +37 -0
  448. package/dist/web-dashboard/hub-data.d.ts.map +1 -1
  449. package/dist/web-dashboard/hub-data.js +61 -1
  450. package/dist/web-dashboard/hub-data.js.map +1 -1
  451. package/dist/web-dashboard/server.d.ts +14 -0
  452. package/dist/web-dashboard/server.d.ts.map +1 -1
  453. package/dist/web-dashboard/server.js +247 -13
  454. package/dist/web-dashboard/server.js.map +1 -1
  455. package/dist/web-dashboard/src/types.d.ts +196 -48
  456. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  457. package/dist/workflow/registry.js +1 -1
  458. package/dist/workflow/registry.js.map +1 -1
  459. package/package.json +22 -6
  460. package/src/web-dashboard/public/assets/{index-Cyd6tIew.css → index-XJjj2cBX.css} +1 -1
  461. package/src/web-dashboard/public/assets/index-YWF9FpwQ.js +207 -0
  462. package/src/web-dashboard/public/assets/index-YWF9FpwQ.js.map +1 -0
  463. package/src/web-dashboard/public/index.html +2 -2
  464. package/dist/tools/child-agent-worker.js +0 -212
  465. package/src/web-dashboard/public/assets/index-CjvoBhlz.js +0 -207
  466. package/src/web-dashboard/public/assets/index-CjvoBhlz.js.map +0 -1
@@ -16,6 +16,20 @@
16
16
  * blocks after its response text (contract in TOOL_CONTRACT_JSON).
17
17
  */
18
18
  import { getTool, toolJsonSchemas } from './registry.js';
19
+ // WS3 (#25) — the turn's span tree. Null-safe throughout: with export off every
20
+ // helper below is a no-op and no span object is ever built.
21
+ import { TOOL_SPAN_PREFIX, withSpanActive } from '../observability/otel.js';
22
+ // WS4 (#26) — the operator's tool lifecycle hooks. Never able to break a call:
23
+ // `runBeforeToolHooks` always resolves, and a broken hook never vetoes.
24
+ import { runBeforeToolHooks, runToolOutcomeHooks, toolHookRefusalText, } from './tool-hooks.js';
25
+ // WS5 (#27) — the resume ledger. A `StepReplay` is threaded through the turn
26
+ // when the run is a resume; `stepDigest` is over the whole input, so a step whose
27
+ // thread or tool schema changed MISSES and is paid for again (see the module).
28
+ import { stepDigest } from '../learning/step-checkpoint.js';
29
+ // WS6 (#28) — the declared fault seam. `faultAt` is a null check when this
30
+ // process declared no fault (`NUVIRA_INJECT_FAULT`), so an ordinary turn is
31
+ // unaffected.
32
+ import { faultAt } from '../runtime/fault-injection.js';
19
33
  import { detectPermissionSeeking, isAffirmativeReply, replyAsksTheReader, requestAuthorizesWrites, stripTrailingPermissionSeek, } from '../learning/autonomy-policy.js';
20
34
  import { envelopeFromPlan, envelopeFromRequest, getEnvelope, grantEnvelope, isEnvelopeKey, } from '../learning/intent-envelope.js';
21
35
  import { detectProcessComplaint, isTraceKey, repeatNudge, runTraceFor, RunTrace, } from '../learning/run-trace.js';
@@ -23,7 +37,50 @@ import { wantsAuthoredArtifact } from '../learning/deliverable-class.js';
23
37
  import { normalizeFollowups } from './followup-utils.js';
24
38
  import { assessEditActivity, detectUnverifiedEditClaim, isVerificationTool, verificationNudgeFor, THINK_ONLY_ESCALATION, AUTHORIZED_WORK_NUDGE, deliverableNudge, } from './edit-verification.js';
25
39
  import { effectiveToolJsonSchemas, coreToolJsonSchemas, isToolEnabled, toolsetForTool } from './toolsets.js';
40
+ import { deliverablesNamedIn, recordStepHandoff } from '../agents/step-handoff.js';
41
+ /**
42
+ * Tools that MUTATE the workspace. A refusal of one of these is the only
43
+ * refusal that leaves work undone — a declined `read_file` costs a step, a
44
+ * declined `write_file` means the deliverable was never produced.
45
+ */
46
+ const MUTATING_TOOL_NAMES = new Set([
47
+ 'write_file',
48
+ 'edit_file',
49
+ 'propose_change',
50
+ 'run_terminal',
51
+ 'run_cli',
52
+ ]);
53
+ /** The path a mutating call was aimed at, when it named one. */
54
+ function mutatedPathOf(args) {
55
+ const a = args;
56
+ const p = a?.path ?? a?.file_path ?? a?.file;
57
+ return typeof p === 'string' && p.trim() ? p.trim() : undefined;
58
+ }
59
+ /**
60
+ * The ask this turn is answering — the text a hand-off is keyed against.
61
+ * `authorizationRequest` is the loop's own record of the last user message;
62
+ * the thread scan is the fallback for callers that do not set it (tests, direct
63
+ * invocations). Injected blocks (`[Project context]`, the route feed) are
64
+ * skipped: they are the loop's own additions, not the user's words.
65
+ */
66
+ function currentAsk(opts) {
67
+ const recorded = opts.context?.authorizationRequest?.trim();
68
+ if (recorded)
69
+ return recorded.slice(0, 600);
70
+ const messages = opts.messages ?? [];
71
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
72
+ const m = messages[i];
73
+ if (m.role !== 'user')
74
+ continue;
75
+ const text = (m.content ?? '').trim();
76
+ if (!text || text.startsWith('['))
77
+ continue;
78
+ return text.slice(0, 600);
79
+ }
80
+ return '';
81
+ }
26
82
  import { appendToolArtifact } from './artifact-append.js';
83
+ import { ROUTE_FEED_MARKER, isUnresolvedModel, routeFeedFingerprint, routeFeedText } from './loop-route-feed.js';
27
84
  import { logger } from '../utils/logger.js';
28
85
  import { toUserFacingGenerationError, TEXTUAL_TOOL_CALL_HINT, isTextualFollowupsPayload, followupEntriesFromPayload, stripTrailingFollowupsHeader, } from '../inference/tool-call-utils.js';
29
86
  // ─── P3c — tool-fallback hints (switch tools when one fails) ────────────────
@@ -519,6 +576,8 @@ async function runToolLoopInner(opts, progress) {
519
576
  return added;
520
577
  };
521
578
  const thread = [...messages];
579
+ /** Serving pairs the loop has already told the model about, oldest first. */
580
+ const routeFeedState = { pairs: [] };
522
581
  // ── Stage 2: answer a question ABOUT the run from the run ────────────────
523
582
  // The live audit's worst moment was the user asking "why are you asking me
524
583
  // this again and again?" and the turn replying with an edit plan — not through
@@ -546,6 +605,13 @@ async function runToolLoopInner(opts, progress) {
546
605
  // steps, one advisory delegate suggestion fires (bounded, deterministic).
547
606
  const parallel = makeParallelSuggester();
548
607
  let steps = 0;
608
+ /**
609
+ * WS5 (#27) — model calls this run did NOT make because the checkpoint had a
610
+ * recorded answer for the exact same input. Zero on an ordinary run, and
611
+ * reported rather than merely logged: "this turn cost one model call instead of
612
+ * three" is the whole claim of a partial resume.
613
+ */
614
+ let replayedSteps = 0;
549
615
  // The last SUBSTANTIVE answer text. JSON-only steps (a `{"tool":...}` block
550
616
  // with no visible text, common after the model already answered) must NOT
551
617
  // clobber it — otherwise the delivered answer is lost and the turn ends
@@ -570,7 +636,7 @@ async function runToolLoopInner(opts, progress) {
570
636
  // P4 — check cancellation BEFORE each step (a pre-aborted signal never
571
637
  // spends a model call) and after tool executions (below).
572
638
  if (opts.signal?.aborted) {
573
- return { content: '', followups, toolCalls: toolCallsRun, steps, bounded: false, cancelled: true };
639
+ return { content: '', followups, toolCalls: toolCallsRun, steps, ...(opts.resume ? { replayedSteps } : {}), bounded: false, cancelled: true };
574
640
  }
575
641
  steps += 1;
576
642
  // Mechanical thread budget: trim BEFORE the model call so a provider
@@ -584,9 +650,35 @@ async function runToolLoopInner(opts, progress) {
584
650
  deps.onEvent?.(` ✂️ ${trimmedResult.trimmed} old tool result(s) trimmed to fit the ${Math.round(budgetChars / 1000)}K-char context budget.`);
585
651
  }
586
652
  }
653
+ // ── The route feed, re-synced before every call ────────────────────────
654
+ // Placed here rather than at thread construction because THIS is the last
655
+ // point before the call: a failover during step N must be visible at step
656
+ // N+1, and a single up-front line would have the model answering about a
657
+ // model that has since stopped serving it.
658
+ syncRouteFeed(thread, opts.servedRoute, routeFeedState);
587
659
  let response;
588
660
  try {
589
- response = await deps.callModel(thread, schemas, opts.onToken, opts.signal);
661
+ // WS5 (#27) — a resumed run replays this step when its input is unchanged.
662
+ // The key is the step's POSITION and the digest is its whole input, so both
663
+ // have to line up: a plan that shifted by one step replays nothing (the
664
+ // positions hold different inputs), while a re-run of the same plan replays
665
+ // every step whose thread and schema are byte-identical.
666
+ const resumeKey = `model:${steps}`;
667
+ const resumeDigest = opts.resume ? stepDigest(thread, schemas) : '';
668
+ const replayed = opts.resume?.replay(resumeKey, resumeDigest) ?? null;
669
+ if (replayed) {
670
+ response = replayed;
671
+ replayedSteps += 1;
672
+ deps.onEvent?.(` ↩️ step ${steps} replayed from the checkpoint — no model call`);
673
+ }
674
+ else {
675
+ response = await deps.callModel(thread, schemas, opts.onToken, opts.signal);
676
+ // Recorded AFTER the call, and only what the model actually returned: a
677
+ // failed call records nothing, so a resume cannot inherit a failure as an
678
+ // answer.
679
+ if (opts.resume)
680
+ opts.resume.record(resumeKey, resumeDigest, response);
681
+ }
590
682
  // A provider/adapter that resolves with nothing usable (undefined, a
591
683
  // missing content field) must not crash the turn — treat it as this
592
684
  // step's generation failure so the bounded continuation logic below can
@@ -595,13 +687,18 @@ async function runToolLoopInner(opts, progress) {
595
687
  if (!response || typeof response.content !== 'string' || !Array.isArray(response.toolCalls)) {
596
688
  throw new Error('model returned a malformed step response (no content/toolCalls)');
597
689
  }
690
+ // R2 — the transport the caller reported for THIS step. Recorded before
691
+ // anything can return, so a turn that dies mid-way still attributes the
692
+ // transport that served the steps it did complete.
693
+ if (response.transport)
694
+ progress.transport = response.transport;
598
695
  }
599
696
  catch (err) {
600
697
  // P4 — an abort (the dashboard Cancel button) is a clean stop, NOT a
601
698
  // generation failure: the caller discards the turn. No error text, no
602
699
  // fallback — the fetch itself aborted on the caller's signal.
603
700
  if (opts.signal?.aborted) {
604
- return { content: '', followups, toolCalls: toolCallsRun, steps, bounded: false, cancelled: true };
701
+ return { content: '', followups, toolCalls: toolCallsRun, steps, ...(opts.resume ? { replayedSteps } : {}), bounded: false, cancelled: true };
605
702
  }
606
703
  // Generation failure — surface what we have rather than crash the turn.
607
704
  // generationFailed is TRUE only when NOTHING happened yet (no content, no
@@ -663,6 +760,7 @@ async function runToolLoopInner(opts, progress) {
663
760
  followups,
664
761
  toolCalls: toolCallsRun,
665
762
  steps,
763
+ ...(opts.resume ? { replayedSteps } : {}),
666
764
  bounded: false,
667
765
  continuations,
668
766
  generationFailed: delivered === '' || !madeProgress,
@@ -934,6 +1032,7 @@ async function runToolLoopInner(opts, progress) {
934
1032
  followups,
935
1033
  toolCalls: toolCallsRun,
936
1034
  steps,
1035
+ ...(opts.resume ? { replayedSteps } : {}),
937
1036
  bounded: false,
938
1037
  continuations,
939
1038
  };
@@ -1016,31 +1115,117 @@ async function runToolLoopInner(opts, progress) {
1016
1115
  followups.length = 0;
1017
1116
  }
1018
1117
  // I2: emit `tool:started` (before execution) + `tool:called` (after)
1019
- // on the observability bus — drives the hooks registry's
1020
- // the `post_tool_call` hook AND the dashboard's
1118
+ // on the observability bus — drives the dashboard's
1021
1119
  // step cards (P0.6: the GUI renders each call as a live card —
1022
1120
  // running → ok/error with duration + collapsible result). Timing is
1023
1121
  // wall-clock; `ok` mirrors the tool-result convention (Error: prefix).
1024
1122
  const startedAt = Date.now();
1123
+ // WS4 (#26) — the `before` hooks, and the ONE place an operator's veto can
1124
+ // stop a call. It happens before `tool:started` is announced and before a
1125
+ // span is created, so a vetoed call leaves no evidence of having run — and
1126
+ // it is reported as a `called` call with an `Error:` result, so every
1127
+ // surface's tool lifecycle shows the attempt and its outcome rather than an
1128
+ // unexplained gap.
1129
+ const beforeHooks = await runBeforeToolHooks({
1130
+ tool: call.name,
1131
+ args: call.arguments,
1132
+ callId: call.id,
1133
+ ...(opts.surface ? { surface: opts.surface } : {}),
1134
+ ...(ctx.cwd ? { cwd: ctx.cwd } : {}),
1135
+ ...(ctx.configManager ? { configManager: ctx.configManager } : {}),
1136
+ });
1137
+ for (const problem of beforeHooks.problems) {
1138
+ // A hook that failed to run is REPORTED rather than swallowed: the seam
1139
+ // fails open, so without this a broken policy looks exactly like one that
1140
+ // allowed everything.
1141
+ traceEvent({ kind: 'gate', gate: 'tool-hook', tool: call.name, ok: false, summary: problem });
1142
+ }
1143
+ if (beforeHooks.denied) {
1144
+ const refusal = toolHookRefusalText(beforeHooks);
1145
+ traceEvent({
1146
+ kind: 'gate',
1147
+ gate: 'tool-hook',
1148
+ tool: call.name,
1149
+ ok: false,
1150
+ summary: `${beforeHooks.by ?? 'a tool hook'} refused ${call.name}` +
1151
+ (beforeHooks.reason ? `: ${beforeHooks.reason}` : ''),
1152
+ });
1153
+ deps.onEvent?.(` ⛔ ${call.name} — refused by a tool hook`);
1154
+ ctx.emit?.('tool:called', {
1155
+ id: call.id,
1156
+ tool: call.name,
1157
+ ok: false,
1158
+ result: refusal,
1159
+ durationMs: Date.now() - startedAt,
1160
+ });
1161
+ return refusal;
1162
+ }
1163
+ /**
1164
+ * Tell the `after` / `failed` hooks what happened to this call.
1165
+ *
1166
+ * Observe-only by construction: the outcome has already been decided and
1167
+ * reported by the time this runs, so a hook here cannot rewrite history —
1168
+ * the phase it is named for is the fact it receives.
1169
+ */
1170
+ const reportToolOutcome = async (outcome) => {
1171
+ const report = await runToolOutcomeHooks({
1172
+ tool: call.name,
1173
+ args: call.arguments,
1174
+ callId: call.id,
1175
+ ...(opts.surface ? { surface: opts.surface } : {}),
1176
+ ...(ctx.cwd ? { cwd: ctx.cwd } : {}),
1177
+ ...(ctx.configManager ? { configManager: ctx.configManager } : {}),
1178
+ durationMs: Date.now() - startedAt,
1179
+ ...outcome,
1180
+ });
1181
+ for (const problem of report.problems) {
1182
+ traceEvent({ kind: 'gate', gate: 'tool-hook', tool: call.name, ok: false, summary: problem });
1183
+ }
1184
+ };
1025
1185
  ctx.emit?.('tool:started', {
1026
1186
  id: call.id,
1027
1187
  tool: call.name,
1028
1188
  args: call.arguments,
1029
1189
  });
1190
+ // WS3 (#25) — the call as a child span of the turn. Created where the call
1191
+ // actually runs (not where it is announced), so a call that was REFUSED by
1192
+ // a gate above never gets a span claiming it happened.
1193
+ const toolSpan = opts.otel?.child(`${TOOL_SPAN_PREFIX}${call.name}`, { 'nuvira.tool': call.name }) ?? null;
1030
1194
  try {
1031
1195
  deps.onEvent?.(` ⚙ ${call.name}(${summarizeArgs(call.arguments)})`);
1032
- let resultText = await deps.executeTool(call.name, call.arguments, ctx);
1196
+ // The span is ACTIVE for the whole execution: that is what lets a tool
1197
+ // which spawns a subagent propagate the trace instead of starting a
1198
+ // second, unrelated one in the child.
1199
+ let resultText = await withSpanActive(toolSpan, async () => {
1200
+ // WS6 (#28) — a DECLARED fault, injected at the one place this loop runs
1201
+ // a tool, so every in-process surface (CLI chat, dashboard, gateway,
1202
+ // execute) is affected by the same declaration and none of them needs to
1203
+ // know the seam exists. The result is an `Error:` string rather than a
1204
+ // throw, which is exactly what a tool that really failed returns — so the
1205
+ // call is reported as FAILED, the `failed` hook fires, and the model is
1206
+ // handed the fault's own words. With no declaration this is a null check.
1207
+ const injected = faultAt('tool', call.name);
1208
+ if (injected)
1209
+ return `Error: ${injected.message}`;
1210
+ return deps.executeTool(call.name, call.arguments, ctx);
1211
+ });
1033
1212
  // I3: a tool that returns {artifact, result} gets its deliverable
1034
1213
  // recorded on the session and only `result`
1035
1214
  // is fed back to the model — the JSON payload is runtime metadata.
1036
1215
  resultText = appendToolArtifact(resultText, ctx.artifacts);
1216
+ // The SAME outcome convention the loop uses everywhere else (`Error:`
1217
+ // prefix), so the span cannot report a success the result did not have.
1218
+ const toolOk = !resultText.startsWith('Error:');
1037
1219
  ctx.emit?.('tool:called', {
1038
1220
  id: call.id,
1039
1221
  tool: call.name,
1040
- ok: !resultText.startsWith('Error:'),
1222
+ ok: toolOk,
1041
1223
  result: resultText,
1042
1224
  durationMs: Date.now() - startedAt,
1043
1225
  });
1226
+ toolSpan?.attr('nuvira.ok', toolOk);
1227
+ toolSpan?.end({ ok: toolOk });
1228
+ await reportToolOutcome({ ok: toolOk, result: resultText });
1044
1229
  if (call.name === 'suggest_followups')
1045
1230
  endedAfterConcluding = true;
1046
1231
  return resultText;
@@ -1054,10 +1239,16 @@ async function runToolLoopInner(opts, progress) {
1054
1239
  error: message,
1055
1240
  durationMs: Date.now() - startedAt,
1056
1241
  });
1242
+ toolSpan?.end({ ok: false, message });
1243
+ await reportToolOutcome({ ok: false, error: message });
1057
1244
  return `Error: ${message}`;
1058
1245
  }
1059
1246
  finally {
1060
1247
  toolDurations.set(call.id, Date.now() - startedAt);
1248
+ // A safety net, not a second report: `end()` is idempotent, so a call
1249
+ // that already ended its span is untouched, and one that reached here
1250
+ // without reporting says so rather than defaulting to success.
1251
+ toolSpan?.end({ ok: false, message: 'tool outcome was never reported' });
1061
1252
  }
1062
1253
  };
1063
1254
  const executed = new Array(plans.length).fill('');
@@ -1146,6 +1337,40 @@ async function runToolLoopInner(opts, progress) {
1146
1337
  // can see "blocked twice, identically" instead of rediscovering it — and
1147
1338
  // so the repetition gate has a refusal signal as well as an ask signal.
1148
1339
  runTrace.recordRefusal(call.name, refusal?.gate ?? 'error', rawResult);
1340
+ // ── DURABLE HAND-OFF ────────────────────────────────────────────────
1341
+ // A refused MUTATION is the one failure that must outlive the turn.
1342
+ // The live NVDA-addon failure is exactly this: `write_file` was refused
1343
+ // on every attempt because the target was outside the workspace, and
1344
+ // each of the 18 following attempts rediscovered it from zero — same
1345
+ // plan, same refusal, same empty package. Recording it here means the
1346
+ // next candidate, the next turn and the next RUN are told which path
1347
+ // could not be written and WHY, so they change approach instead of
1348
+ // repeating the identical call (see step-handoff.ts).
1349
+ //
1350
+ // Best-effort: a hand-off write must never break the loop.
1351
+ if (refusal !== null && MUTATING_TOOL_NAMES.has(call.name)) {
1352
+ try {
1353
+ const ask = currentAsk(opts);
1354
+ const aimedAt = mutatedPathOf(call.arguments);
1355
+ // Key on the deliverable the ASK names when there is one, so the
1356
+ // same work asked for in different words resumes the same hand-off.
1357
+ const named = deliverablesNamedIn(ask);
1358
+ recordStepHandoff({
1359
+ projectPath: opts.context?.cwd || process.cwd(),
1360
+ goal: ask,
1361
+ stepDescription: aimedAt
1362
+ ? `${call.name} → ${aimedAt} (refused: ${refusal.gate ?? 'gate'})`
1363
+ : `${call.name} (refused: ${refusal.gate ?? 'gate'})`,
1364
+ declared: named.length > 0 ? named : aimedAt ? [aimedAt] : [],
1365
+ route: 'loop',
1366
+ kind: 'refused',
1367
+ reason: `${refusal.summary}${aimedAt ? ` — path: ${aimedAt}` : ''}`,
1368
+ });
1369
+ }
1370
+ catch {
1371
+ // Best-effort — a hand-off write must never break the loop.
1372
+ }
1373
+ }
1149
1374
  }
1150
1375
  let resultText = executed[i];
1151
1376
  // P3c — on error/denial, append the deterministic fallback hint for
@@ -1186,7 +1411,7 @@ async function runToolLoopInner(opts, progress) {
1186
1411
  // P4 — cancellation during/after tool execution: do NOT request another
1187
1412
  // model step on a cancelled turn (the user already walked away).
1188
1413
  if (opts.signal?.aborted) {
1189
- return { content: '', followups, toolCalls: toolCallsRun, steps, bounded: false, cancelled: true };
1414
+ return { content: '', followups, toolCalls: toolCallsRun, steps, ...(opts.resume ? { replayedSteps } : {}), bounded: false, cancelled: true };
1190
1415
  }
1191
1416
  // ── endsAgentStep — a successful dispenser's RESULT is the answer ────────
1192
1417
  // `build`/`resume`/`repair`/`document`/`website`/`analyze`/`test`/`publish`
@@ -1213,6 +1438,7 @@ async function runToolLoopInner(opts, progress) {
1213
1438
  followups,
1214
1439
  toolCalls: toolCallsRun,
1215
1440
  steps,
1441
+ ...(opts.resume ? { replayedSteps } : {}),
1216
1442
  bounded: false,
1217
1443
  continuations,
1218
1444
  };
@@ -1291,6 +1517,7 @@ async function runToolLoopInner(opts, progress) {
1291
1517
  followups,
1292
1518
  toolCalls: toolCallsRun,
1293
1519
  steps,
1520
+ ...(opts.resume ? { replayedSteps } : {}),
1294
1521
  bounded: false,
1295
1522
  continuations,
1296
1523
  };
@@ -1310,6 +1537,7 @@ async function runToolLoopInner(opts, progress) {
1310
1537
  followups,
1311
1538
  toolCalls: toolCallsRun,
1312
1539
  steps,
1540
+ ...(opts.resume ? { replayedSteps } : {}),
1313
1541
  bounded: true,
1314
1542
  continuations,
1315
1543
  };
@@ -1444,6 +1672,69 @@ export function detectUnfulfilledIntentPromise(content) {
1444
1672
  * honest-answer flags so every surface (CLI, dashboard, gateway, trace) can
1445
1673
  * distinguish "generated a reply" from "actually performed the action".
1446
1674
  */
1675
+ /**
1676
+ * Keep ONE route frame in the thread, current with what is serving the turn.
1677
+ *
1678
+ * Insert/replace is deliberate: appending a frame per step would pile up stale
1679
+ * route claims the model can quote back ("I am gemini-2.5-flash" — from step 1,
1680
+ * after a failover), and rewriting unconditionally would churn the thread (and
1681
+ * the thread budget) on every step for no change in content.
1682
+ *
1683
+ * A caller with no route supplies nothing; a caller whose route resolves to the
1684
+ * same fact as last step gets no write.
1685
+ */
1686
+ function syncRouteFeed(thread, read, observed) {
1687
+ if (!read)
1688
+ return;
1689
+ let route = null;
1690
+ try {
1691
+ route = read();
1692
+ }
1693
+ catch {
1694
+ // A route read must never break the turn; an unknown route is a fact too.
1695
+ return;
1696
+ }
1697
+ if (!route)
1698
+ return;
1699
+ // The loop OWNS the failover history, because the loop is the only thing that
1700
+ // sees the sequence of routes it told the model about. Requiring each caller
1701
+ // to track it means every future caller can silently forget, and a model told
1702
+ // only the current pair describes the whole turn as having been run by it —
1703
+ // the same fabricated answer, one layer down.
1704
+ const currentPair = isUnresolvedModel(route.model) ? '' : `${route.providerType}/${route.model}`;
1705
+ const previous = [...(route.previous ?? [])];
1706
+ for (const pair of observed.pairs) {
1707
+ if (pair !== currentPair && !previous.includes(pair))
1708
+ previous.push(pair);
1709
+ }
1710
+ const rendered = previous.length > 0 ? { ...route, previous } : route;
1711
+ const fingerprint = routeFeedFingerprint(rendered);
1712
+ const index = thread.findIndex((m) => m.role === 'system' && typeof m.content === 'string' && m.content.startsWith(ROUTE_FEED_MARKER));
1713
+ if (currentPair && !observed.pairs.includes(currentPair))
1714
+ observed.pairs.push(currentPair);
1715
+ if (index === -1) {
1716
+ const frame = { role: 'system', content: routeFeedText(rendered) };
1717
+ // Immediately after the leading system prompt when there is one — the route
1718
+ // belongs with the run's own framing, not buried under tool output.
1719
+ const insertAt = thread.length > 0 && thread[0].role === 'system' ? 1 : 0;
1720
+ thread.splice(insertAt, 0, frame);
1721
+ routeFeedFingerprints.set(frame, fingerprint);
1722
+ return;
1723
+ }
1724
+ const existing = thread[index];
1725
+ if (routeFeedFingerprints.get(existing) === fingerprint)
1726
+ return;
1727
+ const updated = { role: 'system', content: routeFeedText(rendered) };
1728
+ thread[index] = updated;
1729
+ routeFeedFingerprints.delete(existing);
1730
+ routeFeedFingerprints.set(updated, fingerprint);
1731
+ }
1732
+ /**
1733
+ * Fingerprint per frame object, so a thread that was trimmed and re-built does
1734
+ * not re-write identical content. A WeakMap: a frame the loop drops is garbage
1735
+ * the moment nothing references it.
1736
+ */
1737
+ const routeFeedFingerprints = new WeakMap();
1447
1738
  export async function runToolLoop(opts) {
1448
1739
  const progress = {
1449
1740
  successfulToolCalls: [],
@@ -1452,6 +1743,11 @@ export async function runToolLoop(opts) {
1452
1743
  verificationEvidence: [],
1453
1744
  };
1454
1745
  const result = await runToolLoopInner(opts, progress);
1746
+ // R2 — relay the transport the caller's seam reported. Attached on EVERY
1747
+ // exit path, including a cancelled/failed turn: if a model call happened over
1748
+ // the JSON fallback before the turn died, that is a fact about the run.
1749
+ if (progress.transport)
1750
+ result.transport = progress.transport;
1455
1751
  if (!result.cancelled && !result.generationFailed) {
1456
1752
  result.successfulToolCalls = [...progress.successfulToolCalls];
1457
1753
  result.deliveryConfirmed = progress.deliveryConfirmed;
@@ -1579,7 +1875,17 @@ function previewToolResult(result, max = 200) {
1579
1875
  */
1580
1876
  export function classifyToolRefusal(result) {
1581
1877
  const text = result || '';
1582
- if (/needs explicit confirmation|requires confirmation|retry with confirm|call ask_user/i.test(text)) {
1878
+ if (/needs explicit confirmation|requires confirmation|retry with confirm|call ask_user/i.test(text) ||
1879
+ // The REAL `confirmFirst` wording (coding-tools.ts: "write_file: state-changing
1880
+ // — NOT applied. Ask the user first via ask_user (…), then retry write_file with
1881
+ // confirm:true once they approve.") matched NONE of the patterns above: the
1882
+ // words are the same but the phrasing is "retry write_file with confirm", not
1883
+ // "retry with confirm". A write that was NOT applied therefore counted as a call
1884
+ // that ran — so the path it named counted as a MUTATION, the run trace recorded
1885
+ // a change that never happened, and the verification gate asked the model to
1886
+ // check a file nothing had written. Found by the child's gate, where the same
1887
+ // miscount reported `unverifiedEdit` for an empty diff.
1888
+ /\bstate-changing\b[^.]*\bNOT applied\b|\bretry\s+\w+\s+with\s+confirm/i.test(text)) {
1583
1889
  return { gate: 'confirmation', summary: 'declined until the user approves — the gate asked before acting' };
1584
1890
  }
1585
1891
  // A BOUNDARY denial is the other refusal that did not read as one. The