agent-nuvira 2.7.3 → 3.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 (366) hide show
  1. package/dist/agents/agents/writer-tool-calling.d.ts +14 -0
  2. package/dist/agents/agents/writer-tool-calling.d.ts.map +1 -1
  3. package/dist/agents/agents/writer-tool-calling.js +20 -0
  4. package/dist/agents/agents/writer-tool-calling.js.map +1 -1
  5. package/dist/agents/agents/writer.d.ts.map +1 -1
  6. package/dist/agents/agents/writer.js +15 -4
  7. package/dist/agents/agents/writer.js.map +1 -1
  8. package/dist/agents/edit-module.d.ts +7 -0
  9. package/dist/agents/edit-module.d.ts.map +1 -1
  10. package/dist/agents/edit-module.js +21 -6
  11. package/dist/agents/edit-module.js.map +1 -1
  12. package/dist/agents/orchestrator.d.ts +40 -0
  13. package/dist/agents/orchestrator.d.ts.map +1 -1
  14. package/dist/agents/orchestrator.js +116 -27
  15. package/dist/agents/orchestrator.js.map +1 -1
  16. package/dist/agents/tool-calling-agent.d.ts +19 -0
  17. package/dist/agents/tool-calling-agent.d.ts.map +1 -1
  18. package/dist/agents/tool-calling-agent.js +42 -2
  19. package/dist/agents/tool-calling-agent.js.map +1 -1
  20. package/dist/cli/chat.d.ts +55 -1
  21. package/dist/cli/chat.d.ts.map +1 -1
  22. package/dist/cli/chat.js +239 -63
  23. package/dist/cli/chat.js.map +1 -1
  24. package/dist/cli/cli-program.d.ts +6 -0
  25. package/dist/cli/cli-program.d.ts.map +1 -0
  26. package/dist/cli/cli-program.js +222 -0
  27. package/dist/cli/cli-program.js.map +1 -0
  28. package/dist/cli/edit.d.ts.map +1 -1
  29. package/dist/cli/edit.js +18 -3
  30. package/dist/cli/edit.js.map +1 -1
  31. package/dist/cli/execute.d.ts.map +1 -1
  32. package/dist/cli/execute.js +29 -5
  33. package/dist/cli/execute.js.map +1 -1
  34. package/dist/cli/failover-runner.d.ts +13 -0
  35. package/dist/cli/failover-runner.d.ts.map +1 -1
  36. package/dist/cli/failover-runner.js +11 -0
  37. package/dist/cli/failover-runner.js.map +1 -1
  38. package/dist/cli/gateway.d.ts +16 -0
  39. package/dist/cli/gateway.d.ts.map +1 -1
  40. package/dist/cli/gateway.js +108 -2
  41. package/dist/cli/gateway.js.map +1 -1
  42. package/dist/cli/loop-executor.d.ts.map +1 -1
  43. package/dist/cli/loop-executor.js +245 -12
  44. package/dist/cli/loop-executor.js.map +1 -1
  45. package/dist/cli/plan.d.ts.map +1 -1
  46. package/dist/cli/plan.js +20 -6
  47. package/dist/cli/plan.js.map +1 -1
  48. package/dist/cli/process-control.d.ts +10 -0
  49. package/dist/cli/process-control.d.ts.map +1 -1
  50. package/dist/cli/process-control.js +73 -7
  51. package/dist/cli/process-control.js.map +1 -1
  52. package/dist/cli/router.d.ts +16 -5
  53. package/dist/cli/router.d.ts.map +1 -1
  54. package/dist/cli/router.js +31 -206
  55. package/dist/cli/router.js.map +1 -1
  56. package/dist/config/paths.d.ts +23 -0
  57. package/dist/config/paths.d.ts.map +1 -1
  58. package/dist/config/paths.js +30 -0
  59. package/dist/config/paths.js.map +1 -1
  60. package/dist/config/provider-env.d.ts +37 -0
  61. package/dist/config/provider-env.d.ts.map +1 -0
  62. package/dist/config/provider-env.js +86 -0
  63. package/dist/config/provider-env.js.map +1 -0
  64. package/dist/context/history.d.ts.map +1 -1
  65. package/dist/context/history.js +24 -8
  66. package/dist/context/history.js.map +1 -1
  67. package/dist/context/session-recall.d.ts +30 -0
  68. package/dist/context/session-recall.d.ts.map +1 -1
  69. package/dist/context/session-recall.js +61 -0
  70. package/dist/context/session-recall.js.map +1 -1
  71. package/dist/file.js +0 -1
  72. package/dist/file.js.map +1 -1
  73. package/dist/gateway/adapters.d.ts +10 -0
  74. package/dist/gateway/adapters.d.ts.map +1 -1
  75. package/dist/gateway/adapters.js +4 -1
  76. package/dist/gateway/adapters.js.map +1 -1
  77. package/dist/gateway/dedup.d.ts +105 -0
  78. package/dist/gateway/dedup.d.ts.map +1 -0
  79. package/dist/gateway/dedup.js +160 -0
  80. package/dist/gateway/dedup.js.map +1 -0
  81. package/dist/gateway/heartbeat.d.ts +99 -0
  82. package/dist/gateway/heartbeat.d.ts.map +1 -0
  83. package/dist/gateway/heartbeat.js +122 -0
  84. package/dist/gateway/heartbeat.js.map +1 -0
  85. package/dist/gateway/inbox.d.ts +14 -1
  86. package/dist/gateway/inbox.d.ts.map +1 -1
  87. package/dist/gateway/inbox.js.map +1 -1
  88. package/dist/gateway/platform-config.d.ts +5 -0
  89. package/dist/gateway/platform-config.d.ts.map +1 -1
  90. package/dist/gateway/platform-config.js +8 -13
  91. package/dist/gateway/platform-config.js.map +1 -1
  92. package/dist/gateway/registry.d.ts +47 -0
  93. package/dist/gateway/registry.d.ts.map +1 -1
  94. package/dist/gateway/registry.js +227 -21
  95. package/dist/gateway/registry.js.map +1 -1
  96. package/dist/gateway/whatsapp/baileys-bridge.d.ts +11 -1
  97. package/dist/gateway/whatsapp/baileys-bridge.d.ts.map +1 -1
  98. package/dist/gateway/whatsapp/baileys-bridge.js +64 -1
  99. package/dist/gateway/whatsapp/baileys-bridge.js.map +1 -1
  100. package/dist/gateway/whatsapp/bridge.d.ts +4 -2
  101. package/dist/gateway/whatsapp/bridge.d.ts.map +1 -1
  102. package/dist/gateway/whatsapp/bridge.js.map +1 -1
  103. package/dist/index.js +1 -1
  104. package/dist/index.js.map +1 -1
  105. package/dist/inference/factory.d.ts +16 -0
  106. package/dist/inference/factory.d.ts.map +1 -1
  107. package/dist/inference/factory.js +42 -0
  108. package/dist/inference/factory.js.map +1 -1
  109. package/dist/inference/gemini-adapter.d.ts.map +1 -1
  110. package/dist/inference/gemini-adapter.js +3 -0
  111. package/dist/inference/gemini-adapter.js.map +1 -1
  112. package/dist/inference/interface.d.ts +12 -0
  113. package/dist/inference/interface.d.ts.map +1 -1
  114. package/dist/inference/local-adapter.d.ts +2 -0
  115. package/dist/inference/local-adapter.d.ts.map +1 -1
  116. package/dist/inference/local-adapter.js +36 -5
  117. package/dist/inference/local-adapter.js.map +1 -1
  118. package/dist/inference/native-tools.d.ts +8 -0
  119. package/dist/inference/native-tools.d.ts.map +1 -1
  120. package/dist/inference/native-tools.js +14 -3
  121. package/dist/inference/native-tools.js.map +1 -1
  122. package/dist/inference/tool-call-utils.d.ts +66 -7
  123. package/dist/inference/tool-call-utils.d.ts.map +1 -1
  124. package/dist/inference/tool-call-utils.js +134 -13
  125. package/dist/inference/tool-call-utils.js.map +1 -1
  126. package/dist/learning/auto-router.d.ts.map +1 -1
  127. package/dist/learning/auto-router.js +90 -5
  128. package/dist/learning/auto-router.js.map +1 -1
  129. package/dist/learning/context-budget.d.ts +133 -0
  130. package/dist/learning/context-budget.d.ts.map +1 -0
  131. package/dist/learning/context-budget.js +196 -0
  132. package/dist/learning/context-budget.js.map +1 -0
  133. package/dist/learning/cost-tracker.d.ts.map +1 -1
  134. package/dist/learning/cost-tracker.js +20 -8
  135. package/dist/learning/cost-tracker.js.map +1 -1
  136. package/dist/learning/eval-framework.d.ts.map +1 -1
  137. package/dist/learning/eval-framework.js +6 -2
  138. package/dist/learning/eval-framework.js.map +1 -1
  139. package/dist/learning/failure-bookkeeping.d.ts +19 -0
  140. package/dist/learning/failure-bookkeeping.d.ts.map +1 -1
  141. package/dist/learning/failure-bookkeeping.js +67 -2
  142. package/dist/learning/failure-bookkeeping.js.map +1 -1
  143. package/dist/learning/feedback.d.ts.map +1 -1
  144. package/dist/learning/feedback.js +19 -8
  145. package/dist/learning/feedback.js.map +1 -1
  146. package/dist/learning/model-harness.d.ts +76 -0
  147. package/dist/learning/model-harness.d.ts.map +1 -0
  148. package/dist/learning/model-harness.js +119 -0
  149. package/dist/learning/model-harness.js.map +1 -0
  150. package/dist/learning/model-selection.d.ts +15 -0
  151. package/dist/learning/model-selection.d.ts.map +1 -1
  152. package/dist/learning/model-selection.js +27 -1
  153. package/dist/learning/model-selection.js.map +1 -1
  154. package/dist/learning/provider-fallback.d.ts +12 -0
  155. package/dist/learning/provider-fallback.d.ts.map +1 -1
  156. package/dist/learning/provider-fallback.js +74 -0
  157. package/dist/learning/provider-fallback.js.map +1 -1
  158. package/dist/learning/provider-revival.d.ts +106 -0
  159. package/dist/learning/provider-revival.d.ts.map +1 -0
  160. package/dist/learning/provider-revival.js +154 -0
  161. package/dist/learning/provider-revival.js.map +1 -0
  162. package/dist/learning/resilient-call.d.ts.map +1 -1
  163. package/dist/learning/resilient-call.js +48 -3
  164. package/dist/learning/resilient-call.js.map +1 -1
  165. package/dist/nlu/conversation-gate.d.ts +37 -0
  166. package/dist/nlu/conversation-gate.d.ts.map +1 -1
  167. package/dist/nlu/conversation-gate.js +49 -0
  168. package/dist/nlu/conversation-gate.js.map +1 -1
  169. package/dist/nlu/schema.d.ts +2 -2
  170. package/dist/observability/dag-bridge.d.ts +69 -0
  171. package/dist/observability/dag-bridge.d.ts.map +1 -0
  172. package/dist/observability/dag-bridge.js +56 -0
  173. package/dist/observability/dag-bridge.js.map +1 -0
  174. package/dist/observability/event-bus.d.ts +5 -10
  175. package/dist/observability/event-bus.d.ts.map +1 -1
  176. package/dist/observability/event-bus.js +10 -42
  177. package/dist/observability/event-bus.js.map +1 -1
  178. package/dist/skills/execution-audit.d.ts.map +1 -1
  179. package/dist/skills/execution-audit.js +17 -5
  180. package/dist/skills/execution-audit.js.map +1 -1
  181. package/dist/skills/sandbox-executor.d.ts +12 -1
  182. package/dist/skills/sandbox-executor.d.ts.map +1 -1
  183. package/dist/skills/sandbox-executor.js +28 -6
  184. package/dist/skills/sandbox-executor.js.map +1 -1
  185. package/dist/skills/secret-capture.d.ts +45 -1
  186. package/dist/skills/secret-capture.d.ts.map +1 -1
  187. package/dist/skills/secret-capture.js +82 -19
  188. package/dist/skills/secret-capture.js.map +1 -1
  189. package/dist/skills/skill-env-inventory.d.ts +79 -0
  190. package/dist/skills/skill-env-inventory.d.ts.map +1 -0
  191. package/dist/skills/skill-env-inventory.js +167 -0
  192. package/dist/skills/skill-env-inventory.js.map +1 -0
  193. package/dist/skills/skill-executor.d.ts +2 -0
  194. package/dist/skills/skill-executor.d.ts.map +1 -1
  195. package/dist/skills/skill-executor.js +19 -24
  196. package/dist/skills/skill-executor.js.map +1 -1
  197. package/dist/tools/ask-user.d.ts +14 -1
  198. package/dist/tools/ask-user.d.ts.map +1 -1
  199. package/dist/tools/ask-user.js +25 -1
  200. package/dist/tools/ask-user.js.map +1 -1
  201. package/dist/tools/followup-utils.d.ts +64 -0
  202. package/dist/tools/followup-utils.d.ts.map +1 -0
  203. package/dist/tools/followup-utils.js +158 -0
  204. package/dist/tools/followup-utils.js.map +1 -0
  205. package/dist/tools/memory-tools.d.ts +35 -0
  206. package/dist/tools/memory-tools.d.ts.map +1 -1
  207. package/dist/tools/memory-tools.js +76 -0
  208. package/dist/tools/memory-tools.js.map +1 -1
  209. package/dist/tools/pipeline-tool.d.ts.map +1 -1
  210. package/dist/tools/pipeline-tool.js +8 -3
  211. package/dist/tools/pipeline-tool.js.map +1 -1
  212. package/dist/tools/registry.d.ts +25 -12
  213. package/dist/tools/registry.d.ts.map +1 -1
  214. package/dist/tools/registry.js +31 -6
  215. package/dist/tools/registry.js.map +1 -1
  216. package/dist/tools/skill-tool.d.ts.map +1 -1
  217. package/dist/tools/skill-tool.js +52 -5
  218. package/dist/tools/skill-tool.js.map +1 -1
  219. package/dist/tools/tool-loop.d.ts +63 -1
  220. package/dist/tools/tool-loop.d.ts.map +1 -1
  221. package/dist/tools/tool-loop.js +305 -66
  222. package/dist/tools/tool-loop.js.map +1 -1
  223. package/dist/tools/toolsets.d.ts +19 -3
  224. package/dist/tools/toolsets.d.ts.map +1 -1
  225. package/dist/tools/toolsets.js +21 -5
  226. package/dist/tools/toolsets.js.map +1 -1
  227. package/dist/utils/env.d.ts.map +1 -1
  228. package/dist/utils/env.js +9 -13
  229. package/dist/utils/env.js.map +1 -1
  230. package/dist/web-dashboard/chat-console.d.ts +13 -1
  231. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  232. package/dist/web-dashboard/chat-console.js +16 -2
  233. package/dist/web-dashboard/chat-console.js.map +1 -1
  234. package/dist/web-dashboard/hub-data.d.ts +2 -0
  235. package/dist/web-dashboard/hub-data.d.ts.map +1 -1
  236. package/dist/web-dashboard/hub-data.js +4 -0
  237. package/dist/web-dashboard/hub-data.js.map +1 -1
  238. package/dist/web-dashboard/server.d.ts +6 -0
  239. package/dist/web-dashboard/server.d.ts.map +1 -1
  240. package/dist/web-dashboard/server.js +192 -3
  241. package/dist/web-dashboard/server.js.map +1 -1
  242. package/dist/web-dashboard/src/types.d.ts +31 -1
  243. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  244. package/package.json +11 -1
  245. package/src/web-dashboard/public/assets/index-56yPnN_m.js +207 -0
  246. package/src/web-dashboard/public/assets/index-56yPnN_m.js.map +1 -0
  247. package/src/web-dashboard/public/assets/{index-C507EUWf.css → index-Cyd6tIew.css} +1 -1
  248. package/src/web-dashboard/public/index.html +2 -2
  249. package/dist/example.d.ts +0 -1
  250. package/dist/example.d.ts.map +0 -1
  251. package/dist/example.js +0 -3
  252. package/dist/example.js.map +0 -1
  253. package/dist/fresh.d.ts +0 -2
  254. package/dist/fresh.d.ts.map +0 -1
  255. package/dist/fresh.js +0 -2
  256. package/dist/fresh.js.map +0 -1
  257. package/dist/mcp/mcp-dashboard-oauth.d.ts +0 -94
  258. package/dist/mcp/mcp-dashboard-oauth.d.ts.map +0 -1
  259. package/dist/mcp/mcp-dashboard-oauth.js +0 -140
  260. package/dist/mcp/mcp-dashboard-oauth.js.map +0 -1
  261. package/dist/memory/background-sync.d.ts +0 -107
  262. package/dist/memory/background-sync.d.ts.map +0 -1
  263. package/dist/memory/background-sync.js +0 -242
  264. package/dist/memory/background-sync.js.map +0 -1
  265. package/dist/memory/cross-session.d.ts +0 -68
  266. package/dist/memory/cross-session.d.ts.map +0 -1
  267. package/dist/memory/cross-session.js +0 -153
  268. package/dist/memory/cross-session.js.map +0 -1
  269. package/dist/memory/drift-detector.d.ts +0 -76
  270. package/dist/memory/drift-detector.d.ts.map +0 -1
  271. package/dist/memory/drift-detector.js +0 -216
  272. package/dist/memory/drift-detector.js.map +0 -1
  273. package/dist/memory/enhanced-manager.d.ts +0 -105
  274. package/dist/memory/enhanced-manager.d.ts.map +0 -1
  275. package/dist/memory/enhanced-manager.js +0 -280
  276. package/dist/memory/enhanced-manager.js.map +0 -1
  277. package/dist/memory/session-extraction.d.ts +0 -93
  278. package/dist/memory/session-extraction.d.ts.map +0 -1
  279. package/dist/memory/session-extraction.js +0 -290
  280. package/dist/memory/session-extraction.js.map +0 -1
  281. package/dist/memory/sqlite-store.d.ts +0 -208
  282. package/dist/memory/sqlite-store.d.ts.map +0 -1
  283. package/dist/memory/sqlite-store.js +0 -556
  284. package/dist/memory/sqlite-store.js.map +0 -1
  285. package/dist/skills/daytona-executor.d.ts +0 -78
  286. package/dist/skills/daytona-executor.d.ts.map +0 -1
  287. package/dist/skills/daytona-executor.js +0 -256
  288. package/dist/skills/daytona-executor.js.map +0 -1
  289. package/dist/skills/modal-executor.d.ts +0 -81
  290. package/dist/skills/modal-executor.d.ts.map +0 -1
  291. package/dist/skills/modal-executor.js +0 -285
  292. package/dist/skills/modal-executor.js.map +0 -1
  293. package/dist/sync/inventory.d.ts +0 -1
  294. package/dist/sync/inventory.d.ts.map +0 -1
  295. package/dist/sync/inventory.js +0 -3
  296. package/dist/sync/inventory.js.map +0 -1
  297. package/dist/sync/validate.d.ts +0 -1
  298. package/dist/sync/validate.d.ts.map +0 -1
  299. package/dist/sync/validate.js +0 -3
  300. package/dist/sync/validate.js.map +0 -1
  301. package/dist/test.d.ts +0 -2
  302. package/dist/test.d.ts.map +0 -1
  303. package/dist/test.js +0 -3
  304. package/dist/test.js.map +0 -1
  305. package/dist/tools/browser-tool.d.ts +0 -206
  306. package/dist/tools/browser-tool.d.ts.map +0 -1
  307. package/dist/tools/browser-tool.js +0 -726
  308. package/dist/tools/browser-tool.js.map +0 -1
  309. package/dist/tools/delegate-tool.d.ts +0 -109
  310. package/dist/tools/delegate-tool.d.ts.map +0 -1
  311. package/dist/tools/delegate-tool.js +0 -185
  312. package/dist/tools/delegate-tool.js.map +0 -1
  313. package/dist/tools/delegation-state.d.ts +0 -87
  314. package/dist/tools/delegation-state.d.ts.map +0 -1
  315. package/dist/tools/delegation-state.js +0 -278
  316. package/dist/tools/delegation-state.js.map +0 -1
  317. package/dist/tools/desktop-ui.d.ts +0 -148
  318. package/dist/tools/desktop-ui.d.ts.map +0 -1
  319. package/dist/tools/desktop-ui.js +0 -486
  320. package/dist/tools/desktop-ui.js.map +0 -1
  321. package/dist/tools/image-video-tool.d.ts +0 -88
  322. package/dist/tools/image-video-tool.d.ts.map +0 -1
  323. package/dist/tools/image-video-tool.js +0 -276
  324. package/dist/tools/image-video-tool.js.map +0 -1
  325. package/dist/tools/kanban-cron-tools.d.ts +0 -92
  326. package/dist/tools/kanban-cron-tools.d.ts.map +0 -1
  327. package/dist/tools/kanban-cron-tools.js +0 -197
  328. package/dist/tools/kanban-cron-tools.js.map +0 -1
  329. package/dist/tools/messaging-tool.d.ts +0 -127
  330. package/dist/tools/messaging-tool.d.ts.map +0 -1
  331. package/dist/tools/messaging-tool.js +0 -314
  332. package/dist/tools/messaging-tool.js.map +0 -1
  333. package/dist/tools/modality/generic-caller.d.ts +0 -43
  334. package/dist/tools/modality/generic-caller.d.ts.map +0 -1
  335. package/dist/tools/modality/generic-caller.js +0 -254
  336. package/dist/tools/modality/generic-caller.js.map +0 -1
  337. package/dist/tools/modality/modality-catalog.d.ts +0 -134
  338. package/dist/tools/modality/modality-catalog.d.ts.map +0 -1
  339. package/dist/tools/modality/modality-catalog.js +0 -445
  340. package/dist/tools/modality/modality-catalog.js.map +0 -1
  341. package/dist/tools/modality/tool-router.d.ts +0 -81
  342. package/dist/tools/modality/tool-router.d.ts.map +0 -1
  343. package/dist/tools/modality/tool-router.js +0 -84
  344. package/dist/tools/modality/tool-router.js.map +0 -1
  345. package/dist/tools/security-tools.d.ts +0 -107
  346. package/dist/tools/security-tools.d.ts.map +0 -1
  347. package/dist/tools/security-tools.js +0 -264
  348. package/dist/tools/security-tools.js.map +0 -1
  349. package/dist/tools/ssh-tool.d.ts +0 -130
  350. package/dist/tools/ssh-tool.d.ts.map +0 -1
  351. package/dist/tools/ssh-tool.js +0 -328
  352. package/dist/tools/ssh-tool.js.map +0 -1
  353. package/dist/tools/tts-tool.d.ts +0 -91
  354. package/dist/tools/tts-tool.d.ts.map +0 -1
  355. package/dist/tools/tts-tool.js +0 -406
  356. package/dist/tools/tts-tool.js.map +0 -1
  357. package/dist/utils/shell-detection.d.ts +0 -124
  358. package/dist/utils/shell-detection.d.ts.map +0 -1
  359. package/dist/utils/shell-detection.js +0 -305
  360. package/dist/utils/shell-detection.js.map +0 -1
  361. package/dist/utils/windows-paths.d.ts +0 -185
  362. package/dist/utils/windows-paths.d.ts.map +0 -1
  363. package/dist/utils/windows-paths.js +0 -472
  364. package/dist/utils/windows-paths.js.map +0 -1
  365. package/src/web-dashboard/public/assets/index-C4frng1Q.js +0 -207
  366. package/src/web-dashboard/public/assets/index-C4frng1Q.js.map +0 -1
@@ -16,7 +16,8 @@ export interface ToolCallInfo {
16
16
  error?: string;
17
17
  durationMs?: number;
18
18
  }
19
- import { type ToolContext, type FollowupSuggestion } from '../tools/registry.js';
19
+ import { type ToolContext } from '../tools/registry.js';
20
+ import { type FollowupSuggestion } from '../tools/followup-utils.js';
20
21
  /** E3a — the menu-free dispatch decision (rule-based, C1/C3 only). */
21
22
  export interface PipelineDispatchDecision {
22
23
  /** Whether the request runs the coding pipeline. */
@@ -59,6 +60,38 @@ export declare function runDeveloperMode(goal: string, configManager: any, optio
59
60
  provider?: string;
60
61
  model?: string;
61
62
  }): Promise<void>;
63
+ /**
64
+ * E3b — the tool-loop system prompt: base identity + the tool
65
+ * contract (clarify via ask_user, end every response with suggest_followups).
66
+ *
67
+ * E3c — the rule assessment is a HINT, never a bypass: when the rules parsed
68
+ * a confident intent, the model sees it as context ("rule assessment: … you
69
+ * decide") so it can act faster — but the MODEL is the sole decision-maker
70
+ * (rules act only as the no-model fallback in the
71
+ * caller, never to skip the loop).
72
+ */
73
+ /**
74
+ * Backoff schedule for a SAME-provider retry on a transient failure. Two extra
75
+ * attempts, deliberately short: a capacity spike at a shared endpoint clears in
76
+ * seconds, and the user is waiting in the foreground. Long/looping retries belong
77
+ * to the background runners, not the interactive turn.
78
+ */
79
+ export declare const TRANSIENT_RETRY_DELAYS_MS: readonly [1000, 3000];
80
+ /**
81
+ * Run one provider attempt, retrying transient failures against the SAME
82
+ * provider before giving up.
83
+ *
84
+ * Why this exists next to the failover walk rather than inside it: the walk
85
+ * needs a DIFFERENT provider to exist, and it books the failure against the one
86
+ * that just failed. Verified live — a single configured provider plus a Gemini
87
+ * 503 meant no retry at all, the circuit breaker parked the provider for 120s,
88
+ * and the agent degraded to editing with zero gathered context. A transient
89
+ * spike must cost a few seconds, not the whole task.
90
+ *
91
+ * Never retries: non-transient classes (auth, rate-limit, model/harness faults),
92
+ * a cancelled turn, or once the schedule is exhausted.
93
+ */
94
+ export declare function generateWithTransientRetry<T>(attempt: () => Promise<T>, signal?: AbortSignal, onRetry?: (attemptNumber: number, err: unknown) => void): Promise<T>;
62
95
  export declare class ChatCommand extends BaseCommand {
63
96
  private devModeAuto;
64
97
  /**
@@ -127,6 +160,13 @@ export declare class ChatCommand extends BaseCommand {
127
160
  content: string;
128
161
  }>;
129
162
  askUser?: ToolContext['askUser'];
163
+ /**
164
+ * P5 — this message is a CONTINUATION of the previous turn (it came from a
165
+ * clicked followup). The continuation marker is prepended to the
166
+ * model-facing thread ONLY — the raw text is what lands in history, so a
167
+ * long session never accumulates markers. See FOLLOWUP_CONTINUATION_MARKER.
168
+ */
169
+ continuation?: boolean;
130
170
  /** P3 — live progress lines for the dashboard chat console. */
131
171
  onProgress?: (line: string) => void;
132
172
  /**
@@ -221,6 +261,20 @@ export declare class ChatCommand extends BaseCommand {
221
261
  * broken provider never crashes the turn (it answers from the next working
222
262
  * candidate, exactly like the legacy generation block).
223
263
  */
264
+ /**
265
+ * The model id used in the response-cache key.
266
+ *
267
+ * NEVER returns the `'default'` sentinel (or an empty string). Keying the
268
+ * cache on `'default'` — which is what `session.model ?? 'default'` did —
269
+ * collapsed EVERY model of a provider into a single entry (observed live:
270
+ * `cache.json` held `provider: gemini, model: "default"`). Two consequences,
271
+ * both real: an answer produced by a weak model was replayed as though a
272
+ * strong one had written it, and switching `nuvira model switch` could never
273
+ * take effect for a message already cached. Falls back to the provider's
274
+ * effective model, then to a provider-qualified marker so distinct providers
275
+ * still never collide.
276
+ */
277
+ private cacheModelFor;
224
278
  private buildToolCallModel;
225
279
  /**
226
280
  * E3b — render suggest_followups results. Interactive:
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/cli/chat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/cli/chat.ts"],"names":[],"mappings":"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"}
package/dist/cli/chat.js CHANGED
@@ -16,12 +16,13 @@ import { getMemoryManager } from '../memory/manager.js';
16
16
  import { logger } from '../utils/logger.js';
17
17
  import { printOrchestrationResult } from './execute.js';
18
18
  import { applyActiveModel } from './model.js';
19
- import { getProviderFallback, classifyFallbackError, isRetryableError, recordRegistrySuccess } from '../learning/provider-fallback.js';
20
- import { recordActionFailure, TRANSIENT_FAILURE_EXCLUSION_MS } from '../learning/failure-bookkeeping.js';
19
+ import { getProviderFallback, classifyFallbackError, isRetryableError, isTransientForRetry, recordRegistrySuccess } from '../learning/provider-fallback.js';
20
+ import { recordActionFailure } from '../learning/failure-bookkeeping.js';
21
+ import { resolveThreadBudgetChars } from '../learning/context-budget.js';
21
22
  import { getAutoRouter, isAutoModel, isAutoProvider } from '../learning/auto-router.js';
22
23
  import { estimateTokens } from '../learning/cost-tracker.js';
23
24
  import { getModelRegistry } from '../learning/model-registry.js';
24
- import { refreshModelRegistry, spotCheckModel } from '../inference/model-probe.js';
25
+ import { refreshModelRegistry } from '../inference/model-probe.js';
25
26
  import { recordRoutingDecision } from '../learning/routing-history.js';
26
27
  import { shouldConfirmFailover, promptFailoverChoice } from './failover-prompt.js';
27
28
  import { buildAutoResolveOptions } from '../learning/resolve-options.js';
@@ -31,15 +32,19 @@ import { PlanStore } from '../tools/plan-store.js';
31
32
  import { withLogCorrelation } from '../enterprise/log.js';
32
33
  import { recordMetricTime, getMetrics } from '../enterprise/metrics.js';
33
34
  import { resolveDispatch } from '../nlu/actions.js';
34
- import { isConversationalQuestion, hasCodingAction } from '../nlu/conversation-gate.js';
35
+ import { hasCodingAction, resolveAskKind } from '../nlu/conversation-gate.js';
35
36
  import { runToolLoop, extractFallbackToolCalls } from '../tools/tool-loop.js';
36
- import { looksLikeConfusedScaffoldingReply } from '../inference/tool-call-utils.js';
37
+ import { looksLikeConfusedScaffoldingReply, toUserFacingGenerationError, isToolCallingUnsupported, } from '../inference/tool-call-utils.js';
37
38
  import { beginTrace, endTrace, recordStep } from '../learning/reasoning-trace.js';
38
39
  import { getLoopExposureMode } from '../tools/toolsets.js';
40
+ import { resolveModelHarnessProfile, shouldSkipNativeTools } from '../learning/model-harness.js';
41
+ import { resolveAdapterDefault } from '../learning/model-selection.js';
39
42
  import { buildLoopProjectContext } from '../tools/loop-project-context.js';
43
+ import { sweepTransientFailures, collectionRevivalStore } from '../learning/provider-revival.js';
40
44
  import { analyzeComplexity } from '../learning/hybrid-router.js';
41
45
  import { routingCacheSignature, withRoutingCache } from '../learning/routing-cache.js';
42
46
  import { getTool, TOOL_CONTRACT_JSON } from '../tools/registry.js';
47
+ import { buildFollowupContinuationPrompt, isSuggestedFollowup, } from '../tools/followup-utils.js';
43
48
  // S2/S3 — the shared tool-call reliability helpers (salvage failed_generation,
44
49
  // compact fallback schemas). One copy for every tool-calling surface, not
45
50
  // chat-private (execute/plan/… inherit the fix).
@@ -168,7 +173,11 @@ export function resolvePipelineDispatch(parsed, opts) {
168
173
  // NLU alone would misread it as chat ("how do I add JWT auth?" → explain
169
174
  // → chat, but the user wants the auth added).
170
175
  if (opts?.text) {
171
- if (isConversationalQuestion(opts.text)) {
176
+ // ONE shared rule for every surface (see resolveAskKind): a genuine
177
+ // question never dispatches; a coding verb in command position always
178
+ // does. Keeping the gateway on this same function is what stops the two
179
+ // from disagreeing about the same ask.
180
+ if (resolveAskKind(opts.text, parsed) === 'chat') {
172
181
  return { dispatch: false, needConfirm: false };
173
182
  }
174
183
  if (hasCodingAction(opts.text)) {
@@ -217,6 +226,41 @@ export async function runDeveloperMode(goal, configManager, options) {
217
226
  * (rules act only as the no-model fallback in the
218
227
  * caller, never to skip the loop).
219
228
  */
229
+ /**
230
+ * Backoff schedule for a SAME-provider retry on a transient failure. Two extra
231
+ * attempts, deliberately short: a capacity spike at a shared endpoint clears in
232
+ * seconds, and the user is waiting in the foreground. Long/looping retries belong
233
+ * to the background runners, not the interactive turn.
234
+ */
235
+ export const TRANSIENT_RETRY_DELAYS_MS = [1_000, 3_000];
236
+ /**
237
+ * Run one provider attempt, retrying transient failures against the SAME
238
+ * provider before giving up.
239
+ *
240
+ * Why this exists next to the failover walk rather than inside it: the walk
241
+ * needs a DIFFERENT provider to exist, and it books the failure against the one
242
+ * that just failed. Verified live — a single configured provider plus a Gemini
243
+ * 503 meant no retry at all, the circuit breaker parked the provider for 120s,
244
+ * and the agent degraded to editing with zero gathered context. A transient
245
+ * spike must cost a few seconds, not the whole task.
246
+ *
247
+ * Never retries: non-transient classes (auth, rate-limit, model/harness faults),
248
+ * a cancelled turn, or once the schedule is exhausted.
249
+ */
250
+ export async function generateWithTransientRetry(attempt, signal, onRetry) {
251
+ for (let i = 0;; i += 1) {
252
+ try {
253
+ return await attempt();
254
+ }
255
+ catch (err) {
256
+ const canRetry = i < TRANSIENT_RETRY_DELAYS_MS.length && isTransientForRetry(err);
257
+ if (!canRetry || signal?.aborted)
258
+ throw err;
259
+ onRetry?.(i + 1, err);
260
+ await new Promise((resolve) => setTimeout(resolve, TRANSIENT_RETRY_DELAYS_MS[i]));
261
+ }
262
+ }
263
+ }
220
264
  function buildToolSystemPrompt(parsed) {
221
265
  return [
222
266
  "You are Nuvira, Agent-Nuvira's AI agent. You code, create, write, analyze, and automate — anything the user needs. You identify as Nuvira (never 'Buff').",
@@ -351,7 +395,7 @@ export class ChatCommand extends BaseCommand {
351
395
  }
352
396
  const parsed = parseRequestSync(message);
353
397
  const dispatchDecision = resolvePipelineDispatch(parsed, { dev: opts.dev, text: message });
354
- const answer = await this.runChatAnswer(message, opts.history ?? [], { type, provider, model }, { provider: mergedOpts.provider, model: mergedOpts.model, dev: mergedOpts.dev, cache: true }, true, { auto: autoMode }, parsed, { askUser: opts.askUser, onProgress: opts.onProgress, onToolCall: opts.onToolCall, onPlanChange: opts.onPlanChange, onGitDiff: opts.onGitDiff, onSkillDraft: opts.onSkillDraft, planStore: opts.planStore ?? this.planStore, gateway: opts.gateway, projectContext: opts.projectContext, recallContext: recallBlock, projectPath: opts.projectPath, onToken: opts.onToken, signal: opts.signal });
398
+ const answer = await this.runChatAnswer(message, opts.history ?? [], { type, provider, model }, { provider: mergedOpts.provider, model: mergedOpts.model, dev: mergedOpts.dev, cache: true }, true, { auto: autoMode }, parsed, { askUser: opts.askUser, onProgress: opts.onProgress, onToolCall: opts.onToolCall, onPlanChange: opts.onPlanChange, onGitDiff: opts.onGitDiff, onSkillDraft: opts.onSkillDraft, planStore: opts.planStore ?? this.planStore, gateway: opts.gateway, projectContext: opts.projectContext, recallContext: recallBlock, projectPath: opts.projectPath, onToken: opts.onToken, signal: opts.signal, continuation: opts.continuation });
355
399
  // No-model fallback: the tool loop could not generate a single response
356
400
  // AND the rules assessed a high-confidence pipeline intent — run the
357
401
  // pipeline directly (rules decide only when the model is unavailable; the
@@ -522,10 +566,14 @@ export class ChatCommand extends BaseCommand {
522
566
  const picked = await this.renderFollowups(singleAnswer.followups ?? [], true);
523
567
  if (!picked)
524
568
  break;
525
- const next = await this.runChatAnswer(picked, history, { type, provider, model }, options || {}, cacheEnabled, { auto: autoMode }, parseRequestSync(picked));
569
+ const next = await this.runChatAnswer(picked, history, { type, provider, model }, options || {}, cacheEnabled, { auto: autoMode }, parseRequestSync(picked),
570
+ // P5 — a picked followup continues the previous execution.
571
+ { continuation: true });
526
572
  if (next.content.trim()) {
527
573
  console.log('\n' + next.content + '\n');
528
- history.push({ role: 'assistant', content: next.content });
574
+ // NOTE: no history.push here — runChatAnswer already recorded the
575
+ // assistant turn. The old duplicate push gave every subsequent turn
576
+ // TWO copies of the previous answer (relevance noise).
529
577
  }
530
578
  singleAnswer = next;
531
579
  }
@@ -547,8 +595,13 @@ export class ChatCommand extends BaseCommand {
547
595
  await maybeRunBackgroundDuties(this.configManager).catch(() => { });
548
596
  }
549
597
  let pendingMessage;
598
+ // P5 — the followups the agent just suggested, so a message that MATCHES
599
+ // one of them (a clicked chip, or the user re-typing it on the gateway) is
600
+ // recognised as a continuation of the previous execution.
601
+ let lastFollowups = [];
550
602
  while (true) {
551
603
  // E3b: a chosen follow-up recommendation becomes the next message.
604
+ const pickedFollowup = pendingMessage !== undefined;
552
605
  const message = pendingMessage ?? (await this.readMultiLineInput('You:'));
553
606
  pendingMessage = undefined;
554
607
  if (!message)
@@ -602,7 +655,10 @@ export class ChatCommand extends BaseCommand {
602
655
  const parsed = recordMetricTime('rule.parse.ms', () => parseRequestSync(message));
603
656
  const dispatchDecision = recordMetricTime('rule.dispatch.ms', () => resolvePipelineDispatch(parsed, { dev: this.devModeAuto, text: message }));
604
657
  const session = { type, provider, model: effectiveModel };
605
- const answer = await withLogCorrelation({ sessionId: chatSessionId }, () => recordMetricTime('llm.answer.ms', () => this.runChatAnswer(message, history, session, options || {}, cacheEnabled, { auto: autoMode }, parsed)));
658
+ const answer = await withLogCorrelation({ sessionId: chatSessionId }, () => recordMetricTime('llm.answer.ms', () => this.runChatAnswer(message, history, session, options || {}, cacheEnabled, { auto: autoMode }, parsed,
659
+ // P5 — a picked followup (or a typed one that matches the last
660
+ // suggestions) is a continuation, not a fresh independent request.
661
+ { continuation: pickedFollowup || isSuggestedFollowup(message, lastFollowups) })));
606
662
  // No-model fallback: the tool loop could not generate a single response
607
663
  // AND the rules assessed a high-confidence pipeline intent — run the
608
664
  // pipeline directly (rules decide only when the model is unavailable).
@@ -623,7 +679,8 @@ export class ChatCommand extends BaseCommand {
623
679
  if (answer.content.trim()) {
624
680
  console.log('\n' + answer.content + '\n');
625
681
  }
626
- const followupPrompt = await this.renderFollowups(answer.followups ?? [], true);
682
+ lastFollowups = answer.followups ?? [];
683
+ const followupPrompt = await this.renderFollowups(lastFollowups, true);
627
684
  if (followupPrompt) {
628
685
  pendingMessage = followupPrompt;
629
686
  }
@@ -703,9 +760,10 @@ export class ChatCommand extends BaseCommand {
703
760
  async runChatAnswer(message, history, session, options, cacheEnabled, mode, parsed, ctxOverrides) {
704
761
  // Cache check first (same as the legacy path).
705
762
  const cache = getCache();
763
+ const cacheModel = this.cacheModelFor(session);
706
764
  if (cacheEnabled) {
707
765
  try {
708
- const cachedResult = await cache.get(message, session.model ?? 'default', session.type);
766
+ const cachedResult = await cache.get(message, cacheModel, session.type);
709
767
  if (cachedResult) {
710
768
  // NOTE: the cached answer is NOT printed here — the caller prints
711
769
  // content AFTER runChatAnswer returns (answer-first ordering). A
@@ -807,7 +865,11 @@ export class ChatCommand extends BaseCommand {
807
865
  role: (h.role === 'assistant' ? 'assistant' : 'user'),
808
866
  content: h.content,
809
867
  })),
810
- { role: 'user', content: message },
868
+ // P5 — a picked followup reaches the model WITH the continuation marker
869
+ // (the raw text stays in history), so "add a day in Hanoi" is resolved
870
+ // against the plan the previous turn just produced instead of being read
871
+ // as a brand-new request.
872
+ { role: 'user', content: ctxOverrides?.continuation ? buildFollowupContinuationPrompt(message) : message },
811
873
  ];
812
874
  // I3: one artifact session per TURN — every tool
813
875
  // deliverable in this turn lands in the same store folder.
@@ -940,13 +1002,26 @@ export class ChatCommand extends BaseCommand {
940
1002
  }
941
1003
  };
942
1004
  try {
1005
+ // R1 — the harness follows the MODEL, not only the config: config says
1006
+ // what this deployment prefers, the profile decides what this model can
1007
+ // actually use (a 0.5B local model must not get a 120B's surface).
1008
+ const harness = resolveModelHarnessProfile({
1009
+ model: session.model,
1010
+ configExposure: getLoopExposureMode(this.configManager),
1011
+ });
943
1012
  result = await runToolLoop({
944
1013
  messages: thread,
945
1014
  context: toolContext,
946
1015
  maxSteps: 16,
947
- // Tiered tool exposure — config-gated (tools.loopExposure), default
948
- // 'all' = unchanged behavior until Phase 0 evals justify the flip.
949
- toolExposure: getLoopExposureMode(this.configManager),
1016
+ // Model-window-aware thread budget: a 1M-token model keeps its window
1017
+ // instead of being trimmed to the fixed ~50K-token default. Undefined
1018
+ // (unknown window) leaves the tool-loop default untouched.
1019
+ threadBudgetChars: resolveThreadBudgetChars({ provider: session.type, model: session.model }),
1020
+ // Tiered tool exposure — the tiered (core) set starts at ~16 schemas
1021
+ // (~3.8K tokens/step); 'all' hands over the full set (~17K tokens/step)
1022
+ // and is now additionally gated on the model having the context for it.
1023
+ toolExposure: harness.exposure,
1024
+ maxParallelReads: harness.maxParallelReads,
950
1025
  onToken: ctxOverrides?.onToken,
951
1026
  signal: ctxOverrides?.signal,
952
1027
  deps: {
@@ -977,7 +1052,9 @@ export class ChatCommand extends BaseCommand {
977
1052
  logger.error(String(err));
978
1053
  endTrace(chatTraceId, false);
979
1054
  result = {
980
- content: `I ran into a problem: ${err instanceof Error ? err.message : String(err)}`,
1055
+ // Sanitized on purpose: this content is delivered verbatim by every
1056
+ // surface (CLI print, dashboard bubble, gateway send).
1057
+ content: toUserFacingGenerationError(err),
981
1058
  followups: [],
982
1059
  toolCalls: [],
983
1060
  steps: 0,
@@ -994,7 +1071,11 @@ export class ChatCommand extends BaseCommand {
994
1071
  if (result.content.trim() && !result.generationFailed && !result.cancelled) {
995
1072
  if (cacheEnabled) {
996
1073
  try {
997
- await cache.set(message, result.content, session.model ?? 'default', session.type);
1074
+ // Keyed by the model that ACTUALLY answered (tryGenerate records it
1075
+ // on success), so a weak model's reply is never replayed as a strong
1076
+ // model's. `cacheModel` is the pre-flight fallback for the paths that
1077
+ // never resolve one (e.g. a cached-hit turn).
1078
+ await cache.set(message, result.content, this.cacheModelFor(session) || cacheModel, session.type);
998
1079
  }
999
1080
  catch {
1000
1081
  // Best-effort.
@@ -1026,13 +1107,83 @@ export class ChatCommand extends BaseCommand {
1026
1107
  * broken provider never crashes the turn (it answers from the next working
1027
1108
  * candidate, exactly like the legacy generation block).
1028
1109
  */
1110
+ /**
1111
+ * The model id used in the response-cache key.
1112
+ *
1113
+ * NEVER returns the `'default'` sentinel (or an empty string). Keying the
1114
+ * cache on `'default'` — which is what `session.model ?? 'default'` did —
1115
+ * collapsed EVERY model of a provider into a single entry (observed live:
1116
+ * `cache.json` held `provider: gemini, model: "default"`). Two consequences,
1117
+ * both real: an answer produced by a weak model was replayed as though a
1118
+ * strong one had written it, and switching `nuvira model switch` could never
1119
+ * take effect for a message already cached. Falls back to the provider's
1120
+ * effective model, then to a provider-qualified marker so distinct providers
1121
+ * still never collide.
1122
+ */
1123
+ cacheModelFor(session) {
1124
+ if (session.model && session.model !== 'default')
1125
+ return session.model;
1126
+ try {
1127
+ const providers = this.configManager.getAll().providers;
1128
+ return resolveAdapterDefault(session.type, providers?.[session.type]?.model) ?? `${session.type}:unresolved`;
1129
+ }
1130
+ catch {
1131
+ return `${session.type}:unresolved`;
1132
+ }
1133
+ }
1029
1134
  buildToolCallModel(message, session, options, mode, onToken, signal) {
1030
1135
  return async (messages, schemas, stepOnToken, stepSignal) => {
1031
1136
  // The effective token sink: the caller's stream wins; when a step-level
1032
1137
  // sink is also given (loop passthrough) they are the same channel.
1033
1138
  const sink = stepOnToken ?? onToken;
1034
1139
  const abort = stepSignal ?? signal;
1140
+ /**
1141
+ * The CONCRETE model an attempt will use.
1142
+ *
1143
+ * `session.model` is undefined or the `'default'` sentinel whenever the
1144
+ * router picks a provider but no single model (which is the common auto
1145
+ * case). That value used to flow into four places at once — the provider
1146
+ * request, the reasoning trace, registry telemetry and the response
1147
+ * cache key — so traces read `model: unknown`, the literal `default`
1148
+ * reached provider APIs (`The model \`default\` does not exist`, observed
1149
+ * live), and EVERY model of a provider shared one cache entry (a bad
1150
+ * answer produced by a weak model was then replayed as if it came from a
1151
+ * good one). Resolving here keeps all four on the same real model id.
1152
+ * Falls back to undefined only when nothing can be resolved, which leaves
1153
+ * the adapter's own last-resort resolution in charge.
1154
+ */
1155
+ /**
1156
+ * Per-turn memo of candidates that REJECTED native tool calling, keyed by
1157
+ * `provider|model`. Once a model has answered "tool calling is not
1158
+ * supported", it can never start supporting it within this turn, so
1159
+ * re-issuing the native call is pure waste — the live execute run paid 13
1160
+ * failing native requests (one per step) before each fell back to the
1161
+ * JSON transport, burning the provider's rate limit for nothing.
1162
+ * Keyed per candidate on purpose: a DIFFERENT model that does support
1163
+ * native tools must still get them, so a failover re-enables the fast
1164
+ * path automatically.
1165
+ */
1166
+ const nativeToolsRejected = new Set();
1167
+ const resolveEffectiveModel = (providerType, requested) => {
1168
+ if (requested && requested !== 'default')
1169
+ return requested;
1170
+ try {
1171
+ const providers = this.configManager.getAll().providers;
1172
+ return resolveAdapterDefault(providerType, providers?.[providerType]?.model);
1173
+ }
1174
+ catch {
1175
+ return undefined;
1176
+ }
1177
+ };
1035
1178
  const tryGenerate = async (prov, typ, mdl) => {
1179
+ // One resolution per attempt — the request, the trace, the telemetry
1180
+ // and the cache key all read the same value (see above).
1181
+ const effectiveModel = resolveEffectiveModel(typ, mdl);
1182
+ // Record the ATTEMPTED model immediately so a failed step's trace and
1183
+ // telemetry name the model that failed, instead of "unknown".
1184
+ if (effectiveModel)
1185
+ session.model = effectiveModel;
1186
+ const nativeKey = `${typ}|${effectiveModel ?? ''}`;
1036
1187
  // Answer-quality resilience: a CONFUSED reply — the model talking about
1037
1188
  // the tool contract (e.g. apologizing that "the provided example call
1038
1189
  // to suggest_followups is incomplete") instead of executing it — never
@@ -1048,22 +1199,35 @@ export class ChatCommand extends BaseCommand {
1048
1199
  throw err;
1049
1200
  }
1050
1201
  };
1051
- if (typeof prov.generateTools === 'function' && schemas.length > 0) {
1202
+ /** Mark the model that actually produced this response. */
1203
+ const answered = (resp) => {
1204
+ if (effectiveModel)
1205
+ session.model = effectiveModel;
1206
+ return resp;
1207
+ };
1208
+ // R1 — deterministic transport. A tiny model cannot use a native tool
1209
+ // API, and finding that out by trying cost a 400 on every turn (the
1210
+ // refusal memo is per-call). Unknown families are untouched: they still
1211
+ // try native and fall back, so nothing that works today stops working.
1212
+ if (shouldSkipNativeTools({ model: effectiveModel })) {
1213
+ nativeToolsRejected.add(nativeKey);
1214
+ }
1215
+ if (!nativeToolsRejected.has(nativeKey) && typeof prov.generateTools === 'function' && schemas.length > 0) {
1052
1216
  try {
1053
1217
  // P4 — stream when the provider supports it AND a sink is wired
1054
1218
  // (the dashboard); otherwise the one-shot path with the whole
1055
1219
  // content delivered as a single chunk so the typewriter channel
1056
1220
  // still receives the answer (appears at once — today's behavior).
1057
1221
  if (sink && typeof prov.generateToolsStream === 'function') {
1058
- const result = await prov.generateToolsStream(messages, schemas, { ...options, model: mdl, signal: abort }, sink);
1222
+ const result = await prov.generateToolsStream(messages, schemas, { ...options, model: effectiveModel, signal: abort }, sink);
1059
1223
  confuseCheck(result.content);
1060
- return result;
1224
+ return answered(result);
1061
1225
  }
1062
- const result = await prov.generateTools(messages, schemas, { ...options, model: mdl, signal: abort });
1226
+ const result = await prov.generateTools(messages, schemas, { ...options, model: effectiveModel, signal: abort });
1063
1227
  confuseCheck(result.content);
1064
1228
  if (sink && result.content)
1065
1229
  sink(result.content);
1066
- return result;
1230
+ return answered(result);
1067
1231
  }
1068
1232
  catch (err) {
1069
1233
  // S3: a tool-call 400 often carries the model's COMPLETE answer in
@@ -1082,9 +1246,23 @@ export class ChatCommand extends BaseCommand {
1082
1246
  const toolCalls = salvaged.followups?.length
1083
1247
  ? [{ id: 'call_salvage_1', name: 'suggest_followups', arguments: { followups: salvaged.followups } }]
1084
1248
  : [];
1085
- return { content: salvaged.content, toolCalls };
1249
+ return answered({ content: salvaged.content, toolCalls });
1250
+ }
1251
+ // The MODEL itself cannot do native tool calling — Groq answers
1252
+ // 400 "`tool calling` is not supported with this model". That is
1253
+ // not a reason to lose the turn: the loop already ships a transport
1254
+ // that needs no provider tool support, and the system prompt
1255
+ // carries the tool contract for it. Fall THROUGH to it (no throw)
1256
+ // so an otherwise-good model still answers.
1257
+ if (isToolCallingUnsupported(err)) {
1258
+ // Remember it for the REST of this turn so later steps go straight
1259
+ // to the JSON transport instead of re-paying the failing call.
1260
+ nativeToolsRejected.add(nativeKey);
1261
+ logger.warn(' ⚠️ Model does not support native tool calling — retrying this step over the JSON tool transport.');
1262
+ }
1263
+ else {
1264
+ throw err;
1086
1265
  }
1087
- throw err;
1088
1266
  }
1089
1267
  }
1090
1268
  // JSON fallback transport: flatten the thread into one prompt with
@@ -1094,18 +1272,22 @@ export class ChatCommand extends BaseCommand {
1094
1272
  let raw;
1095
1273
  if (typeof prov.generateStream === 'function') {
1096
1274
  const chunks = [];
1097
- await prov.generateStream(prompt, { ...options, model: mdl, signal: abort }, (t) => chunks.push(t));
1275
+ await prov.generateStream(prompt, { ...options, model: effectiveModel, signal: abort }, (t) => chunks.push(t));
1098
1276
  raw = chunks.join('');
1099
1277
  }
1100
1278
  else {
1101
- raw = await prov.generate(prompt, { ...options, model: mdl, signal: abort });
1279
+ raw = await prov.generate(prompt, { ...options, model: effectiveModel, signal: abort });
1102
1280
  }
1103
1281
  const { text, calls } = extractFallbackToolCalls(raw);
1104
1282
  confuseCheck(text);
1105
- return { content: text, toolCalls: calls };
1283
+ return answered({ content: text, toolCalls: calls });
1106
1284
  };
1107
1285
  try {
1108
- return await tryGenerate(session.provider, session.type, session.model);
1286
+ // Same-provider transient retry FIRST (see the helper's contract): a
1287
+ // 503 spike at a shared endpoint must not become a dead run. Failover
1288
+ // only helps if a DIFFERENT provider exists — and it also hides the real
1289
+ // failure from the user while parking a healthy provider for 120s.
1290
+ return await generateWithTransientRetry(() => tryGenerate(session.provider, session.type, session.model), abort, (attempt, err) => logger.warn(` ⏳ ${session.provider.name} transient failure (attempt ${attempt}) — retrying shortly: ${err instanceof Error ? err.message.split('\n')[0] : String(err)}`));
1109
1291
  }
1110
1292
  catch (err) {
1111
1293
  // Auto mode: fail over across the ranked candidates (never stuck).
@@ -1149,7 +1331,11 @@ export class ChatCommand extends BaseCommand {
1149
1331
  const resp = await tryGenerate(next.provider, next.type, next.model);
1150
1332
  session.type = next.type;
1151
1333
  session.provider = next.provider;
1152
- session.model = next.model;
1334
+ // Keep the model that actually answered: `next.model` is often
1335
+ // undefined ("provider default"), and assigning it here used to
1336
+ // erase the resolved id that tryGenerate just recorded.
1337
+ if (next.model && next.model !== 'default')
1338
+ session.model = next.model;
1153
1339
  logger.success(`✅ Auto failover: answered from ${next.provider.name} (${next.model}) after ${firstType} failed`);
1154
1340
  return resp;
1155
1341
  }
@@ -1184,10 +1370,22 @@ export class ChatCommand extends BaseCommand {
1184
1370
  // deliver THAT instead of failing the whole turn. A confusing answer
1185
1371
  // still beats an error banner in a messaging app; the confusion is
1186
1372
  // now also visible in the chat trace for post-mortem.
1373
+ // The old behavior here — DELIVER the confused reply as if it were the
1374
+ // answer (`return { content: confusedReply }`) — was the worst of both
1375
+ // worlds. It shipped contract meta-talk to the sender ("Sure, I can
1376
+ // help you with suggestions and followups. Please provide me with more
1377
+ // details…") AND marked the turn a SUCCESS, so the loop cached it for
1378
+ // an hour and every retry inside that window replayed the same
1379
+ // deflection. Live evidence: that exact string sat in
1380
+ // ~/.nuvira/cache.json with `model: "default"`.
1381
+ //
1382
+ // Now it stays a FAILURE: rethrow so the tool loop surfaces the
1383
+ // sanitized, user-facing line with `generationFailed: true` (never
1384
+ // cached, never persisted), while the raw reply is preserved in the
1385
+ // log and the reasoning trace for post-mortem.
1187
1386
  const confusedReply = err.confusedReply;
1188
1387
  if (typeof confusedReply === 'string' && confusedReply.trim()) {
1189
- logger.warn(' ⚠️ No alternative model answered — delivering the raw reply (contract-confusion fallback).');
1190
- return { content: confusedReply, toolCalls: [] };
1388
+ logger.warn(` ⚠️ No alternative model answered — contract-confusion reply suppressed (${confusedReply.length} chars, kept in the trace): ${confusedReply.slice(0, 160)}`);
1191
1389
  }
1192
1390
  throw err;
1193
1391
  }
@@ -1398,37 +1596,15 @@ export class ChatCommand extends BaseCommand {
1398
1596
  // registry may still mark it unavailable (learned from the failure), and
1399
1597
  // blindly re-admitting would fail again on the very next message. Recovery
1400
1598
  // is discovered in SECONDS (a 1-token spot-check), not by re-failing.
1401
- // NOTE: iterate a SNAPSHOT — the loop mutates the set (delete + re-add),
1402
- // and Set iteration can revisit a re-added key, double-spot-checking.
1403
- // Bounded: at most one spot-check per provider per 60s (the exclusion is
1404
- // re-armed on failure), and only for registry-blocked providers.
1405
- for (const providerType of [...this.sessionTransientFailedProviders]) {
1406
- const expiresAt = this.sessionFailedProviders.get(providerType);
1407
- // Skip still-active exclusions and already-cleared providers.
1408
- if (expiresAt !== undefined && expiresAt > exclusionTime)
1409
- continue;
1410
- this.sessionTransientFailedProviders.delete(providerType);
1411
- this.sessionFailedProviders.delete(providerType);
1412
- try {
1413
- const registry = getModelRegistry();
1414
- // Only re-verify when the registry still believes the provider is dead
1415
- // (unavailable/parked) — a healthy entry means it recovered already.
1416
- if (!registry.getBlockedProviders().includes(providerType))
1417
- continue;
1418
- const desired = getAutoRouter().resolveModel(providerType, 'chat', this.configManager);
1419
- const outcome = await spotCheckModel(providerType, desired, this.configManager);
1420
- // 'skipped' = the model was VERIFIED recently (within the spot-check
1421
- // throttle) — that's healthy, so treat it as a pass too.
1422
- if (outcome !== 'verified' && outcome !== 'skipped') {
1423
- // Still down — keep it excluded for another transient window.
1424
- this.sessionFailedProviders.set(providerType, Date.now() + TRANSIENT_FAILURE_EXCLUSION_MS);
1425
- this.sessionTransientFailedProviders.add(providerType);
1426
- }
1427
- }
1428
- catch {
1429
- // Best-effort — re-verification must never break routing.
1430
- }
1431
- }
1599
+ // The sweep itself now lives in `learning/provider-revival.ts` so every
1600
+ // entry path shares ONE implementation (chat was previously the only path
1601
+ // that read the transient-failure marker at all — the orchestrator, edit,
1602
+ // execute, plan and resilient-call allocated it and never acted on it, so a
1603
+ // recovered provider stayed excluded for the rest of their runs).
1604
+ await sweepTransientFailures({
1605
+ ...collectionRevivalStore(this.sessionFailedProviders, this.sessionTransientFailedProviders),
1606
+ resolveProbeModel: (provider) => getAutoRouter().resolveModel(provider, 'chat', this.configManager),
1607
+ }, this.configManager, { agentType: 'chat' });
1432
1608
  const excluded = new Set([
1433
1609
  ...excludeProviders,
1434
1610
  ...[...this.sessionFailedProviders.keys()].filter((p) => isActiveExclusion(p)),