@symerian/symi 2.6.0 → 2.6.1

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 (188) hide show
  1. package/dist/{agents-Bi50kp6u.js → agents-DQIz-_on.js} +4 -4
  2. package/dist/{agents.config-Duce7lam.js → agents.config-CIJRaVWl.js} +1 -1
  3. package/dist/{agents.config-BcTeP94V.js → agents.config-D8WPDf-m.js} +1 -1
  4. package/dist/{audio-preflight-DHTaS5U1.js → audio-preflight-BVaaZWkg.js} +4 -4
  5. package/dist/{audio-preflight-C40mKAp7.js → audio-preflight-CPBOQV4I.js} +4 -4
  6. package/dist/{auth-choice-BFIBR4l9.js → auth-choice-BqFbNDuP.js} +1 -1
  7. package/dist/{auth-choice-Dyq-0MNq.js → auth-choice-DTDyJL1r.js} +1 -1
  8. package/dist/{banner-D50f_0qf.js → banner-DYDCxnDL.js} +1 -1
  9. package/dist/build-info.json +3 -3
  10. package/dist/bundled/boot-md/handler.js +6 -6
  11. package/dist/bundled/session-memory/handler.js +6 -6
  12. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  13. package/dist/{channel-options-BvBcjqyk.js → channel-options-CvHSm_Kx.js} +1 -1
  14. package/dist/{channel-options-BFqaanEt.js → channel-options-vBCJhJB7.js} +1 -1
  15. package/dist/{channel-web-BQtFg4IP.js → channel-web-CM2LyWZW.js} +1 -1
  16. package/dist/{channels-cli-BYFQdWnL.js → channels-cli-D5H3Wcho.js} +4 -4
  17. package/dist/{channels-cli-BuTH-iVi.js → channels-cli-DzOjNNFn.js} +4 -4
  18. package/dist/{chrome-DYZwl5Gv.js → chrome-D2SKJnR7.js} +5 -5
  19. package/dist/{chrome-CDJYxX5a.js → chrome-DkaXoP36.js} +5 -5
  20. package/dist/{cli-r2L-UK6y.js → cli-8hqssnRJ.js} +1 -1
  21. package/dist/{cli-eOBlVLcC.js → cli-DNZwCDRe.js} +1 -1
  22. package/dist/{command-registry-D-pwcAIW.js → command-registry-CHtN2HeK.js} +9 -9
  23. package/dist/{completion-cli-DMO2OGTm.js → completion-cli-C8y_J5KC.js} +1 -1
  24. package/dist/{completion-cli-DIx7KyOG.js → completion-cli-Cov6N3BO.js} +2 -2
  25. package/dist/{config-cli-BsDxqYDU.js → config-cli-B2REEd2l.js} +1 -1
  26. package/dist/{config-cli-seaVWVru.js → config-cli-BwO0xAbV.js} +1 -1
  27. package/dist/{configure-BmPwuHXL.js → configure-BWSYSi2-.js} +3 -3
  28. package/dist/{configure-CqbKA0_V.js → configure-C6yNe33U.js} +3 -3
  29. package/dist/{deliver-BH0l3UKW.js → deliver-C-37cZUe.js} +1 -1
  30. package/dist/{deliver-dODxSv3b.js → deliver-C46-vyqg.js} +1 -1
  31. package/dist/{doctor-completion-DMjs7-Qa.js → doctor-completion-BKKzstt6.js} +1 -1
  32. package/dist/{doctor-completion-C2IV3lKi.js → doctor-completion-D7CDLFLg.js} +1 -1
  33. package/dist/entry.js +1 -1
  34. package/dist/extensionAPI.js +6 -6
  35. package/dist/{gateway-cli-C-J_s559.js → gateway-cli-BhFM4Bkm.js} +9 -9
  36. package/dist/{gateway-cli-BanaeKQ_.js → gateway-cli-Datohp3m.js} +9 -9
  37. package/dist/{glass-ui-ws-DK7x3Tz7.js → glass-ui-ws-BzqfD_wX.js} +7 -7
  38. package/dist/{glass-ui-ws-DUzp9m0D.js → glass-ui-ws-D9yvYULL.js} +7 -7
  39. package/dist/{health-DK6rAOhC.js → health-BjwDRAdd.js} +1 -1
  40. package/dist/{health-BpHgCv-u.js → health-C5XJPwpt.js} +1 -1
  41. package/dist/{hooks-cli-Cin_3tFg.js → hooks-cli-CafMq9Vr.js} +2 -2
  42. package/dist/{hooks-cli-D-75G_66.js → hooks-cli-U12oVyLH.js} +2 -2
  43. package/dist/{image-CXu8W39c.js → image-CuzFLQWC.js} +1 -1
  44. package/dist/{image-CHzdaNJ4.js → image-DcpMiprB.js} +1 -1
  45. package/dist/index.js +6 -6
  46. package/dist/llm-slug-generator.js +6 -6
  47. package/dist/{models-CeKIXf5B.js → models-Bo4iHJy-.js} +2 -2
  48. package/dist/{models-cli-2NcPKR9A.js → models-cli-6aNi3eN9.js} +2 -2
  49. package/dist/{models-cli-DN4AVlpI.js → models-cli-B5vh-XK8.js} +3 -3
  50. package/dist/{onboard-BcxDiUl_.js → onboard-Bm-pmstf.js} +2 -2
  51. package/dist/{onboard-channels-HPxu77wp.js → onboard-channels-BRd1cXye.js} +1 -1
  52. package/dist/{onboard-channels-DS6s341R.js → onboard-channels-CUl5U8kV.js} +1 -1
  53. package/dist/{onboard-BukRqcRH.js → onboard-f-GJ26Ix.js} +2 -2
  54. package/dist/{onboarding-B8uz24jt.js → onboarding-CvBDWlBJ.js} +3 -3
  55. package/dist/{onboarding-DI-o_sax.js → onboarding-DIVKvosg.js} +3 -3
  56. package/dist/{onboarding.finalize-CfE_AEto.js → onboarding.finalize-DJX6mSLa.js} +6 -6
  57. package/dist/{onboarding.finalize-Bn2e61yb.js → onboarding.finalize-WSac-JKd.js} +5 -5
  58. package/dist/{pi-embedded-B5qBa69e.js → pi-embedded-BmbbC1Sb.js} +133 -27
  59. package/dist/{pi-embedded-helpers-lgx_U5KS.js → pi-embedded-helpers-B8kqLWns.js} +4 -4
  60. package/dist/{pi-embedded-helpers-pubKo8HQ.js → pi-embedded-helpers-CfqDGQ9J.js} +4 -4
  61. package/dist/{plugin-registry-NIUxULTk.js → plugin-registry-2zUJMasm.js} +1 -1
  62. package/dist/{plugin-registry-cj99EI0k.js → plugin-registry-5yf-hu_W.js} +1 -1
  63. package/dist/plugin-sdk/{accounts-BtaOa4z_.js → accounts-BToL3HlP.js} +1 -1
  64. package/dist/plugin-sdk/{accounts-Ddm33hQm.js → accounts-D9zGZU5t.js} +3 -3
  65. package/dist/plugin-sdk/{accounts-s-AdhXVR.js → accounts-Dtszw3Zn.js} +1 -1
  66. package/dist/plugin-sdk/{active-listener-BXYeALs0.js → active-listener-bEk__wbB.js} +1 -1
  67. package/dist/plugin-sdk/{agent-scope-CYYpcO9W.js → agent-scope-C3gMMKCU.js} +2 -2
  68. package/dist/plugin-sdk/agents/model-token-filter.d.ts +10 -0
  69. package/dist/plugin-sdk/agents/pi-tools.validate-wrapper.d.ts +23 -0
  70. package/dist/plugin-sdk/agents/pi-tools.validate.d.ts +26 -0
  71. package/dist/plugin-sdk/agents/tool-loop-detection.d.ts +3 -1
  72. package/dist/plugin-sdk/{api-key-rotation-D_sMvI5W.js → api-key-rotation-CVBMpnPc.js} +1 -1
  73. package/dist/plugin-sdk/{audio-preflight-VpItkiy3.js → audio-preflight-DoQQKlxa.js} +24 -24
  74. package/dist/plugin-sdk/{bindings-C7hRtgYW.js → bindings-BbwoUGPx.js} +2 -2
  75. package/dist/plugin-sdk/{channel-activity-DoC1xtDu.js → channel-activity-Ji7f0gqq.js} +1 -1
  76. package/dist/plugin-sdk/{channel-web-CSd16cDi.js → channel-web-DPyyTvFo.js} +22 -22
  77. package/dist/plugin-sdk/{chrome-B7RdxmJ0.js → chrome-C7c_0I5M.js} +3 -3
  78. package/dist/plugin-sdk/{chunk-Dw2XBYXv.js → chunk-jvk9axTQ.js} +1 -1
  79. package/dist/plugin-sdk/{command-format-GKSevep4.js → command-format-DSdvQ_M5.js} +1 -1
  80. package/dist/plugin-sdk/{commands-registry-COIaslGl.js → commands-registry-CQFbmUMs.js} +4 -4
  81. package/dist/plugin-sdk/config/model-profiles.d.ts +2 -0
  82. package/dist/plugin-sdk/{config-KlTNfkFF.js → config-DDkdiUOR.js} +9 -9
  83. package/dist/plugin-sdk/{deliver-BZ99UKQq.js → deliver-BZ6iNLl7.js} +10 -10
  84. package/dist/plugin-sdk/{diagnostic-05pm5Rxi.js → diagnostic-mFf4i4G9.js} +1 -1
  85. package/dist/plugin-sdk/{image-CLOPx7yW.js → image-BOYy0Ump.js} +4 -4
  86. package/dist/plugin-sdk/{image-ops-BlQR__MN.js → image-ops-Bnp6LXEx.js} +1 -1
  87. package/dist/plugin-sdk/index.js +53 -53
  88. package/dist/plugin-sdk/infra/diagnostic-events.d.ts +1 -1
  89. package/dist/plugin-sdk/{ir-BJ6BHE5b.js → ir-Fb3qpcis.js} +4 -4
  90. package/dist/plugin-sdk/{local-roots-BHLNSI8U.js → local-roots-Ckk1QfzI.js} +3 -3
  91. package/dist/plugin-sdk/logging/diagnostic-session-state.d.ts +2 -0
  92. package/dist/plugin-sdk/logging/diagnostic.d.ts +1 -1
  93. package/dist/plugin-sdk/{login-DQMXuxOk.js → login-Bh3DZPam.js} +7 -7
  94. package/dist/plugin-sdk/{login-qr-BjVZSoCi.js → login-qr-DbR7odSr.js} +9 -9
  95. package/dist/plugin-sdk/{manager-CBSBFuFz.js → manager-DckktAQ3.js} +8 -8
  96. package/dist/plugin-sdk/{manifest-registry-CPnHl_K3.js → manifest-registry-B3ugY9-f.js} +1 -1
  97. package/dist/plugin-sdk/{markdown-tables-BoYFajMu.js → markdown-tables-Dfaqilz6.js} +1 -1
  98. package/dist/plugin-sdk/{message-channel-COTAJzHd.js → message-channel-BdI5Ra9S.js} +1 -1
  99. package/dist/plugin-sdk/{model-selection-CsbEfrS0.js → model-selection-OpU8HN50.js} +4 -4
  100. package/dist/plugin-sdk/{outbound-attachment-CnslKL38.js → outbound-attachment-DnVQfTG2.js} +2 -2
  101. package/dist/plugin-sdk/{outbound-B0e8KdaR.js → outbound-rF6G8Xpr.js} +7 -7
  102. package/dist/plugin-sdk/{pi-auth-json-qWi7ZIYV.js → pi-auth-json-CJk8t14T.js} +5 -5
  103. package/dist/plugin-sdk/{pi-embedded-helpers-CW630epe.js → pi-embedded-helpers-BveUP4hk.js} +17 -17
  104. package/dist/plugin-sdk/{plugins-BNByVCIH.js → plugins-BbAvhC25.js} +4 -4
  105. package/dist/plugin-sdk/{pw-ai-CnbPIPY9.js → pw-ai-DjGUsee-.js} +8 -8
  106. package/dist/plugin-sdk/{qmd-manager-CH0XbIHf.js → qmd-manager-mjKcdwVr.js} +4 -4
  107. package/dist/plugin-sdk/{registry-D0xTnUWt.js → registry--_pGht6S.js} +2 -2
  108. package/dist/plugin-sdk/{replies-LLcQL3w6.js → replies-fI39rPGa.js} +3 -3
  109. package/dist/plugin-sdk/{reply-CkqSfQZN.js → reply-QAcAd9ev.js} +193 -87
  110. package/dist/plugin-sdk/{reply-prefix-uxfMZW4p.js → reply-prefix-BHuV5t70.js} +1 -1
  111. package/dist/plugin-sdk/{resolve-outbound-target-BiyAyTWz.js → resolve-outbound-target-BkCUbYGV.js} +2 -2
  112. package/dist/plugin-sdk/{resolve-route-B3CCBumQ.js → resolve-route-D3JH_D2N.js} +3 -3
  113. package/dist/plugin-sdk/{retry-CwQ_iIj8.js → retry-ilSJqnz9.js} +1 -1
  114. package/dist/plugin-sdk/{runner-CGBT7tgF.js → runner-BVqnEfNe.js} +9 -9
  115. package/dist/plugin-sdk/{send-C5h_YxNb.js → send-BHbXh8Ly.js} +7 -7
  116. package/dist/plugin-sdk/{send-pYqe432l.js → send-BMfJIhCk.js} +6 -6
  117. package/dist/plugin-sdk/{send-B2CEnVLL.js → send-BtANzsAo.js} +6 -6
  118. package/dist/plugin-sdk/{send-CjOBB3Vo.js → send-Bxdu6ZZy.js} +10 -10
  119. package/dist/plugin-sdk/{send-CRsR8-vO.js → send-D6LMZJ_h.js} +10 -10
  120. package/dist/plugin-sdk/{session-BsOrxiMj.js → session-kI0tzViQ.js} +4 -4
  121. package/dist/plugin-sdk/{skill-commands-ff_01_r3.js → skill-commands-DCNXVERE.js} +5 -5
  122. package/dist/plugin-sdk/{skills-_yTP47Cd.js → skills-B1GeRYlu.js} +7 -7
  123. package/dist/plugin-sdk/{sqlite-CxAR5ttJ.js → sqlite-Cq_7Cg4E.js} +1 -1
  124. package/dist/plugin-sdk/{store-BdrNabcU.js → store-Do3t33-c.js} +2 -2
  125. package/dist/plugin-sdk/{subsystem-B2uDN3TV.js → subsystem-Coz2AgU8.js} +1 -1
  126. package/dist/plugin-sdk/{tables-DNwXwNFa.js → tables-DR0NmBeH.js} +1 -1
  127. package/dist/plugin-sdk/{target-errors-Paro1BjP.js → target-errors-B7YyMnIi.js} +2 -2
  128. package/dist/plugin-sdk/{thinking-CXqf7WTe.js → thinking-DCNUIAHY.js} +5 -5
  129. package/dist/plugin-sdk/{tokens-bC3UVmVH.js → tokens-CWMflosr.js} +1 -1
  130. package/dist/plugin-sdk/{tool-images-HJ2sfZDV.js → tool-images-D7Lno-TE.js} +2 -2
  131. package/dist/plugin-sdk/{tool-loop-detection-BVA6fax-.js → tool-loop-detection-DU5sTIKg.js} +55 -5
  132. package/dist/plugin-sdk/web-DaTTL9M0.js +65 -0
  133. package/dist/plugin-sdk/{whatsapp-actions-DfseosPO.js → whatsapp-actions-CcBzDuL-.js} +21 -21
  134. package/dist/{plugins-cli-CcjxxESJ.js → plugins-cli-D4eRESV2.js} +2 -2
  135. package/dist/{plugins-cli-D8hhTHZD.js → plugins-cli-DksVl33N.js} +2 -2
  136. package/dist/{program-D09h71pS.js → program-BjORH7Cc.js} +7 -7
  137. package/dist/{program-context-CLJSWBZr.js → program-context-DaNGrTOm.js} +17 -17
  138. package/dist/{prompt-select-styled-zRUqu0c8.js → prompt-select-styled-QaS2zul_.js} +4 -4
  139. package/dist/{prompt-select-styled-DQqZEGoo.js → prompt-select-styled-YOj4xigd.js} +4 -4
  140. package/dist/{provider-auth-helpers-16r2WHNe.js → provider-auth-helpers-BAGT_RXV.js} +1 -1
  141. package/dist/{provider-auth-helpers-LzJ2WQIc.js → provider-auth-helpers-DCEbm2hz.js} +1 -1
  142. package/dist/{push-apns-B5xZKIxK.js → push-apns-BECodU1i.js} +1 -1
  143. package/dist/{push-apns-DJddAK3u.js → push-apns-Bek3ANJa.js} +1 -1
  144. package/dist/{pw-ai-De-KR9_s.js → pw-ai-1htA-NnS.js} +1 -1
  145. package/dist/{pw-ai-B5asscAD.js → pw-ai-m0mj2KWK.js} +1 -1
  146. package/dist/{register.agent-D7NKuUkY.js → register.agent-B34lxx7F.js} +5 -5
  147. package/dist/{register.agent-CP_sigRh.js → register.agent-DJHQo-Iq.js} +6 -6
  148. package/dist/{register.configure-BEsGd0PR.js → register.configure-BdhhIzb0.js} +6 -6
  149. package/dist/{register.configure-BjRLNatb.js → register.configure-DC_-t5kj.js} +6 -6
  150. package/dist/{register.maintenance-DD6TNFtV.js → register.maintenance-CITur3O_.js} +8 -8
  151. package/dist/{register.maintenance-CN6KUuX7.js → register.maintenance-cs-A4kHF.js} +7 -7
  152. package/dist/{register.message-DEUcNly1.js → register.message--RhtnEYn.js} +2 -2
  153. package/dist/{register.message-DMVC_Sqm.js → register.message-PIaHm2pZ.js} +2 -2
  154. package/dist/{register.onboard-CP6RP90V.js → register.onboard-CTJQoDcK.js} +4 -4
  155. package/dist/{register.onboard-J1pgV7lz.js → register.onboard-DucZgrF7.js} +4 -4
  156. package/dist/{register.setup-Dhc3jKpK.js → register.setup-2ZiUN7ui.js} +4 -4
  157. package/dist/{register.setup-BeHpW3xI.js → register.setup-DD4Rgkt9.js} +4 -4
  158. package/dist/{register.status-health-sessions-b-lWNsTM.js → register.status-health-sessions-BqD7L8XL.js} +3 -3
  159. package/dist/{register.status-health-sessions-DDkC0aoW.js → register.status-health-sessions-sLgA92t7.js} +3 -3
  160. package/dist/{register.subclis-BJqiT8Q2.js → register.subclis-B2dGWFur.js} +9 -9
  161. package/dist/{reply-D40cmAci.js → reply-DYnTEYoa.js} +119 -13
  162. package/dist/{run-main-BruREeZ6.js → run-main-DWmu2b6D.js} +14 -14
  163. package/dist/{runner-DUBExAb5.js → runner-BcQ0sF9T.js} +1 -1
  164. package/dist/{runner-WAG0M5s9.js → runner-CU9l0uJh.js} +1 -1
  165. package/dist/{server-methods-K-0MHs8x.js → server-methods-C8EWZt2g.js} +7 -7
  166. package/dist/{server-methods-Cyw_WS3A.js → server-methods-vDGoM3xL.js} +7 -7
  167. package/dist/{server-node-events-89R9Ryky.js → server-node-events-CKi12bol.js} +2 -2
  168. package/dist/{server-node-events-RA8RurtC.js → server-node-events-DFwGbkcO.js} +2 -2
  169. package/dist/{status-DHJLMwQN.js → status--iNVOTMO.js} +2 -2
  170. package/dist/{status-CtNKWuzg.js → status-B1_iHrOg.js} +2 -2
  171. package/dist/{status-BSMEjz4q.js → status-BQcdARV4.js} +1 -1
  172. package/dist/{status-Kv_hsY8N.js → status-DiX0DAtH.js} +1 -1
  173. package/dist/{subagent-registry-Cb5e_x99.js → subagent-registry-CXrOOgPW.js} +119 -13
  174. package/dist/{tool-loop-detection-BgbtzUGc.js → tool-loop-detection-C7TCF2V2.js} +53 -3
  175. package/dist/{tool-loop-detection-BU3fbtCd.js → tool-loop-detection-D7qjFnRh.js} +53 -3
  176. package/dist/{tool-loop-detection-B6j1r-Wk.js → tool-loop-detection-DPVtQOfM.js} +53 -3
  177. package/dist/{tool-loop-detection-D0kUzUGu.js → tool-loop-detection-DR_rrIA1.js} +53 -3
  178. package/dist/{unified-runner-CkJLTsTK.js → unified-runner-CulJZMxc.js} +133 -27
  179. package/dist/{update-cli-Bl66LJZ4.js → update-cli-560gprSp.js} +7 -7
  180. package/dist/{update-cli-CrRBoiVU.js → update-cli-D1pLX3eo.js} +8 -8
  181. package/dist/{update-runner-DxpSPK-f.js → update-runner-BQxFFCGc.js} +1 -1
  182. package/dist/{update-runner-FgrqoxvV.js → update-runner-CLKHrONW.js} +1 -1
  183. package/dist/{web-Czp0JS6-.js → web-Bqrgp43v.js} +1 -1
  184. package/dist/{web-D99WHLTL.js → web-D4qJ9XKP.js} +6 -6
  185. package/dist/{web-BYRKX5Ln.js → web-Dr5cOn-1.js} +2 -2
  186. package/dist/{web-RePh7lRy.js → web-hHX9a9YO.js} +6 -6
  187. package/package.json +1 -1
  188. package/dist/plugin-sdk/web-DdTTil50.js +0 -65
@@ -1,6 +1,6 @@
1
1
  import { u as resolveGatewayPort } from "./paths-Cqn-zk3M.js";
2
2
  import { B as theme, k as info, z as isRich } from "./utils-B-0b9bGM.js";
3
- import { B as summarizeRestartSentinel, Jt as sha256HexPrefix, _t as formatUsageReportLines, a as buildChannelSummary, gt as loadProviderUsageSummary, h as listAgentsForGateway, m as classifySessionKey, o as buildChannelAccountSnapshot, pr as lookupContextTokens, s as formatChannelAllowFrom, wr as peekSystemEvents, x as resolveSessionModelRef, z as readRestartSentinel } from "./reply-D40cmAci.js";
3
+ import { B as summarizeRestartSentinel, Jt as sha256HexPrefix, _t as formatUsageReportLines, a as buildChannelSummary, gt as loadProviderUsageSummary, h as listAgentsForGateway, m as classifySessionKey, o as buildChannelAccountSnapshot, pr as lookupContextTokens, s as formatChannelAllowFrom, wr as peekSystemEvents, x as resolveSessionModelRef, z as readRestartSentinel } from "./reply-DYnTEYoa.js";
4
4
  import { S as parseAgentSessionKey } from "./session-key-DCt45XZa.js";
5
5
  import { t as resolveSymiPackageRoot } from "./symi-root-CrGJbkzf.js";
6
6
  import { n as runExec } from "./exec-CWkblSrI.js";
@@ -28,7 +28,7 @@ import { t as formatRuntimeStatusWithDetails } from "./runtime-status-CR9445g5.j
28
28
  import { t as readLastGatewayErrorLine } from "./diagnostics-BAMlsVVX.js";
29
29
  import { t as renderTable } from "./table-BTgkRafz.js";
30
30
  import { a as resolveGatewayProbeAuth$1, c as probeGateway, t as runSecurityAudit } from "./audit-CrITRV6w.js";
31
- import { a as resolveHeartbeatSummaryForAgent, t as formatHealthChannelLines } from "./health-DK6rAOhC.js";
31
+ import { a as resolveHeartbeatSummaryForAgent, t as formatHealthChannelLines } from "./health-BjwDRAdd.js";
32
32
  import { g as resolveUpdateChannelDisplay, i as formatGitInstallLabel, m as normalizeUpdateChannel, t as checkUpdateStatus } from "./update-check-CtckACbb.js";
33
33
  import { t as resolveNodeService } from "./node-service-Cxz4e-Qd.js";
34
34
  import { n as redactSecrets, t as formatGatewayAuthUsed } from "./format-yQZNwAF2.js";
@@ -9,7 +9,7 @@ import { c as resolveAgentWorkspaceDir } from "./agent-scope-D-jRCY0d.js";
9
9
  import { t as buildWorkspaceSkillStatus } from "./skills-status-DKXJ-tbi.js";
10
10
  import { H as VERSION, i as loadConfig, o as readConfigFileSnapshot } from "./config-DHBLS1Hl.js";
11
11
  import { n as callGateway, t as buildGatewayConnectionDetails } from "./call-BcE47FtD.js";
12
- import { En as peekSystemEvents, St as summarizeRestartSentinel, cn as buildChannelAccountSnapshot, in as formatUsageReportLines, ln as formatChannelAllowFrom, nt as sha256HexPrefix, rn as loadProviderUsageSummary, sn as buildChannelSummary, xt as readRestartSentinel } from "./subagent-registry-Cb5e_x99.js";
12
+ import { En as peekSystemEvents, St as summarizeRestartSentinel, cn as buildChannelAccountSnapshot, in as formatUsageReportLines, ln as formatChannelAllowFrom, nt as sha256HexPrefix, rn as loadProviderUsageSummary, sn as buildChannelSummary, xt as readRestartSentinel } from "./subagent-registry-CXrOOgPW.js";
13
13
  import { F as resolveMainSessionKey, j as resolveFreshSessionTotalTokens, o as loadSessionStore } from "./sessions-CJXnZVjR.js";
14
14
  import { n as listChannelPlugins } from "./plugins-CwSlLxM8.js";
15
15
  import { o as getTailnetHostname, s as readTailscaleStatusJson } from "./tailscale-CbbvYNVw.js";
@@ -29,7 +29,7 @@ import { t as formatRuntimeStatusWithDetails } from "./runtime-status-hFVEC3wO.j
29
29
  import { t as readLastGatewayErrorLine } from "./diagnostics-CS1ov_hH.js";
30
30
  import { t as renderTable } from "./table-D01d2GuY.js";
31
31
  import { a as resolveGatewayProbeAuth$1, c as probeGateway, t as runSecurityAudit } from "./audit-Byo5jCLN.js";
32
- import { o as resolveHeartbeatSummaryForAgent, t as formatHealthChannelLines } from "./health-BpHgCv-u.js";
32
+ import { o as resolveHeartbeatSummaryForAgent, t as formatHealthChannelLines } from "./health-C5XJPwpt.js";
33
33
  import { g as resolveUpdateChannelDisplay, i as formatGitInstallLabel, m as normalizeUpdateChannel, t as checkUpdateStatus } from "./update-check-ZdimP1aU.js";
34
34
  import { t as resolveNodeService } from "./node-service-fcZExd22.js";
35
35
  import { n as redactSecrets, t as formatGatewayAuthUsed } from "./format-DVLB9DNB.js";
@@ -1,7 +1,7 @@
1
1
  import { o as createSubsystemLogger } from "./entry.js";
2
2
  import { D as resolveDefaultAgentWorkspaceDir, c as resolveAgentWorkspaceDir, l as resolveDefaultAgentId } from "./agent-scope-D-jRCY0d.js";
3
3
  import { i as loadConfig } from "./config-DHBLS1Hl.js";
4
- import { m as loadSymiPlugins, p as createPluginLoaderLogger } from "./subagent-registry-Cb5e_x99.js";
4
+ import { m as loadSymiPlugins, p as createPluginLoaderLogger } from "./subagent-registry-CXrOOgPW.js";
5
5
 
6
6
  //#region src/plugins/status.ts
7
7
  const log = createSubsystemLogger("plugins");
@@ -1,4 +1,4 @@
1
- import { dt as createPluginLoaderLogger, ft as loadSymiPlugins } from "./reply-D40cmAci.js";
1
+ import { dt as createPluginLoaderLogger, ft as loadSymiPlugins } from "./reply-DYnTEYoa.js";
2
2
  import { t as createSubsystemLogger } from "./subsystem-D9vIQve0.js";
3
3
  import { D as resolveDefaultAgentWorkspaceDir, c as resolveAgentWorkspaceDir, l as resolveDefaultAgentId } from "./agent-scope-CgUHAtCo.js";
4
4
  import { i as loadConfig } from "./config-CHwyw6l5.js";
@@ -32327,7 +32327,8 @@ const BUILTIN_PROFILES = [
32327
32327
  "<end_of_turn>"
32328
32328
  ],
32329
32329
  suppressMonologue: true,
32330
- hasStructuredThinking: false
32330
+ hasStructuredThinking: false,
32331
+ validateToolArgs: true
32331
32332
  },
32332
32333
  promptAdditions: [
32333
32334
  "CRITICAL OUTPUT RULES:",
@@ -32337,7 +32338,8 @@ const BUILTIN_PROFILES = [
32337
32338
  "- Do not write verification checklists, self-assessments, or numbered assessment lists after your answer.",
32338
32339
  "- Do not write 'Verification:', 'Requirements:', 'Status Check:', or 'Everything is correct'.",
32339
32340
  "- If you need to verify your work, do so before writing your response, not after.",
32340
- "- Send brief progress updates on long tasks so the user knows you are active."
32341
+ "- Send brief progress updates on long tasks so the user knows you are active.",
32342
+ "- When calling tools, provide arguments as clean values only. Never include <|, |>, or XML-like tags in tool argument values. For exec/bash commands, provide only valid shell syntax."
32341
32343
  ],
32342
32344
  ui: {
32343
32345
  badge: "Local",
@@ -32361,7 +32363,8 @@ const BUILTIN_PROFILES = [
32361
32363
  filters: {
32362
32364
  stripPatterns: [],
32363
32365
  suppressMonologue: false,
32364
- hasStructuredThinking: true
32366
+ hasStructuredThinking: true,
32367
+ validateToolArgs: false
32365
32368
  },
32366
32369
  promptAdditions: [],
32367
32370
  ui: {
@@ -32395,7 +32398,8 @@ const BUILTIN_PROFILES = [
32395
32398
  "^\\s*<bos>\\s*"
32396
32399
  ],
32397
32400
  suppressMonologue: true,
32398
- hasStructuredThinking: false
32401
+ hasStructuredThinking: false,
32402
+ validateToolArgs: true
32399
32403
  },
32400
32404
  promptAdditions: [
32401
32405
  "CRITICAL OUTPUT RULES:",
@@ -32406,7 +32410,8 @@ const BUILTIN_PROFILES = [
32406
32410
  "- Do not write 'Verification:', 'Requirements:', 'Status Check:', or 'Everything is correct'.",
32407
32411
  "- If you need to verify your work, do so before writing your response, not after.",
32408
32412
  "- Keep planning and reasoning internal. Only speak when you have a result or need user input.",
32409
- "- When working on multi-step tasks, send a brief one-line status update so the user knows you are active."
32413
+ "- When working on multi-step tasks, send a brief one-line status update so the user knows you are active.",
32414
+ "- When calling tools, provide arguments as clean values only. Never include <|, |>, or XML-like tags in tool argument values. For exec/bash commands, provide only valid shell syntax."
32410
32415
  ],
32411
32416
  ui: {
32412
32417
  badge: "CoreWeave",
@@ -32434,7 +32439,8 @@ const FALLBACK_PROFILE = {
32434
32439
  filters: {
32435
32440
  stripPatterns: [],
32436
32441
  suppressMonologue: false,
32437
- hasStructuredThinking: false
32442
+ hasStructuredThinking: false,
32443
+ validateToolArgs: false
32438
32444
  },
32439
32445
  promptAdditions: [],
32440
32446
  ui: {
@@ -32861,6 +32867,19 @@ function stripModelTokensAggressive(text, modelId) {
32861
32867
  }
32862
32868
  return result;
32863
32869
  }
32870
+ /**
32871
+ * Clean residual fragments left after aggressive token stripping.
32872
+ * After `<|` and `|>` are removed, orphaned `<`, `"`, `|` can remain
32873
+ * at string boundaries (e.g., `<<|"|uname -a` → `<"uname -a`).
32874
+ *
32875
+ * Conservative: only trims leading junk before what looks like a command,
32876
+ * and trailing junk. Does NOT touch content in the middle of the string.
32877
+ * Preserves heredoc syntax (`<<EOF`, `<<"EOF"`) via negative lookahead.
32878
+ */
32879
+ function cleanResidualTokenFragments(text) {
32880
+ if (!text) return text;
32881
+ return text.replace(/^[<"|]+(?!<[A-Z_])(?=[a-zA-Z0-9/~$.(])/gm, "").replace(/[<"|]+$/gm, "").trim();
32882
+ }
32864
32883
 
32865
32884
  //#endregion
32866
32885
  //#region src/agents/output-normalizer.ts
@@ -32946,10 +32965,14 @@ function detectRepetition(text, minBlock) {
32946
32965
  const searchStart = sampleStart + minBlock;
32947
32966
  return text.indexOf(sample, searchStart) >= 0;
32948
32967
  }
32968
+ function sanitizeStringValue(value, modelId) {
32969
+ const stripped = stripModelTokensAggressive(value, modelId);
32970
+ return isGemmaModel$1(modelId) ? cleanResidualTokenFragments(stripped) : stripped;
32971
+ }
32949
32972
  function sanitizeArgs(args, modelId) {
32950
32973
  const result = {};
32951
- for (const [key, value] of Object.entries(args)) if (typeof value === "string") result[key] = stripModelTokensAggressive(value, modelId);
32952
- else if (Array.isArray(value)) result[key] = value.map((item) => typeof item === "string" ? stripModelTokensAggressive(item, modelId) : item && typeof item === "object" ? sanitizeArgs(item, modelId) : item);
32974
+ for (const [key, value] of Object.entries(args)) if (typeof value === "string") result[key] = sanitizeStringValue(value, modelId);
32975
+ else if (Array.isArray(value)) result[key] = value.map((item) => typeof item === "string" ? sanitizeStringValue(item, modelId) : item && typeof item === "object" ? sanitizeArgs(item, modelId) : item);
32953
32976
  else if (value && typeof value === "object") result[key] = sanitizeArgs(value, modelId);
32954
32977
  else result[key] = value;
32955
32978
  return result;
@@ -35243,7 +35266,7 @@ async function recordLoopOutcome(args) {
35243
35266
  if (!args.ctx?.sessionKey) return;
35244
35267
  try {
35245
35268
  const { getDiagnosticSessionState } = await import("./diagnostic-session-state-CIjIGxEE.js").then((n) => n.n);
35246
- const { recordToolCallOutcome } = await import("./tool-loop-detection-BU3fbtCd.js");
35269
+ const { recordToolCallOutcome } = await import("./tool-loop-detection-D7qjFnRh.js");
35247
35270
  recordToolCallOutcome(getDiagnosticSessionState({
35248
35271
  sessionKey: args.ctx.sessionKey,
35249
35272
  sessionId: args.ctx?.agentId
@@ -35265,7 +35288,7 @@ async function runBeforeToolCallHook(args) {
35265
35288
  if (args.ctx?.sessionKey) {
35266
35289
  const { getDiagnosticSessionState } = await import("./diagnostic-session-state-CIjIGxEE.js").then((n) => n.n);
35267
35290
  const { logToolLoopAction } = await import("./diagnostic-Ci8Xsc_Y.js").then((n) => n.n);
35268
- const { detectToolCallLoop, recordToolCall } = await import("./tool-loop-detection-BU3fbtCd.js");
35291
+ const { detectToolCallLoop, recordToolCall } = await import("./tool-loop-detection-D7qjFnRh.js");
35269
35292
  const sessionState = getDiagnosticSessionState({
35270
35293
  sessionKey: args.ctx.sessionKey,
35271
35294
  sessionId: args.ctx?.agentId
@@ -35536,6 +35559,88 @@ function toClientToolDefinitions(tools, onClientToolCall, hookContext) {
35536
35559
  });
35537
35560
  }
35538
35561
 
35562
+ //#endregion
35563
+ //#region src/agents/pi-tools.validate.ts
35564
+ /**
35565
+ * Tool argument validation — validates that exec/bash commands contain
35566
+ * valid shell syntax after model token sanitization.
35567
+ *
35568
+ * Gated on ModelProfile.filters.validateToolArgs — only active for
35569
+ * models known to leak control tokens into structured output (Gemma, Ollama).
35570
+ *
35571
+ * @module
35572
+ */
35573
+ /**
35574
+ * Validate that a command string contains extractable valid shell syntax.
35575
+ * Returns the cleaned command if valid, or an error reason if not.
35576
+ *
35577
+ * Does NOT attempt to parse the full shell grammar — just checks for
35578
+ * obvious corruption (residual control tokens, empty commands).
35579
+ * Preserves heredocs, redirects, pipes, and other legitimate shell syntax.
35580
+ */
35581
+ function validateShellCommand(command) {
35582
+ if (!command || !command.trim()) return {
35583
+ valid: false,
35584
+ cleaned: "",
35585
+ reason: "empty command"
35586
+ };
35587
+ let cleaned = cleanResidualTokenFragments(command);
35588
+ if (/(?:^|[^<])<\|/.test(cleaned) || /\|>/.test(cleaned)) {
35589
+ cleaned = cleaned.replace(/<\|[^|>]*(?:\|>)?/g, "").trim();
35590
+ cleaned = cleanResidualTokenFragments(cleaned);
35591
+ }
35592
+ if (!cleaned || !cleaned.trim()) return {
35593
+ valid: false,
35594
+ cleaned: "",
35595
+ reason: "command is empty after removing control tokens"
35596
+ };
35597
+ if (!/[a-zA-Z0-9_/.~-]/.test(cleaned)) return {
35598
+ valid: false,
35599
+ cleaned,
35600
+ reason: "no recognizable shell command found"
35601
+ };
35602
+ return {
35603
+ valid: true,
35604
+ cleaned: cleaned.trim()
35605
+ };
35606
+ }
35607
+
35608
+ //#endregion
35609
+ //#region src/agents/pi-tools.validate-wrapper.ts
35610
+ const EXEC_TOOL_NAMES = new Set(["exec", "bash"]);
35611
+ /**
35612
+ * Wrap tools with argument validation. Only exec/bash tools are validated.
35613
+ * Other tools pass through unchanged.
35614
+ *
35615
+ * Call this AFTER tools are created and the model profile is resolved.
35616
+ */
35617
+ function wrapToolsWithArgValidation(tools, profile) {
35618
+ if (!profile.filters?.validateToolArgs) return tools;
35619
+ return tools.map((tool) => wrapToolWithArgValidation(tool));
35620
+ }
35621
+ /**
35622
+ * Wrap a single tool with argument validation for exec/bash commands.
35623
+ */
35624
+ function wrapToolWithArgValidation(tool) {
35625
+ const toolName = (tool.name ?? "").toLowerCase().trim();
35626
+ if (!EXEC_TOOL_NAMES.has(toolName)) return tool;
35627
+ const originalExecute = tool.execute;
35628
+ return {
35629
+ ...tool,
35630
+ execute(toolCallId, params, ...rest) {
35631
+ const command = params.command ?? params.cmd;
35632
+ if (typeof command !== "string") return originalExecute.call(tool, toolCallId, params, ...rest);
35633
+ const result = validateShellCommand(command);
35634
+ if (!result.valid) return Promise.resolve({ output: `Error: ${result.reason}. The command appears to contain model control tokens. Please provide a clean shell command without any <|, |>, or XML-like tags. Only valid shell syntax is accepted.` });
35635
+ if (result.cleaned !== command) return originalExecute.call(tool, toolCallId, {
35636
+ ...params,
35637
+ command: result.cleaned
35638
+ }, ...rest);
35639
+ return originalExecute.call(tool, toolCallId, params, ...rest);
35640
+ }
35641
+ };
35642
+ }
35643
+
35539
35644
  //#endregion
35540
35645
  //#region src/agents/plan-mode.ts
35541
35646
  /**
@@ -39488,10 +39593,11 @@ async function runEmbeddedAttempt(params) {
39488
39593
  await resourceLoader.reload();
39489
39594
  }
39490
39595
  const hookRunner = getGlobalHookRunner();
39491
- const { builtInTools, customTools } = splitSdkTools({
39596
+ const { builtInTools: rawBuiltInTools, customTools } = splitSdkTools({
39492
39597
  tools,
39493
39598
  sandboxEnabled: !!sandbox?.enabled
39494
39599
  });
39600
+ const builtInTools = wrapToolsWithArgValidation(rawBuiltInTools, modelProfile);
39495
39601
  let clientToolCallDetected = null;
39496
39602
  const clientToolLoopDetection = resolveToolLoopDetectionConfig({
39497
39603
  cfg: params.config,
@@ -52836,7 +52942,7 @@ function isVoiceChannelType(type) {
52836
52942
  function createDefaultDeps() {
52837
52943
  return {
52838
52944
  sendMessageWhatsApp: async (...args) => {
52839
- const { sendMessageWhatsApp } = await import("./web-Czp0JS6-.js");
52945
+ const { sendMessageWhatsApp } = await import("./web-Bqrgp43v.js");
52840
52946
  return await sendMessageWhatsApp(...args);
52841
52947
  },
52842
52948
  sendMessageTelegram: async (...args) => {
@@ -68150,7 +68256,7 @@ function loadWebLoginQr() {
68150
68256
  return webLoginQrPromise;
68151
68257
  }
68152
68258
  function loadWebChannel() {
68153
- webChannelPromise ??= import("./web-Czp0JS6-.js");
68259
+ webChannelPromise ??= import("./web-Bqrgp43v.js");
68154
68260
  return webChannelPromise;
68155
68261
  }
68156
68262
  function loadWhatsAppActions() {
@@ -9,6 +9,8 @@ const TOOL_CALL_HISTORY_SIZE = 30;
9
9
  const WARNING_THRESHOLD = 10;
10
10
  const CRITICAL_THRESHOLD = 20;
11
11
  const GLOBAL_CIRCUIT_BREAKER_THRESHOLD = 30;
12
+ const CONSECUTIVE_FAILURE_WARNING_THRESHOLD = 3;
13
+ const CONSECUTIVE_FAILURE_CRITICAL_THRESHOLD = 5;
12
14
  const DEFAULT_LOOP_DETECTION_CONFIG = {
13
15
  enabled: false,
14
16
  historySize: TOOL_CALL_HISTORY_SIZE,
@@ -18,8 +20,11 @@ const DEFAULT_LOOP_DETECTION_CONFIG = {
18
20
  detectors: {
19
21
  genericRepeat: true,
20
22
  knownPollNoProgress: true,
21
- pingPong: true
22
- }
23
+ pingPong: true,
24
+ consecutiveFailure: true
25
+ },
26
+ consecutiveFailureWarningThreshold: CONSECUTIVE_FAILURE_WARNING_THRESHOLD,
27
+ consecutiveFailureCriticalThreshold: CONSECUTIVE_FAILURE_CRITICAL_THRESHOLD
23
28
  };
24
29
  function asPositiveInt(value, fallback) {
25
30
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) return fallback;
@@ -37,10 +42,13 @@ function resolveLoopDetectionConfig(config) {
37
42
  warningThreshold,
38
43
  criticalThreshold,
39
44
  globalCircuitBreakerThreshold,
45
+ consecutiveFailureWarningThreshold: asPositiveInt(config?.consecutiveFailureWarningThreshold, DEFAULT_LOOP_DETECTION_CONFIG.consecutiveFailureWarningThreshold),
46
+ consecutiveFailureCriticalThreshold: asPositiveInt(config?.consecutiveFailureCriticalThreshold, DEFAULT_LOOP_DETECTION_CONFIG.consecutiveFailureCriticalThreshold),
40
47
  detectors: {
41
48
  genericRepeat: config?.detectors?.genericRepeat ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.genericRepeat,
42
49
  knownPollNoProgress: config?.detectors?.knownPollNoProgress ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.knownPollNoProgress,
43
- pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong
50
+ pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong,
51
+ consecutiveFailure: (config?.detectors)?.consecutiveFailure ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.consecutiveFailure
44
52
  }
45
53
  };
46
54
  }
@@ -218,6 +226,22 @@ function canonicalPairKey(signatureA, signatureB) {
218
226
  return [signatureA, signatureB].toSorted().join("|");
219
227
  }
220
228
  /**
229
+ * Count consecutive error results for a specific tool, walking backward from
230
+ * the most recent call. A success or a call to a different tool resets the streak.
231
+ * Only counts entries that have been resolved (have a resultHash or isError flag).
232
+ */
233
+ function getConsecutiveFailureStreak(history, toolName) {
234
+ let count = 0;
235
+ for (let i = history.length - 1; i >= 0; i--) {
236
+ const entry = history[i];
237
+ if (entry.toolName !== toolName) break;
238
+ if (entry.resultHash === void 0 && entry.isError === void 0) break;
239
+ if (!entry.isError) break;
240
+ count++;
241
+ }
242
+ return count;
243
+ }
244
+ /**
221
245
  * Detect if an agent is stuck in a repetitive tool call loop.
222
246
  * Checks if the same tool+params combination has been called excessively.
223
247
  */
@@ -230,6 +254,31 @@ function detectToolCallLoop(state, toolName, params, config) {
230
254
  const noProgressStreak = noProgress.count;
231
255
  const knownPollTool = isKnownPollToolCall(toolName, params);
232
256
  const pingPong = getPingPongStreak(history, currentHash);
257
+ if (resolvedConfig.detectors.consecutiveFailure) {
258
+ const failureStreak = getConsecutiveFailureStreak(history, toolName);
259
+ if (failureStreak >= resolvedConfig.consecutiveFailureCriticalThreshold) {
260
+ log.error(`Consecutive failure circuit breaker: ${toolName} failed ${failureStreak} times in a row`);
261
+ return {
262
+ stuck: true,
263
+ level: "critical",
264
+ detector: "consecutive_failure",
265
+ count: failureStreak,
266
+ message: `CRITICAL: ${toolName} has failed ${failureStreak} consecutive times. Stop calling this tool. Explain to the user what you were trying to do and why it failed.`,
267
+ warningKey: `consecutive-failure:${toolName}`
268
+ };
269
+ }
270
+ if (failureStreak >= resolvedConfig.consecutiveFailureWarningThreshold) {
271
+ log.warn(`Consecutive failure warning: ${toolName} failed ${failureStreak} times in a row`);
272
+ return {
273
+ stuck: true,
274
+ level: "warning",
275
+ detector: "consecutive_failure",
276
+ count: failureStreak,
277
+ message: `WARNING: You have failed to call ${toolName} ${failureStreak} times consecutively. Stop retrying and explain to the user what you were trying to do. The tool arguments may contain invalid syntax from model control tokens.`,
278
+ warningKey: `consecutive-failure:${toolName}`
279
+ };
280
+ }
281
+ }
233
282
  if (noProgressStreak >= resolvedConfig.globalCircuitBreakerThreshold) {
234
283
  log.error(`Global circuit breaker triggered: ${toolName} repeated ${noProgressStreak} times with no progress`);
235
284
  return {
@@ -334,6 +383,7 @@ function recordToolCallOutcome(state, params) {
334
383
  if (call.toolName !== params.toolName || call.argsHash !== argsHash) continue;
335
384
  if (call.resultHash !== void 0) continue;
336
385
  call.resultHash = resultHash;
386
+ call.isError = params.error !== void 0;
337
387
  matched = true;
338
388
  break;
339
389
  }
@@ -7,6 +7,8 @@ const TOOL_CALL_HISTORY_SIZE = 30;
7
7
  const WARNING_THRESHOLD = 10;
8
8
  const CRITICAL_THRESHOLD = 20;
9
9
  const GLOBAL_CIRCUIT_BREAKER_THRESHOLD = 30;
10
+ const CONSECUTIVE_FAILURE_WARNING_THRESHOLD = 3;
11
+ const CONSECUTIVE_FAILURE_CRITICAL_THRESHOLD = 5;
10
12
  const DEFAULT_LOOP_DETECTION_CONFIG = {
11
13
  enabled: false,
12
14
  historySize: TOOL_CALL_HISTORY_SIZE,
@@ -16,8 +18,11 @@ const DEFAULT_LOOP_DETECTION_CONFIG = {
16
18
  detectors: {
17
19
  genericRepeat: true,
18
20
  knownPollNoProgress: true,
19
- pingPong: true
20
- }
21
+ pingPong: true,
22
+ consecutiveFailure: true
23
+ },
24
+ consecutiveFailureWarningThreshold: CONSECUTIVE_FAILURE_WARNING_THRESHOLD,
25
+ consecutiveFailureCriticalThreshold: CONSECUTIVE_FAILURE_CRITICAL_THRESHOLD
21
26
  };
22
27
  function asPositiveInt(value, fallback) {
23
28
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) return fallback;
@@ -35,10 +40,13 @@ function resolveLoopDetectionConfig(config) {
35
40
  warningThreshold,
36
41
  criticalThreshold,
37
42
  globalCircuitBreakerThreshold,
43
+ consecutiveFailureWarningThreshold: asPositiveInt(config?.consecutiveFailureWarningThreshold, DEFAULT_LOOP_DETECTION_CONFIG.consecutiveFailureWarningThreshold),
44
+ consecutiveFailureCriticalThreshold: asPositiveInt(config?.consecutiveFailureCriticalThreshold, DEFAULT_LOOP_DETECTION_CONFIG.consecutiveFailureCriticalThreshold),
38
45
  detectors: {
39
46
  genericRepeat: config?.detectors?.genericRepeat ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.genericRepeat,
40
47
  knownPollNoProgress: config?.detectors?.knownPollNoProgress ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.knownPollNoProgress,
41
- pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong
48
+ pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong,
49
+ consecutiveFailure: (config?.detectors)?.consecutiveFailure ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.consecutiveFailure
42
50
  }
43
51
  };
44
52
  }
@@ -216,6 +224,22 @@ function canonicalPairKey(signatureA, signatureB) {
216
224
  return [signatureA, signatureB].toSorted().join("|");
217
225
  }
218
226
  /**
227
+ * Count consecutive error results for a specific tool, walking backward from
228
+ * the most recent call. A success or a call to a different tool resets the streak.
229
+ * Only counts entries that have been resolved (have a resultHash or isError flag).
230
+ */
231
+ function getConsecutiveFailureStreak(history, toolName) {
232
+ let count = 0;
233
+ for (let i = history.length - 1; i >= 0; i--) {
234
+ const entry = history[i];
235
+ if (entry.toolName !== toolName) break;
236
+ if (entry.resultHash === void 0 && entry.isError === void 0) break;
237
+ if (!entry.isError) break;
238
+ count++;
239
+ }
240
+ return count;
241
+ }
242
+ /**
219
243
  * Detect if an agent is stuck in a repetitive tool call loop.
220
244
  * Checks if the same tool+params combination has been called excessively.
221
245
  */
@@ -228,6 +252,31 @@ function detectToolCallLoop(state, toolName, params, config) {
228
252
  const noProgressStreak = noProgress.count;
229
253
  const knownPollTool = isKnownPollToolCall(toolName, params);
230
254
  const pingPong = getPingPongStreak(history, currentHash);
255
+ if (resolvedConfig.detectors.consecutiveFailure) {
256
+ const failureStreak = getConsecutiveFailureStreak(history, toolName);
257
+ if (failureStreak >= resolvedConfig.consecutiveFailureCriticalThreshold) {
258
+ log.error(`Consecutive failure circuit breaker: ${toolName} failed ${failureStreak} times in a row`);
259
+ return {
260
+ stuck: true,
261
+ level: "critical",
262
+ detector: "consecutive_failure",
263
+ count: failureStreak,
264
+ message: `CRITICAL: ${toolName} has failed ${failureStreak} consecutive times. Stop calling this tool. Explain to the user what you were trying to do and why it failed.`,
265
+ warningKey: `consecutive-failure:${toolName}`
266
+ };
267
+ }
268
+ if (failureStreak >= resolvedConfig.consecutiveFailureWarningThreshold) {
269
+ log.warn(`Consecutive failure warning: ${toolName} failed ${failureStreak} times in a row`);
270
+ return {
271
+ stuck: true,
272
+ level: "warning",
273
+ detector: "consecutive_failure",
274
+ count: failureStreak,
275
+ message: `WARNING: You have failed to call ${toolName} ${failureStreak} times consecutively. Stop retrying and explain to the user what you were trying to do. The tool arguments may contain invalid syntax from model control tokens.`,
276
+ warningKey: `consecutive-failure:${toolName}`
277
+ };
278
+ }
279
+ }
231
280
  if (noProgressStreak >= resolvedConfig.globalCircuitBreakerThreshold) {
232
281
  log.error(`Global circuit breaker triggered: ${toolName} repeated ${noProgressStreak} times with no progress`);
233
282
  return {
@@ -332,6 +381,7 @@ function recordToolCallOutcome(state, params) {
332
381
  if (call.toolName !== params.toolName || call.argsHash !== argsHash) continue;
333
382
  if (call.resultHash !== void 0) continue;
334
383
  call.resultHash = resultHash;
384
+ call.isError = params.error !== void 0;
335
385
  matched = true;
336
386
  break;
337
387
  }
@@ -10,6 +10,8 @@ const TOOL_CALL_HISTORY_SIZE = 30;
10
10
  const WARNING_THRESHOLD = 10;
11
11
  const CRITICAL_THRESHOLD = 20;
12
12
  const GLOBAL_CIRCUIT_BREAKER_THRESHOLD = 30;
13
+ const CONSECUTIVE_FAILURE_WARNING_THRESHOLD = 3;
14
+ const CONSECUTIVE_FAILURE_CRITICAL_THRESHOLD = 5;
13
15
  const DEFAULT_LOOP_DETECTION_CONFIG = {
14
16
  enabled: false,
15
17
  historySize: TOOL_CALL_HISTORY_SIZE,
@@ -19,8 +21,11 @@ const DEFAULT_LOOP_DETECTION_CONFIG = {
19
21
  detectors: {
20
22
  genericRepeat: true,
21
23
  knownPollNoProgress: true,
22
- pingPong: true
23
- }
24
+ pingPong: true,
25
+ consecutiveFailure: true
26
+ },
27
+ consecutiveFailureWarningThreshold: CONSECUTIVE_FAILURE_WARNING_THRESHOLD,
28
+ consecutiveFailureCriticalThreshold: CONSECUTIVE_FAILURE_CRITICAL_THRESHOLD
24
29
  };
25
30
  function asPositiveInt(value, fallback) {
26
31
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) return fallback;
@@ -38,10 +43,13 @@ function resolveLoopDetectionConfig(config) {
38
43
  warningThreshold,
39
44
  criticalThreshold,
40
45
  globalCircuitBreakerThreshold,
46
+ consecutiveFailureWarningThreshold: asPositiveInt(config?.consecutiveFailureWarningThreshold, DEFAULT_LOOP_DETECTION_CONFIG.consecutiveFailureWarningThreshold),
47
+ consecutiveFailureCriticalThreshold: asPositiveInt(config?.consecutiveFailureCriticalThreshold, DEFAULT_LOOP_DETECTION_CONFIG.consecutiveFailureCriticalThreshold),
41
48
  detectors: {
42
49
  genericRepeat: config?.detectors?.genericRepeat ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.genericRepeat,
43
50
  knownPollNoProgress: config?.detectors?.knownPollNoProgress ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.knownPollNoProgress,
44
- pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong
51
+ pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong,
52
+ consecutiveFailure: (config?.detectors)?.consecutiveFailure ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.consecutiveFailure
45
53
  }
46
54
  };
47
55
  }
@@ -219,6 +227,22 @@ function canonicalPairKey(signatureA, signatureB) {
219
227
  return [signatureA, signatureB].toSorted().join("|");
220
228
  }
221
229
  /**
230
+ * Count consecutive error results for a specific tool, walking backward from
231
+ * the most recent call. A success or a call to a different tool resets the streak.
232
+ * Only counts entries that have been resolved (have a resultHash or isError flag).
233
+ */
234
+ function getConsecutiveFailureStreak(history, toolName) {
235
+ let count = 0;
236
+ for (let i = history.length - 1; i >= 0; i--) {
237
+ const entry = history[i];
238
+ if (entry.toolName !== toolName) break;
239
+ if (entry.resultHash === void 0 && entry.isError === void 0) break;
240
+ if (!entry.isError) break;
241
+ count++;
242
+ }
243
+ return count;
244
+ }
245
+ /**
222
246
  * Detect if an agent is stuck in a repetitive tool call loop.
223
247
  * Checks if the same tool+params combination has been called excessively.
224
248
  */
@@ -231,6 +255,31 @@ function detectToolCallLoop(state, toolName, params, config) {
231
255
  const noProgressStreak = noProgress.count;
232
256
  const knownPollTool = isKnownPollToolCall(toolName, params);
233
257
  const pingPong = getPingPongStreak(history, currentHash);
258
+ if (resolvedConfig.detectors.consecutiveFailure) {
259
+ const failureStreak = getConsecutiveFailureStreak(history, toolName);
260
+ if (failureStreak >= resolvedConfig.consecutiveFailureCriticalThreshold) {
261
+ log.error(`Consecutive failure circuit breaker: ${toolName} failed ${failureStreak} times in a row`);
262
+ return {
263
+ stuck: true,
264
+ level: "critical",
265
+ detector: "consecutive_failure",
266
+ count: failureStreak,
267
+ message: `CRITICAL: ${toolName} has failed ${failureStreak} consecutive times. Stop calling this tool. Explain to the user what you were trying to do and why it failed.`,
268
+ warningKey: `consecutive-failure:${toolName}`
269
+ };
270
+ }
271
+ if (failureStreak >= resolvedConfig.consecutiveFailureWarningThreshold) {
272
+ log.warn(`Consecutive failure warning: ${toolName} failed ${failureStreak} times in a row`);
273
+ return {
274
+ stuck: true,
275
+ level: "warning",
276
+ detector: "consecutive_failure",
277
+ count: failureStreak,
278
+ message: `WARNING: You have failed to call ${toolName} ${failureStreak} times consecutively. Stop retrying and explain to the user what you were trying to do. The tool arguments may contain invalid syntax from model control tokens.`,
279
+ warningKey: `consecutive-failure:${toolName}`
280
+ };
281
+ }
282
+ }
234
283
  if (noProgressStreak >= resolvedConfig.globalCircuitBreakerThreshold) {
235
284
  log.error(`Global circuit breaker triggered: ${toolName} repeated ${noProgressStreak} times with no progress`);
236
285
  return {
@@ -335,6 +384,7 @@ function recordToolCallOutcome(state, params) {
335
384
  if (call.toolName !== params.toolName || call.argsHash !== argsHash) continue;
336
385
  if (call.resultHash !== void 0) continue;
337
386
  call.resultHash = resultHash;
387
+ call.isError = params.error !== void 0;
338
388
  matched = true;
339
389
  break;
340
390
  }