agim-cli 1.2.144 → 1.2.147

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 (266) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/dist/cli-ui/setup-llm.d.ts.map +1 -1
  3. package/dist/cli-ui/setup-llm.js +3 -1
  4. package/dist/cli-ui/setup-llm.js.map +1 -1
  5. package/dist/core/circuit-breaker.d.ts +28 -0
  6. package/dist/core/circuit-breaker.d.ts.map +1 -1
  7. package/dist/core/circuit-breaker.js +45 -0
  8. package/dist/core/circuit-breaker.js.map +1 -1
  9. package/dist/core/intent.d.ts.map +1 -1
  10. package/dist/core/intent.js +3 -1
  11. package/dist/core/intent.js.map +1 -1
  12. package/dist/core/llm/agent-loop.d.ts +9 -1
  13. package/dist/core/llm/agent-loop.d.ts.map +1 -1
  14. package/dist/core/llm/agent-loop.js +80 -1
  15. package/dist/core/llm/agent-loop.js.map +1 -1
  16. package/dist/core/llm/anthropic-provider.d.ts.map +1 -1
  17. package/dist/core/llm/anthropic-provider.js +18 -4
  18. package/dist/core/llm/anthropic-provider.js.map +1 -1
  19. package/dist/core/llm/hallucination-detector.d.ts +33 -0
  20. package/dist/core/llm/hallucination-detector.d.ts.map +1 -0
  21. package/dist/core/llm/hallucination-detector.js +103 -0
  22. package/dist/core/llm/hallucination-detector.js.map +1 -0
  23. package/dist/core/llm/imhub-dispatcher.d.ts.map +1 -1
  24. package/dist/core/llm/imhub-dispatcher.js +7 -0
  25. package/dist/core/llm/imhub-dispatcher.js.map +1 -1
  26. package/dist/core/llm/provider-base.d.ts +9 -0
  27. package/dist/core/llm/provider-base.d.ts.map +1 -1
  28. package/dist/core/llm/provider-base.js.map +1 -1
  29. package/dist/core/memory-distill.d.ts.map +1 -1
  30. package/dist/core/memory-distill.js +18 -3
  31. package/dist/core/memory-distill.js.map +1 -1
  32. package/dist/core/memory.d.ts +14 -0
  33. package/dist/core/memory.d.ts.map +1 -1
  34. package/dist/core/memory.js +39 -0
  35. package/dist/core/memory.js.map +1 -1
  36. package/dist/core/message-sink.d.ts +6 -0
  37. package/dist/core/message-sink.d.ts.map +1 -1
  38. package/dist/core/message-sink.js +18 -3
  39. package/dist/core/message-sink.js.map +1 -1
  40. package/dist/core/outbox.d.ts +30 -2
  41. package/dist/core/outbox.d.ts.map +1 -1
  42. package/dist/core/outbox.js +102 -10
  43. package/dist/core/outbox.js.map +1 -1
  44. package/dist/core/reminders.d.ts.map +1 -1
  45. package/dist/core/reminders.js +11 -1
  46. package/dist/core/reminders.js.map +1 -1
  47. package/dist/core/router.d.ts.map +1 -1
  48. package/dist/core/router.js +16 -4
  49. package/dist/core/router.js.map +1 -1
  50. package/dist/core/schedule.d.ts +18 -0
  51. package/dist/core/schedule.d.ts.map +1 -1
  52. package/dist/core/schedule.js +80 -17
  53. package/dist/core/schedule.js.map +1 -1
  54. package/dist/core/sensitive-paths.d.ts.map +1 -1
  55. package/dist/core/sensitive-paths.js +53 -9
  56. package/dist/core/sensitive-paths.js.map +1 -1
  57. package/dist/core/types.d.ts +6 -0
  58. package/dist/core/types.d.ts.map +1 -1
  59. package/dist/plugins/agents/native/index.d.ts +47 -8
  60. package/dist/plugins/agents/native/index.d.ts.map +1 -1
  61. package/dist/plugins/agents/native/index.js +253 -102
  62. package/dist/plugins/agents/native/index.js.map +1 -1
  63. package/dist/plugins/agents/native/tool-registry.d.ts +33 -0
  64. package/dist/plugins/agents/native/tool-registry.d.ts.map +1 -0
  65. package/dist/plugins/agents/native/tool-registry.js +82 -0
  66. package/dist/plugins/agents/native/tool-registry.js.map +1 -0
  67. package/dist/plugins/messengers/dingtalk/dingtalk-client.d.ts.map +1 -1
  68. package/dist/plugins/messengers/dingtalk/dingtalk-client.js +11 -11
  69. package/dist/plugins/messengers/dingtalk/dingtalk-client.js.map +1 -1
  70. package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -1
  71. package/dist/plugins/messengers/feishu/feishu-adapter.js +9 -5
  72. package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -1
  73. package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -1
  74. package/dist/plugins/messengers/wechat/ilink-adapter.js +11 -1
  75. package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -1
  76. package/dist/web/public/assets/{a2a-DczMMkbl.js → a2a-Cll3P4QN.js} +2 -2
  77. package/dist/web/public/assets/{a2a-DczMMkbl.js.map → a2a-Cll3P4QN.js.map} +1 -1
  78. package/dist/web/public/assets/{activity-cbLHkzca.js → activity-B7T7YFlD.js} +2 -2
  79. package/dist/web/public/assets/{activity-cbLHkzca.js.map → activity-B7T7YFlD.js.map} +1 -1
  80. package/dist/web/public/assets/{admins-C-YsGMj7.js → admins-CN7P018S.js} +2 -2
  81. package/dist/web/public/assets/{admins-C-YsGMj7.js.map → admins-CN7P018S.js.map} +1 -1
  82. package/dist/web/public/assets/{agents-BWfov_1-.js → agents-Bqgq7GBF.js} +2 -2
  83. package/dist/web/public/assets/{agents-BWfov_1-.js.map → agents-Bqgq7GBF.js.map} +1 -1
  84. package/dist/web/public/assets/{approvals-HSssmXKS.js → approvals-C8IUJQ_A.js} +2 -2
  85. package/dist/web/public/assets/{approvals-HSssmXKS.js.map → approvals-C8IUJQ_A.js.map} +1 -1
  86. package/dist/web/public/assets/{arrow-down-BXvC8Al2.js → arrow-down-SLWKqtDc.js} +2 -2
  87. package/dist/web/public/assets/{arrow-down-BXvC8Al2.js.map → arrow-down-SLWKqtDc.js.map} +1 -1
  88. package/dist/web/public/assets/{arrow-up-63xELY5Q.js → arrow-up-BOADc9ce.js} +2 -2
  89. package/dist/web/public/assets/{arrow-up-63xELY5Q.js.map → arrow-up-BOADc9ce.js.map} +1 -1
  90. package/dist/web/public/assets/{asks-COLEFOvK.js → asks-C-j-DypC.js} +2 -2
  91. package/dist/web/public/assets/{asks-COLEFOvK.js.map → asks-C-j-DypC.js.map} +1 -1
  92. package/dist/web/public/assets/{audit-D4ZEiZub.js → audit-DQb-RuXh.js} +2 -2
  93. package/dist/web/public/assets/{audit-D4ZEiZub.js.map → audit-DQb-RuXh.js.map} +1 -1
  94. package/dist/web/public/assets/{bell-Cg2Bvv06.js → bell-CV88-ul6.js} +2 -2
  95. package/dist/web/public/assets/{bell-Cg2Bvv06.js.map → bell-CV88-ul6.js.map} +1 -1
  96. package/dist/web/public/assets/{bgjobs-CEjCzwtd.js → bgjobs-CDrK0d-W.js} +2 -2
  97. package/dist/web/public/assets/{bgjobs-CEjCzwtd.js.map → bgjobs-CDrK0d-W.js.map} +1 -1
  98. package/dist/web/public/assets/{brain-euvl6F6C.js → brain-B7HtSOQU.js} +2 -2
  99. package/dist/web/public/assets/{brain-euvl6F6C.js.map → brain-B7HtSOQU.js.map} +1 -1
  100. package/dist/web/public/assets/{briefcase-DPWLbCnA.js → briefcase-mdzuIa__.js} +2 -2
  101. package/dist/web/public/assets/{briefcase-DPWLbCnA.js.map → briefcase-mdzuIa__.js.map} +1 -1
  102. package/dist/web/public/assets/{browser-ponyfill-BUutOaRz.js → browser-ponyfill-DBWdeCTC.js} +2 -2
  103. package/dist/web/public/assets/{browser-ponyfill-BUutOaRz.js.map → browser-ponyfill-DBWdeCTC.js.map} +1 -1
  104. package/dist/web/public/assets/{chat-Dz9kfaxH.js → chat-CSjtY2rN.js} +3 -3
  105. package/dist/web/public/assets/{chat-Dz9kfaxH.js.map → chat-CSjtY2rN.js.map} +1 -1
  106. package/dist/web/public/assets/{chevron-left-BeIh5thq.js → chevron-left-uSfPn636.js} +2 -2
  107. package/dist/web/public/assets/{chevron-left-BeIh5thq.js.map → chevron-left-uSfPn636.js.map} +1 -1
  108. package/dist/web/public/assets/{chevron-right-uP_l9MMb.js → chevron-right-CtelqacW.js} +2 -2
  109. package/dist/web/public/assets/{chevron-right-uP_l9MMb.js.map → chevron-right-CtelqacW.js.map} +1 -1
  110. package/dist/web/public/assets/{circle-check-CewnjFgv.js → circle-check-8dbL-u7O.js} +2 -2
  111. package/dist/web/public/assets/{circle-check-CewnjFgv.js.map → circle-check-8dbL-u7O.js.map} +1 -1
  112. package/dist/web/public/assets/{circle-check-big-C2RTc48c.js → circle-check-big-D8-svk9a.js} +2 -2
  113. package/dist/web/public/assets/{circle-check-big-C2RTc48c.js.map → circle-check-big-D8-svk9a.js.map} +1 -1
  114. package/dist/web/public/assets/{circle-x-Ccg1HyV-.js → circle-x-rUxzIz5P.js} +2 -2
  115. package/dist/web/public/assets/{circle-x-Ccg1HyV-.js.map → circle-x-rUxzIz5P.js.map} +1 -1
  116. package/dist/web/public/assets/{clock-qxbYSynv.js → clock-CG5dlBGB.js} +2 -2
  117. package/dist/web/public/assets/{clock-qxbYSynv.js.map → clock-CG5dlBGB.js.map} +1 -1
  118. package/dist/web/public/assets/{confirm-dialog-DmJq4Td9.js → confirm-dialog-DlUsSur3.js} +2 -2
  119. package/dist/web/public/assets/{confirm-dialog-DmJq4Td9.js.map → confirm-dialog-DlUsSur3.js.map} +1 -1
  120. package/dist/web/public/assets/{copy-DxSHRdbc.js → copy-DnC76wFT.js} +2 -2
  121. package/dist/web/public/assets/{copy-DxSHRdbc.js.map → copy-DnC76wFT.js.map} +1 -1
  122. package/dist/web/public/assets/{data-table-S7rIjwdO.js → data-table-DswkWUfG.js} +2 -2
  123. package/dist/web/public/assets/{data-table-S7rIjwdO.js.map → data-table-DswkWUfG.js.map} +1 -1
  124. package/dist/web/public/assets/dialog-Ceo4YuXy.js +6 -0
  125. package/dist/web/public/assets/dialog-Ceo4YuXy.js.map +1 -0
  126. package/dist/web/public/assets/{download-OhsGtnO-.js → download-DF-46tS4.js} +2 -2
  127. package/dist/web/public/assets/{download-OhsGtnO-.js.map → download-DF-46tS4.js.map} +1 -1
  128. package/dist/web/public/assets/{email-C1-HxWLF.js → email-CZee26-_.js} +3 -3
  129. package/dist/web/public/assets/{email-C1-HxWLF.js.map → email-CZee26-_.js.map} +1 -1
  130. package/dist/web/public/assets/{empty-state-C-qjOHyu.js → empty-state-D9Hi0Atm.js} +2 -2
  131. package/dist/web/public/assets/{empty-state-C-qjOHyu.js.map → empty-state-D9Hi0Atm.js.map} +1 -1
  132. package/dist/web/public/assets/{external-link-DRVp9-lb.js → external-link-D64iZa9P.js} +2 -2
  133. package/dist/web/public/assets/{external-link-DRVp9-lb.js.map → external-link-D64iZa9P.js.map} +1 -1
  134. package/dist/web/public/assets/{eye-CFhg5BTa.js → eye-sY6WZb7D.js} +2 -2
  135. package/dist/web/public/assets/{eye-CFhg5BTa.js.map → eye-sY6WZb7D.js.map} +1 -1
  136. package/dist/web/public/assets/{facts-CGaLWhzi.js → facts-B7bGGwvi.js} +2 -2
  137. package/dist/web/public/assets/{facts-CGaLWhzi.js.map → facts-B7bGGwvi.js.map} +1 -1
  138. package/dist/web/public/assets/{goals-C-dJANmn.js → goals-BfQbsvZv.js} +2 -2
  139. package/dist/web/public/assets/{goals-C-dJANmn.js.map → goals-BfQbsvZv.js.map} +1 -1
  140. package/dist/web/public/assets/{health-CWcti5h3.js → health-Ba_mY0Ts.js} +2 -2
  141. package/dist/web/public/assets/{health-CWcti5h3.js.map → health-Ba_mY0Ts.js.map} +1 -1
  142. package/dist/web/public/assets/{heart-pulse-DmGhKR2W.js → heart-pulse-BjikOVwU.js} +2 -2
  143. package/dist/web/public/assets/{heart-pulse-DmGhKR2W.js.map → heart-pulse-BjikOVwU.js.map} +1 -1
  144. package/dist/web/public/assets/{heartbeat-kLoGBNCo.js → heartbeat-BM8LlPes.js} +2 -2
  145. package/dist/web/public/assets/{heartbeat-kLoGBNCo.js.map → heartbeat-BM8LlPes.js.map} +1 -1
  146. package/dist/web/public/assets/{hot-BITDoax1.js → hot-BtuLL6n8.js} +2 -2
  147. package/dist/web/public/assets/{hot-BITDoax1.js.map → hot-BtuLL6n8.js.map} +1 -1
  148. package/dist/web/public/assets/index-DEWFfW_Z.js +199 -0
  149. package/dist/web/public/assets/index-DEWFfW_Z.js.map +1 -0
  150. package/dist/web/public/assets/{installed-Co9WrtQ7.js → installed-Xr8p31ij.js} +2 -2
  151. package/dist/web/public/assets/{installed-Co9WrtQ7.js.map → installed-Xr8p31ij.js.map} +1 -1
  152. package/dist/web/public/assets/{jobs-hdHhBEvi.js → jobs-Ddy81Udm.js} +2 -2
  153. package/dist/web/public/assets/{jobs-hdHhBEvi.js.map → jobs-Ddy81Udm.js.map} +1 -1
  154. package/dist/web/public/assets/{layout-CQtbOBag.js → layout-BL74fT-L.js} +2 -2
  155. package/dist/web/public/assets/{layout-CQtbOBag.js.map → layout-BL74fT-L.js.map} +1 -1
  156. package/dist/web/public/assets/{layout-bDMXIKIR.js → layout-Bn2qUxcK.js} +2 -2
  157. package/dist/web/public/assets/{layout-bDMXIKIR.js.map → layout-Bn2qUxcK.js.map} +1 -1
  158. package/dist/web/public/assets/{layout-BMXC1Uh1.js → layout-Bp4SAA8_.js} +2 -2
  159. package/dist/web/public/assets/{layout-BMXC1Uh1.js.map → layout-Bp4SAA8_.js.map} +1 -1
  160. package/dist/web/public/assets/{layout-CysVsySh.js → layout-CZ9pGnW8.js} +2 -2
  161. package/dist/web/public/assets/{layout-CysVsySh.js.map → layout-CZ9pGnW8.js.map} +1 -1
  162. package/dist/web/public/assets/{layout-CyBGneZ9.js → layout-pasFRkKV.js} +2 -2
  163. package/dist/web/public/assets/{layout-CyBGneZ9.js.map → layout-pasFRkKV.js.map} +1 -1
  164. package/dist/web/public/assets/llm-yp7b5xxL.js +7 -0
  165. package/dist/web/public/assets/llm-yp7b5xxL.js.map +1 -0
  166. package/dist/web/public/assets/{loader-circle-9VUMGitw.js → loader-circle-Bbw4pEyE.js} +2 -2
  167. package/dist/web/public/assets/{loader-circle-9VUMGitw.js.map → loader-circle-Bbw4pEyE.js.map} +1 -1
  168. package/dist/web/public/assets/{map-pin-BXYvvHry.js → map-pin-DIXHUQgM.js} +2 -2
  169. package/dist/web/public/assets/{map-pin-BXYvvHry.js.map → map-pin-DIXHUQgM.js.map} +1 -1
  170. package/dist/web/public/assets/{mcp-BgLdlwSn.js → mcp-DyaljIM_.js} +2 -2
  171. package/dist/web/public/assets/{mcp-BgLdlwSn.js.map → mcp-DyaljIM_.js.map} +1 -1
  172. package/dist/web/public/assets/memos-Dkoc157i.js +12 -0
  173. package/dist/web/public/assets/memos-Dkoc157i.js.map +1 -0
  174. package/dist/web/public/assets/{messengers-7Phqea62.js → messengers-CcyGDeUI.js} +2 -2
  175. package/dist/web/public/assets/{messengers-7Phqea62.js.map → messengers-CcyGDeUI.js.map} +1 -1
  176. package/dist/web/public/assets/{mobile-CV5b6D2W.js → mobile-DqzIv4Xb.js} +2 -2
  177. package/dist/web/public/assets/{mobile-CV5b6D2W.js.map → mobile-DqzIv4Xb.js.map} +1 -1
  178. package/dist/web/public/assets/{native-agent-QvIa6LjE.js → native-agent-BQ7WaRGK.js} +2 -2
  179. package/dist/web/public/assets/{native-agent-QvIa6LjE.js.map → native-agent-BQ7WaRGK.js.map} +1 -1
  180. package/dist/web/public/assets/{network-BXhEjGhE.js → network-B_yUFAqC.js} +2 -2
  181. package/dist/web/public/assets/{network-BXhEjGhE.js.map → network-B_yUFAqC.js.map} +1 -1
  182. package/dist/web/public/assets/{outbox-DHQL7TQb.js → outbox-l8aVOZqO.js} +2 -2
  183. package/dist/web/public/assets/{outbox-DHQL7TQb.js.map → outbox-l8aVOZqO.js.map} +1 -1
  184. package/dist/web/public/assets/{pagination-VKuPb1Ot.js → pagination-BAKRGKa9.js} +2 -2
  185. package/dist/web/public/assets/{pagination-VKuPb1Ot.js.map → pagination-BAKRGKa9.js.map} +1 -1
  186. package/dist/web/public/assets/{persona-CWug2GLR.js → persona-D3VL9Rg1.js} +2 -2
  187. package/dist/web/public/assets/{persona-CWug2GLR.js.map → persona-D3VL9Rg1.js.map} +1 -1
  188. package/dist/web/public/assets/{plans-CZoEs5SY.js → plans-BBB5e9my.js} +2 -2
  189. package/dist/web/public/assets/{plans-CZoEs5SY.js.map → plans-BBB5e9my.js.map} +1 -1
  190. package/dist/web/public/assets/{play-CfSn5Vdl.js → play-7-Wd369f.js} +2 -2
  191. package/dist/web/public/assets/{play-CfSn5Vdl.js.map → play-7-Wd369f.js.map} +1 -1
  192. package/dist/web/public/assets/{plus-Z8l4CiqJ.js → plus-B0sfZy-j.js} +2 -2
  193. package/dist/web/public/assets/{plus-Z8l4CiqJ.js.map → plus-B0sfZy-j.js.map} +1 -1
  194. package/dist/web/public/assets/{policy-CutDSEPW.js → policy-BM1WRXH0.js} +2 -2
  195. package/dist/web/public/assets/{policy-CutDSEPW.js.map → policy-BM1WRXH0.js.map} +1 -1
  196. package/dist/web/public/assets/{qr-code-DgU5aiM6.js → qr-code-DcKs5fi3.js} +2 -2
  197. package/dist/web/public/assets/{qr-code-DgU5aiM6.js.map → qr-code-DcKs5fi3.js.map} +1 -1
  198. package/dist/web/public/assets/{react-Cb2sDjhD.js → react-DlP5eolq.js} +2 -2
  199. package/dist/web/public/assets/{react-Cb2sDjhD.js.map → react-DlP5eolq.js.map} +1 -1
  200. package/dist/web/public/assets/{refresh-ccw-D2CWiyU_.js → refresh-ccw-uNKeBeRl.js} +2 -2
  201. package/dist/web/public/assets/{refresh-ccw-D2CWiyU_.js.map → refresh-ccw-uNKeBeRl.js.map} +1 -1
  202. package/dist/web/public/assets/{reminders-Cb6Izedg.js → reminders-DHM8K0_O.js} +2 -2
  203. package/dist/web/public/assets/{reminders-Cb6Izedg.js.map → reminders-DHM8K0_O.js.map} +1 -1
  204. package/dist/web/public/assets/{save-DB0BDYTs.js → save-qwJa5_SA.js} +2 -2
  205. package/dist/web/public/assets/{save-DB0BDYTs.js.map → save-qwJa5_SA.js.map} +1 -1
  206. package/dist/web/public/assets/{schedules-8mSjE14D.js → schedules-Bcd0wbT4.js} +2 -2
  207. package/dist/web/public/assets/{schedules-8mSjE14D.js.map → schedules-Bcd0wbT4.js.map} +1 -1
  208. package/dist/web/public/assets/{search-Con69NhG.js → search-BUlzNWrj.js} +2 -2
  209. package/dist/web/public/assets/{search-Con69NhG.js.map → search-BUlzNWrj.js.map} +1 -1
  210. package/dist/web/public/assets/{search-B4fHilZ0.js → search-i1tP2maJ.js} +2 -2
  211. package/dist/web/public/assets/{search-B4fHilZ0.js.map → search-i1tP2maJ.js.map} +1 -1
  212. package/dist/web/public/assets/{security-BTe3zUg8.js → security-DgJyTT4g.js} +2 -2
  213. package/dist/web/public/assets/{security-BTe3zUg8.js.map → security-DgJyTT4g.js.map} +1 -1
  214. package/dist/web/public/assets/{service-C7SqcwfL.js → service-A0Hzear0.js} +2 -2
  215. package/dist/web/public/assets/{service-C7SqcwfL.js.map → service-A0Hzear0.js.map} +1 -1
  216. package/dist/web/public/assets/{shield-alert-CKFVsGgI.js → shield-alert-DrnN6fz_.js} +2 -2
  217. package/dist/web/public/assets/{shield-alert-CKFVsGgI.js.map → shield-alert-DrnN6fz_.js.map} +1 -1
  218. package/dist/web/public/assets/{status-badge-BSkpyN4D.js → status-badge-Ryzf96Pl.js} +2 -2
  219. package/dist/web/public/assets/{status-badge-BSkpyN4D.js.map → status-badge-Ryzf96Pl.js.map} +1 -1
  220. package/dist/web/public/assets/{subtasks-Bel-I1Sk.js → subtasks-Bzh3o3EF.js} +2 -2
  221. package/dist/web/public/assets/{subtasks-Bel-I1Sk.js.map → subtasks-Bzh3o3EF.js.map} +1 -1
  222. package/dist/web/public/assets/{table-CPn1MRcy.js → table-BbAOSyc8.js} +2 -2
  223. package/dist/web/public/assets/{table-CPn1MRcy.js.map → table-BbAOSyc8.js.map} +1 -1
  224. package/dist/web/public/assets/{topn-Ba3RjcK1.js → topn-DkhYw-Gp.js} +2 -2
  225. package/dist/web/public/assets/{topn-Ba3RjcK1.js.map → topn-DkhYw-Gp.js.map} +1 -1
  226. package/dist/web/public/assets/{trash-2-Dfov8aHD.js → trash-2-CA0cLpnU.js} +2 -2
  227. package/dist/web/public/assets/{trash-2-Dfov8aHD.js.map → trash-2-CA0cLpnU.js.map} +1 -1
  228. package/dist/web/public/assets/{use-background-tasks-BQrEeUwY.js → use-background-tasks-B64YjlA8.js} +2 -2
  229. package/dist/web/public/assets/{use-background-tasks-BQrEeUwY.js.map → use-background-tasks-B64YjlA8.js.map} +1 -1
  230. package/dist/web/public/assets/{use-event-stream-DgGpGKop.js → use-event-stream-I1lMFEfh.js} +2 -2
  231. package/dist/web/public/assets/{use-event-stream-DgGpGKop.js.map → use-event-stream-I1lMFEfh.js.map} +1 -1
  232. package/dist/web/public/assets/{use-llm-admin-DYekqogG.js → use-llm-admin-DY2axI4D.js} +2 -2
  233. package/dist/web/public/assets/{use-llm-admin-DYekqogG.js.map → use-llm-admin-DY2axI4D.js.map} +1 -1
  234. package/dist/web/public/assets/{use-memory-DbJ4pP2Z.js → use-memory-BYEjVWbU.js} +2 -2
  235. package/dist/web/public/assets/{use-memory-DbJ4pP2Z.js.map → use-memory-BYEjVWbU.js.map} +1 -1
  236. package/dist/web/public/assets/{use-observability-C2M6WZ9W.js → use-observability-Coj02yDo.js} +2 -2
  237. package/dist/web/public/assets/{use-observability-C2M6WZ9W.js.map → use-observability-Coj02yDo.js.map} +1 -1
  238. package/dist/web/public/assets/{use-settings-DMdaoWsB.js → use-settings-i1MhlkyC.js} +2 -2
  239. package/dist/web/public/assets/{use-settings-DMdaoWsB.js.map → use-settings-i1MhlkyC.js.map} +1 -1
  240. package/dist/web/public/assets/{use-workspace-BHG7h3jQ.js → use-workspace-DgEM35PY.js} +2 -2
  241. package/dist/web/public/assets/{use-workspace-BHG7h3jQ.js.map → use-workspace-DgEM35PY.js.map} +1 -1
  242. package/dist/web/public/assets/{useQuery-PdiC7-sY.js → useQuery-CY2iazjN.js} +2 -2
  243. package/dist/web/public/assets/{useQuery-PdiC7-sY.js.map → useQuery-CY2iazjN.js.map} +1 -1
  244. package/dist/web/public/assets/{vector-DnZM3OXU.js → vector-Ic76u2hY.js} +2 -2
  245. package/dist/web/public/assets/{vector-DnZM3OXU.js.map → vector-Ic76u2hY.js.map} +1 -1
  246. package/dist/web/public/assets/{viewer-Dz6k0YKp.js → viewer-BXbUN1Rl.js} +2 -2
  247. package/dist/web/public/assets/{viewer-Dz6k0YKp.js.map → viewer-BXbUN1Rl.js.map} +1 -1
  248. package/dist/web/public/assets/{workspace-BnXrWS3j.js → workspace-CUg0JPn6.js} +3 -3
  249. package/dist/web/public/assets/{workspace-BnXrWS3j.js.map → workspace-CUg0JPn6.js.map} +1 -1
  250. package/dist/web/public/assets/{workspaces-CSS_UBEi.js → workspaces-C-wb5FQj.js} +2 -2
  251. package/dist/web/public/assets/{workspaces-CSS_UBEi.js.map → workspaces-C-wb5FQj.js.map} +1 -1
  252. package/dist/web/public/assets/{x-DG-JKVw_.js → x-D1iSuoqg.js} +2 -2
  253. package/dist/web/public/assets/{x-DG-JKVw_.js.map → x-D1iSuoqg.js.map} +1 -1
  254. package/dist/web/public/index.html +2 -2
  255. package/dist/web/server.d.ts.map +1 -1
  256. package/dist/web/server.js +6 -0
  257. package/dist/web/server.js.map +1 -1
  258. package/package.json +1 -1
  259. package/dist/web/public/assets/dialog-bAIDaO-6.js +0 -6
  260. package/dist/web/public/assets/dialog-bAIDaO-6.js.map +0 -1
  261. package/dist/web/public/assets/index-O0BQoyzo.js +0 -199
  262. package/dist/web/public/assets/index-O0BQoyzo.js.map +0 -1
  263. package/dist/web/public/assets/llm-CPIRNQU2.js +0 -7
  264. package/dist/web/public/assets/llm-CPIRNQU2.js.map +0 -1
  265. package/dist/web/public/assets/memos-CfneX9DH.js +0 -12
  266. package/dist/web/public/assets/memos-CfneX9DH.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugins/agents/native/index.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EAOL,KAAK,WAAW,EAEjB,MAAM,4BAA4B,CAAA;AAKnC,OAAO,EAIL,KAAK,wBAAwB,EAC9B,MAAM,2CAA2C,CAAA;AAyClD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAoLR;AAED;;;;;;;+DAO+D;AAC/D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmBpD;AA+LD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE;IACJ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE;QACN,SAAS,EAAE,aAAa,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC9E,UAAU,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,QAAQ,EAAE,WAAW,CAAA;IACrB,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,CAAA;IACvD,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB,GACA,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC,CAwG1D;AAwED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,wBAAwB,CAgE1E;AAWD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;AA8ED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCvF;AAmlBD,eAAO,MAAM,kBAAkB,EAAE,YAAuC,CAAA;AAExE;;cAEc;AACd,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugins/agents/native/index.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAIL,KAAK,wBAAwB,EAC9B,MAAM,2CAA2C,CAAA;AAoDlD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAM5C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA+NR;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiBpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AA4OD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE;IACJ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE;QACN,SAAS,EAAE,aAAa,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC9E,UAAU,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,QAAQ,EAAE,WAAW,CAAA;IACrB,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,CAAA;IACvD,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB,GACA,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC,CAwG1D;AAwED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,wBAAwB,CAgE1E;AAWD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;AAoDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCvF;AA2kBD,eAAO,MAAM,kBAAkB,EAAE,YAAuC,CAAA;AAExE;;cAEc;AACd,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C"}
@@ -40,16 +40,14 @@
40
40
  // persistence across turns)
41
41
  import { logger as rootLogger } from '../../../core/logger.js';
42
42
  import { logInvocation } from '../../../core/audit-log.js';
43
- import { runAgentLoop, buildMcpDispatcher, combineDispatchers, getProvider, getAllMcpTools, } from '../../../core/llm/index.js';
44
- import { buildBuiltinDispatcher, getBuiltinTools, } from '../../../core/llm/builtin-dispatcher.js';
43
+ import { runAgentLoop, getProvider, } from '../../../core/llm/index.js';
45
44
  import { buildPolicyApprovalGate, describePolicy, } from '../../../core/llm/policy-approval-gate.js';
46
- import { buildImhubDispatcher, getImhubTools, } from '../../../core/llm/imhub-dispatcher.js';
47
- import { buildFsDispatcher, getFsTools, } from '../../../core/llm/fs-dispatcher.js';
48
- import { buildWebDispatcher, getWebTools, } from '../../../core/llm/web-dispatcher.js';
49
- import { buildExecDispatcher, getExecTools, } from '../../../core/llm/exec-dispatcher.js';
50
- import { buildTodoDispatcher, getTodoTools, } from '../../../core/llm/todo-dispatcher.js';
51
- import { buildPlanExitDispatcher, getPlanExitTools, } from '../../../core/llm/plan-exit-dispatcher.js';
52
- import { listSkills } from '../../../core/skills/loader.js';
45
+ // Tool assembly (defs + dispatch + per-call concurrency classifier) moved
46
+ // into tool-registry.ts (#86); the per-dispatcher builder imports live there
47
+ // now. MAX_INJECTED_SKILLS is still needed here for the system-prompt skills
48
+ // cap (#85/T3).
49
+ import { assembleNativeTools } from './tool-registry.js';
50
+ import { listSkills, MAX_INJECTED_SKILLS } from '../../../core/skills/loader.js';
53
51
  import { describeRegistry as describeMcpRegistry } from '../../../core/llm/mcp-registry.js';
54
52
  import { resolveAgentCwd, defaultAgentCwd } from '../../../core/agent-cwd.js';
55
53
  import { handlePushOp } from '../../../core/push-rpc.js';
@@ -61,6 +59,50 @@ import { existsSync as fsExistsSync, statSync as fsStatSync, readFileSync as fsR
61
59
  import { resolve as pathResolve, sep as pathSep, join as pathJoin } from 'node:path';
62
60
  import { sanitizeForInjection, scanForInjectionAttempts } from '../../../core/prompt-injection-guard.js';
63
61
  const log = rootLogger.child({ component: 'native-agent' });
62
+ /**
63
+ * v1.2.147 — framework-level tool-call discipline injected into EVERY
64
+ * native turn's system prompt. Sits at the top, before the operator
65
+ * role definition, so it dominates any persona-level rules the user
66
+ * authors. Pure prompt — paired with the runtime hallucination
67
+ * detector (`detectHallucinatedToolCall`) that catches the failure
68
+ * mode if the model ignores the rule.
69
+ *
70
+ * Why this is hard-coded, not optional:
71
+ * - The failure (model narrates a tool call without emitting it) is
72
+ * LLM-side and silent; we cannot fully prevent it. But almost every
73
+ * instance we've seen would have been deterred by an explicit
74
+ * "do not narrate, just emit" rule.
75
+ * - Leaving this to operator AGENTS.md means every fresh install
76
+ * reproduces the same harm before the operator notices.
77
+ * - Escape hatch: IMHUB_NATIVE_TOOL_DISCIPLINE=off (or 0/false/no/
78
+ * disable) lets advanced operators drop the block, e.g. when
79
+ * measuring whether the prompt itself is hurting tool-call recall.
80
+ */
81
+ const TOOL_CALL_DISCIPLINE_PROMPT = [
82
+ '## 工具调用纪律(agim 框架级硬约束)',
83
+ '',
84
+ '- 禁止用文本"描述/演练"工具调用。例如不可输出 `我现在调用 native_write_file: \\`\\`\\`python ...\\`\\`\\`` —— 想用工具就直接发 `toolCalls`,不要先用纯文本预告。',
85
+ '- 工具调用必须真的发生:如果当轮没真正发出 `toolCalls`,就不要输出"已经写好 / 已经存好 / 已经调了 X"这类口径,更不要承诺"下一步直接写"。',
86
+ '- 不确定是否该调用,用 `native_todo_write` 把意图留为 todo 让用户审,不要假装调用了又没调。',
87
+ '- 用户追问"做了吗 / 写好了吗 / 又没消息了",先回顾本轮 `toolCalls` 历史;没有就直接坦白"没调成",禁止编造"这次直接写""不废话了"等空承诺——空承诺没有任何代价但会摧毁信任。',
88
+ '- write / edit / exec 类副作用工具一次性写完整调用,不要分两步"先承诺再调用"。',
89
+ '',
90
+ '(运行时会有 hallucination 检测器在末轮抓"narrate without emit",触发会用复盘卡替换原回复。)',
91
+ ].join('\n');
92
+ /**
93
+ * Read the IMHUB_NATIVE_TOOL_DISCIPLINE kill-switch. Default ON.
94
+ * Recognized OFF values: 'off' / '0' / 'false' / 'no' / 'disable'.
95
+ * Pairs with isHallucinationDetectorOn (hallucination-detector.ts): the
96
+ * detector still fires when discipline is off, by design — discipline
97
+ * removed is a prompting test, not a license to ship lies.
98
+ */
99
+ export function isToolDisciplineOn() {
100
+ const raw = (process.env.IMHUB_NATIVE_TOOL_DISCIPLINE ?? '').toLowerCase().trim();
101
+ if (raw === 'off' || raw === '0' || raw === 'false' || raw === 'no' || raw === 'disable') {
102
+ return false;
103
+ }
104
+ return true;
105
+ }
64
106
  /**
65
107
  * v1.2.47 — system prompt is rebuilt per IM turn so the model sees:
66
108
  * - which LLM backend + role it's actually running on
@@ -76,9 +118,26 @@ const log = rootLogger.child({ component: 'native-agent' });
76
118
  * field surfaced here is operator-configured and non-sensitive.
77
119
  */
78
120
  export function buildSystemPrompt(provider, role, cwd, threadKey) {
79
- const skills = listSkills();
80
- const skillsBlock = skills.length
81
- ? skills.map((s) => ` - ${s.name}: ${s.description}`).join('\n')
121
+ // T3 (context-as-budget): bound the tier-1 skills listing. listSkills()
122
+ // already caps each description at MAX_DESCRIPTION_CHARS; here we also cap
123
+ // the COUNT to MAX_INJECTED_SKILLS (the same ceiling buildSkillsSummary
124
+ // uses) so a large catalog can't blow the per-turn system-prompt budget.
125
+ // Sorted for stable ordering; overflow collapses into a "+N more" hint
126
+ // that points the model at the on-demand read_skill / /skill list path.
127
+ // (Previously this re-rolled an UNCAPPED `name: desc` line per skill.)
128
+ const allSkills = listSkills().slice().sort((a, b) => a.name.localeCompare(b.name));
129
+ const visibleSkills = allSkills.slice(0, MAX_INJECTED_SKILLS);
130
+ const skillsBlock = visibleSkills.length
131
+ ? visibleSkills
132
+ .map((s) => {
133
+ const desc = (s.description || '').trim() || '(no description; read body via mcp__imhub__read_skill)';
134
+ const mark = s.available ? '' : ` (unavailable: ${s.unavailableReason ?? 'requires not met'})`;
135
+ return ` - ${s.name}: ${desc}${mark}`;
136
+ })
137
+ .join('\n')
138
+ + (allSkills.length > visibleSkills.length
139
+ ? `\n … and ${allSkills.length - visibleSkills.length} more (use /skill list; read any with mcp__imhub__read_skill)`
140
+ : '')
82
141
  : ' (no skill cards loaded; see docs/skills.md to add one)';
83
142
  const mcpReg = describeMcpRegistry();
84
143
  const externalMcp = mcpReg.servers.length
@@ -87,6 +146,16 @@ export function buildSystemPrompt(provider, role, cwd, threadKey) {
87
146
  .join('\n')
88
147
  : ' (none configured)';
89
148
  const lines = [];
149
+ // v1.2.147 — framework-level tool-call discipline. Prepended BEFORE
150
+ // the operator role so it dominates persona-level rules. Hard-coded
151
+ // (not derived from a file) so every fresh agim install gets it, no
152
+ // per-user setup. Pairs with the runtime hallucination detector.
153
+ if (isToolDisciplineOn()) {
154
+ lines.push('[agim framework rule — tool-call discipline]');
155
+ lines.push(TOOL_CALL_DISCIPLINE_PROMPT);
156
+ lines.push('[/agim framework rule]');
157
+ lines.push('');
158
+ }
90
159
  // Operator-supplied role definition. Reads <cwd>/AGENTS.md (seeded by
91
160
  // bootstrapAgentWorkspaces) and prepends it as a role block. Lets
92
161
  // operators customise the agent's identity, tone, and house rules
@@ -108,32 +177,37 @@ export function buildSystemPrompt(provider, role, cwd, threadKey) {
108
177
  if (isPlanModeOn(threadKey)) {
109
178
  lines.push(`⚠ Plan mode is ACTIVE`, ` - You MUST produce a read-only plan; native_write_file and native_exec are HARD-BLOCKED.`, ` - Use read tools freely: native_read_file / native_list_dir / native_glob / native_grep / native_web_fetch / native_web_search.`, ` - Exit handshake (v1.2.131): when the plan is ready, call`, ` native_exit_plan_mode({ plan: '<markdown of the steps>' })`, ` The user will see an Approve/Reject card. On approve you regain full write access and proceed immediately. On reject you stay in Plan Mode with the user's feedback in the tool result — revise and call again.`, ` - DO NOT just describe the plan in prose and stop — the user expects the exit handshake. Skip it only if the user explicitly asks for "no exit" / "just brainstorm".`, ``);
110
179
  }
111
- lines.push(`Tools available beyond the four native built-ins (echo / now / sleep / random_uuid):`, ` - agim built-in MCP tools (mcp__imhub__*): read_skill, list_skills, save_memo, search_memos, update_memo, delete_memo, push_message, ask_user, call_agent, long_task, complete_goal`, ` - native filesystem tools: native_read_file, native_write_file, native_list_dir, native_glob, native_grep — constrained to your workspace cwd unless IMHUB_NATIVE_FS_RESTRICT=0`, ` - native web tools: native_web_fetch (r.jina.ai reader by default), native_web_search (duckduckgo → metaso fallback). Private IPs blocked.`, ` - native_exec(command, timeout_ms?, cwd?): run shell commands. Always approval-gated; bwrap sandbox when IMHUB_EXEC_SANDBOX=bwrap.`, ` - External MCP servers configured by the operator:`, externalMcp, ``, `Available skill cards (call mcp__imhub__read_skill('<name>') for the full body):`, skillsBlock, ``, `Guidance:`, ` - Be terse; avoid filler. Prefer tool use over guessing.`, ` - When uncertain, call mcp__imhub__ask_user(question, choices[]) instead of free-form back-and-forth.`, ` - When the user references something they told the bot before, search memos via mcp__imhub__search_memos.`, ``, `Tool selection priority (HARD RULE — v1.2.59):`, ` - For "read this file / list this dir / search this content / fetch this URL", you MUST FIRST try`, ` your own native tools: native_read_file, native_list_dir, native_glob, native_grep,`, ` native_web_fetch (if available). Do NOT delegate these to call_agent.`, ` - call_agent is reserved for tasks that genuinely need a CLI agent's specialised capabilities`, ` (writing/editing source code in a real repo → claude-code; long-running plans → codex; etc).`, ` - You have a per-turn call_agent cap (default 2). Burning it on file reads will leave you`, ` unable to delegate later when you actually need to.`, ``, `When to USE call_agent (v1.2.139 positive framing — pair with the HARD RULE above):`, ` - The task needs sustained source-code editing in a real repo → call_agent('claude-code', …)`, ` or call_agent('codex', …). Don't try to mimic them with native_write_file when the work is`, ` multi-file refactors or feature builds.`, ` - You need a second pair of eyes / cross-checking on your own conclusion → call_agent('codex',`, ` 'audit my findings: …'). Useful before committing or reporting.`, ` - The task is large enough that parallel research helps (e.g. survey 3 independent areas of`, ` a codebase) → fan-out via call_agent('native', …) so each sub-agent's context is fresh.`, ` - Tip: write the sub-agent prompt as a self-contained brief — they don't see this conversation.`, ``, `Web tool routing (HARD RULE — v1.2.64):`, ` - If the user provided a SPECIFIC URL (http://… or https://…) → native_web_fetch.`, ` - If the user wants to FIND / DISCOVER something by keywords ("查找最新 X" / "search for Y" /`, ` "find docs on Z" / "今天 / 最近的 W") → native_web_search FIRST. Don't guess a URL.`, ` - Common pattern: native_web_search(query) → pick a result → native_web_fetch(that.url).`, ` - NEVER call native_web_fetch with a URL you fabricated from the user's keywords.`, ``, `Short-input rule:`, ` - If the user's message is ONLY a slash command alias for an agent name (e.g. "/native", "/llm",`, ` "/na", "/cc", "/oc") and you are already that agent, respond with ONE short line confirming`, ` your identity (e.g. "我是 native,正在听。"). Do NOT call any tool. The slash router handles`, ` actual agent switching; if it didn't switch, the user is already on this agent.`, ``, `Plan tracking (v1.2.124 — native_todo_write):`, ` - When the user gives you a task with ≥ 3 distinct steps, FIRST call native_todo_write({items}) to`, ` write out your plan, then update statuses as you complete each step.`, ` Status values: pending | in_progress | completed. Keep exactly one item in_progress at a time.`, ` - The tool result is a rendered markdown checklist; the user sees your progress.`, ` - Don't call native_todo_write for trivial one-step tasks — overhead.`, ` - Example sequence:`, ` 1) native_todo_write([{c:"Fetch market data",s:"in_progress"}, {c:"Analyse",s:"pending"}, {c:"Reply",s:"pending"}])`, ` 2) … do fetch via native_web_fetch …`, ` 3) native_todo_write([{c:"Fetch market data",s:"completed"}, {c:"Analyse",s:"in_progress"}, {c:"Reply",s:"pending"}])`, ` 4) … analysis …`, ` 5) write final answer to user`, ``, `Closure rule (v1.2.94 — HARD RULE):`, ` - When you finish a tool chain (read_file / web_fetch / native_exec / search_memos / etc.) you`, ` MUST write a short Chinese summary BEFORE stopping. The tool output by itself is not a`, ` user-facing answer — the user can't see the raw JSON / shell stdout. Always close with at`, ` least one sentence stating the finding / conclusion.`, ` - Do NOT end a turn with empty assistant text when you've just called tools. If you genuinely`, ` have nothing to add, say so explicitly ("已查完,未发现 X").`, ``, `Proactive memory rule (v1.2.96 — borrowed from Hermes Agent's "agent-curated memory"):`, ` - Before ending a substantive turn, scan the conversation for facts that should outlive the`, ` current chat. Persist them yourself via mcp__imhub__save_memo — don't wait for the user to`, ` ask you to remember.`, ` - Worth saving (call save_memo for each):`, ` · personal preferences ("我不喝咖啡" / "我用 vim"),`, ` · holdings or portfolio codes ("我持有 600519"),`, ` · recurring people / places ("我家在朝阳" / "爸爸生日 5月8日"),`, ` · stable identifiers (账号 / 邮箱 / API base / 配置路径),`, ` · explicit "记一下" / "remember this" instructions.`, ` - NOT worth saving: one-off questions, transient debugging context, tool outputs that are`, ` already cached elsewhere (memos point AT data, they're not a cache of data).`, ` - Each save_memo call is cheap. Two short memos beat one long one — small atomic facts`, ` search better. Add a 1-line user-facing acknowledgement so the user knows you remembered`, ` (e.g. "已记下 600519 是你的持仓").`, ``, `Long-task SOP (v1.2.93 — for any work you estimate will run > 10 minutes):`, ` - You CANNOT keep a long synchronous turn alive: the IM bridge times out around 30 min, and`, ` most useful work past the 10-min mark loses intermediate state if it crashes mid-flight.`, ` - Instead, use native_exec to invoke the agim bgjob wrapper, which spawns a detached worker`, ` that survives independent of this conversation:`, ` native_exec("/root/.claude/scripts/bgjob start <slug> -- /usr/bin/python3 /path/to/script.py [args]")`, ` Substitute python3 for the runtime you actually need. The wrapper returns a job_id; relay it`, ` to the user verbatim and tell them how to check back: \`bgjob status <id>\` / \`bgjob tail <id> -f\`.`, ` - When the user follows up asking about the job, native_exec calls like \`bgjob status <id>\` or`, ` \`bgjob tail <id> -n 100\` give you the current state + recent log lines.`, ` - The bwrap sandbox (when configured) is bypassed for this specific wrapper path so the`, ` setsid-detached worker actually survives. Any OTHER native_exec command remains sandboxed.`, ` - DO NOT use \`nohup ... &\` or backgrounded shell pipelines for long work — those die with the`, ` parent shell. bgjob is the only correct path on this platform.`, ``, `Python-RPC bridge (v1.2.97 — when a task means MANY similar tool calls):`, ` - When you would otherwise call mcp__imhub__* dozens of times in this chat turn (saving 30`, ` facts, fetching 50 stocks, scoring 100 candidates), DO NOT do it inline — that wastes the`, ` iteration budget and is likely to trip the stuck-loop detector. Write ONE Python script,`, ` run it in bgjob, and let it loop locally while calling back to agim's tool surface via the`, ` local RPC bridge agim sets up automatically for every native_exec child.`, ` - The Python sidecar lives at \`<npm install dir>/bin/agim_rpc.py\` (typically`, ` /usr/local/lib/node_modules/agim-cli/bin/agim_rpc.py — find it with`, ` \`node -e "console.log(require.resolve('agim-cli'))"\`). Import it and instantiate the client:`, ` from agim_rpc import client`, ` rpc = client() # reads env, validates token, no args needed`, ` memos = rpc.search_memos(query="茅台", k=10)`, ` for m in memos.get("rows", []):`, ` ...`, ` rpc.push_message(text="后台跑完了,结果是 X")`, ` - Available tools through the bridge (whitelist): search_memos, save_memo, read_skill,`, ` list_skills, push_message. Everything else (native_exec, fs writes, call_agent, long_task,`, ` ask_user) is NOT exposed — the worker already has a shell + filesystem.`, ` - The token is automatically injected via env (IMHUB_RPC_SOCKET + IMHUB_RPC_TOKEN), bound`, ` to THIS IM thread, valid for 24 h. The worker can only drive this thread; it cannot`, ` push_message into someone else's chat.`, ` - End the worker with rpc.push_message(text="…done…") so the user sees the result come back`, ` asynchronously. Don't expect the user to poll \`bgjob tail\` themselves.`);
180
+ lines.push(`Tools available beyond the four native built-ins (echo / now / sleep / random_uuid):`, ` - agim built-in MCP tools (mcp__imhub__*): read_skill, list_skills, save_memo, search_memos, update_memo, delete_memo, push_message, ask_user, call_agent, long_task, complete_goal`, ` - native filesystem tools: native_read_file, native_write_file, native_list_dir, native_glob, native_grep — constrained to your workspace cwd unless IMHUB_NATIVE_FS_RESTRICT=0`, ` - native web tools: native_web_fetch (r.jina.ai reader by default), native_web_search (duckduckgo → metaso fallback). Private IPs blocked.`, ` - native_exec(command, timeout_ms?, cwd?): run shell commands. Always approval-gated; bwrap sandbox when IMHUB_EXEC_SANDBOX=bwrap.`, ` - External MCP servers configured by the operator:`, externalMcp, ``, `Available skill cards (call mcp__imhub__read_skill('<name>') for the full body):`, skillsBlock, ``, `Guidance:`, ` - Be terse; avoid filler. Prefer tool use over guessing.`, ` - When uncertain, call mcp__imhub__ask_user(question, choices[]) instead of free-form back-and-forth.`, ` - When the user references something they told the bot before, search memos via mcp__imhub__search_memos.`, ``, `Tool selection priority (HARD RULE — v1.2.59):`, ` - For "read this file / list this dir / search this content / fetch this URL", you MUST FIRST try`, ` your own native tools: native_read_file, native_list_dir, native_glob, native_grep,`, ` native_web_fetch (if available). Do NOT delegate these to call_agent.`, ` - call_agent is reserved for tasks that genuinely need a CLI agent's specialised capabilities`, ` (writing/editing source code in a real repo → claude-code; long-running plans → codex; etc).`, ` - You have a per-turn call_agent cap (default 2). Burning it on file reads will leave you`, ` unable to delegate later when you actually need to.`, ``, `When to USE call_agent (v1.2.139 positive framing — pair with the HARD RULE above):`, ` - The task needs sustained source-code editing in a real repo → call_agent('claude-code', …)`, ` or call_agent('codex', …). Don't try to mimic them with native_write_file when the work is`, ` multi-file refactors or feature builds.`, ` - You need a second pair of eyes / cross-checking on your own conclusion → call_agent('codex',`, ` 'audit my findings: …'). Useful before committing or reporting.`, ` - The task is large enough that parallel research helps (e.g. survey 3 independent areas of`, ` a codebase) → fan-out via call_agent('native', …) so each sub-agent's context is fresh.`, ` - Tip: write the sub-agent prompt as a self-contained brief — they don't see this conversation.`, ``, `Verification subagent (T5 — harness pattern, the most reliably useful multi-agent move):`, ` - After you produce a SUBSTANTIVE result (a multi-step conclusion, a refactor, a data`, ` analysis, anything you're about to report or commit), spin up a FRESH subagent whose sole`, ` job is to verify it independently:`, ` call_agent('codex', 'Verify the result below against <source / acceptance criteria>.`, ` Report ONLY discrepancies or risks; if it checks out, say so. <paste result>')`, ` Use 'codex' for code/logic review, 'native' for a fresh-eyes fact/consistency check.`, ` - Give the verifier a SELF-CONTAINED brief: restate the claim AND how to check it, and paste`, ` the artifact. It does NOT see this conversation — "verify my findings" with nothing attached`, ` is useless.`, ` - Synthesize, don't delegate understanding: digest what you learned into a PRECISE spec before`, ` delegating implementation or verification. "Based on your findings, fix it" is an anti-pattern`, ` — you (the coordinator) must state exactly what to do, not hand off the thinking.`, ` - Don't over-verify: skip the verifier for trivial / low-stakes turns (it costs a call_agent hop).`, ``, `Web tool routing (HARD RULE — v1.2.64):`, ` - If the user provided a SPECIFIC URL (http://… or https://…) → native_web_fetch.`, ` - If the user wants to FIND / DISCOVER something by keywords ("查找最新 X" / "search for Y" /`, ` "find docs on Z" / "今天 / 最近的 W") → native_web_search FIRST. Don't guess a URL.`, ` - Common pattern: native_web_search(query) → pick a result → native_web_fetch(that.url).`, ` - NEVER call native_web_fetch with a URL you fabricated from the user's keywords.`, ``, `Short-input rule:`, ` - If the user's message is ONLY a slash command alias for an agent name (e.g. "/native", "/llm",`, ` "/na", "/cc", "/oc") and you are already that agent, respond with ONE short line confirming`, ` your identity (e.g. "我是 native,正在听。"). Do NOT call any tool. The slash router handles`, ` actual agent switching; if it didn't switch, the user is already on this agent.`, ``, `Plan tracking (v1.2.124 — native_todo_write):`, ` - When the user gives you a task with ≥ 3 distinct steps, FIRST call native_todo_write({items}) to`, ` write out your plan, then update statuses as you complete each step.`, ` Status values: pending | in_progress | completed. Keep exactly one item in_progress at a time.`, ` - The tool result is a rendered markdown checklist; the user sees your progress.`, ` - Don't call native_todo_write for trivial one-step tasks — overhead.`, ` - Example sequence:`, ` 1) native_todo_write([{c:"Fetch market data",s:"in_progress"}, {c:"Analyse",s:"pending"}, {c:"Reply",s:"pending"}])`, ` 2) … do fetch via native_web_fetch …`, ` 3) native_todo_write([{c:"Fetch market data",s:"completed"}, {c:"Analyse",s:"in_progress"}, {c:"Reply",s:"pending"}])`, ` 4) … analysis …`, ` 5) write final answer to user`, ``, `Closure rule (v1.2.94 — HARD RULE):`, ` - When you finish a tool chain (read_file / web_fetch / native_exec / search_memos / etc.) you`, ` MUST write a short Chinese summary BEFORE stopping. The tool output by itself is not a`, ` user-facing answer — the user can't see the raw JSON / shell stdout. Always close with at`, ` least one sentence stating the finding / conclusion.`, ` - Do NOT end a turn with empty assistant text when you've just called tools. If you genuinely`, ` have nothing to add, say so explicitly ("已查完,未发现 X").`, ``, `Proactive memory rule (v1.2.96 — borrowed from Hermes Agent's "agent-curated memory"):`, ` - Before ending a substantive turn, scan the conversation for facts that should outlive the`, ` current chat. Persist them yourself via mcp__imhub__save_memo — don't wait for the user to`, ` ask you to remember.`, ` - Worth saving (call save_memo for each):`, ` · personal preferences ("我不喝咖啡" / "我用 vim"),`, ` · holdings or portfolio codes ("我持有 600519"),`, ` · recurring people / places ("我家在朝阳" / "爸爸生日 5月8日"),`, ` · stable identifiers (账号 / 邮箱 / API base / 配置路径),`, ` · explicit "记一下" / "remember this" instructions.`, ` - NOT worth saving: one-off questions, transient debugging context, tool outputs that are`, ` already cached elsewhere (memos point AT data, they're not a cache of data).`, ` - Each save_memo call is cheap. Two short memos beat one long one — small atomic facts`, ` search better. Add a 1-line user-facing acknowledgement so the user knows you remembered`, ` (e.g. "已记下 600519 是你的持仓").`, ``, `Long-task SOP (v1.2.93 — for any work you estimate will run > 10 minutes):`, ` - You CANNOT keep a long synchronous turn alive: the IM bridge times out around 30 min, and`, ` most useful work past the 10-min mark loses intermediate state if it crashes mid-flight.`, ` - Instead, use native_exec to invoke the agim bgjob wrapper, which spawns a detached worker`, ` that survives independent of this conversation:`, ` native_exec("/root/.claude/scripts/bgjob start <slug> -- /usr/bin/python3 /path/to/script.py [args]")`, ` Substitute python3 for the runtime you actually need. The wrapper returns a job_id; relay it`, ` to the user verbatim and tell them how to check back: \`bgjob status <id>\` / \`bgjob tail <id> -f\`.`, ` - When the user follows up asking about the job, native_exec calls like \`bgjob status <id>\` or`, ` \`bgjob tail <id> -n 100\` give you the current state + recent log lines.`, ` - The bwrap sandbox (when configured) is bypassed for this specific wrapper path so the`, ` setsid-detached worker actually survives. Any OTHER native_exec command remains sandboxed.`, ` - DO NOT use \`nohup ... &\` or backgrounded shell pipelines for long work — those die with the`, ` parent shell. bgjob is the only correct path on this platform.`, ``, `Python-RPC bridge (v1.2.97 — when a task means MANY similar tool calls):`, ` - When you would otherwise call mcp__imhub__* dozens of times in this chat turn (saving 30`, ` facts, fetching 50 stocks, scoring 100 candidates), DO NOT do it inline — that wastes the`, ` iteration budget and is likely to trip the stuck-loop detector. Write ONE Python script,`, ` run it in bgjob, and let it loop locally while calling back to agim's tool surface via the`, ` local RPC bridge agim sets up automatically for every native_exec child.`, ` - The Python sidecar lives at \`<npm install dir>/bin/agim_rpc.py\` (typically`, ` /usr/local/lib/node_modules/agim-cli/bin/agim_rpc.py — find it with`, ` \`node -e "console.log(require.resolve('agim-cli'))"\`). Import it and instantiate the client:`, ` from agim_rpc import client`, ` rpc = client() # reads env, validates token, no args needed`, ` memos = rpc.search_memos(query="茅台", k=10)`, ` for m in memos.get("rows", []):`, ` ...`, ` rpc.push_message(text="后台跑完了,结果是 X")`, ` - Available tools through the bridge (whitelist): search_memos, save_memo, read_skill,`, ` list_skills, push_message. Everything else (native_exec, fs writes, call_agent, long_task,`, ` ask_user) is NOT exposed — the worker already has a shell + filesystem.`, ` - The token is automatically injected via env (IMHUB_RPC_SOCKET + IMHUB_RPC_TOKEN), bound`, ` to THIS IM thread, valid for 24 h. The worker can only drive this thread; it cannot`, ` push_message into someone else's chat.`, ` - End the worker with rpc.push_message(text="…done…") so the user sees the result come back`, ` asynchronously. Don't expect the user to poll \`bgjob tail\` themselves.`);
112
181
  return lines.join('\n');
113
182
  }
114
- /** Read <cwd>/AGENTS.md (if present) and prepare it for injection into
115
- * the system prompt. Returns '' on missing file / read failure / empty
116
- * content. Sanitises text length (8000 char cap) and scans for the
117
- * classic prompt-injection signals ("ignore previous instructions",
118
- * "always allow", etc.); when matches are found we still inject (the
119
- * operator likely wrote them deliberately) but log a warn + audit
120
- * hit so post-incident forensics can correlate. Honours
121
- * IMHUB_NATIVE_AGENT_ROLE_FILE for a non-default location. */
122
- export function readOperatorRole(cwd) {
123
- const override = process.env.IMHUB_NATIVE_AGENT_ROLE_FILE;
124
- const path = override && override.length > 0 ? override : pathJoin(cwd, 'AGENTS.md');
125
- if (!fsExistsSync(path))
183
+ const _operatorRoleCache = new Map();
184
+ function readRoleFile(path) {
185
+ let st;
186
+ try {
187
+ st = fsStatSync(path);
188
+ }
189
+ catch {
190
+ // Missing / unreadable no role block; drop any stale cache entry.
191
+ _operatorRoleCache.delete(path);
126
192
  return '';
193
+ }
194
+ const cached = _operatorRoleCache.get(path);
195
+ if (cached && cached.mtimeMs === st.mtimeMs && cached.size === st.size) {
196
+ return cached.content;
197
+ }
127
198
  let raw = '';
128
199
  try {
129
200
  raw = fsReadFileSync(path, 'utf-8');
130
201
  }
131
202
  catch {
203
+ _operatorRoleCache.delete(path);
132
204
  return '';
133
205
  }
134
206
  const trimmed = raw.trim();
135
- if (!trimmed)
207
+ if (!trimmed) {
208
+ _operatorRoleCache.set(path, { mtimeMs: st.mtimeMs, size: st.size, content: '' });
136
209
  return '';
210
+ }
137
211
  try {
138
212
  const scan = scanForInjectionAttempts(trimmed);
139
213
  if (scan.suspicious) {
@@ -145,7 +219,70 @@ export function readOperatorRole(cwd) {
145
219
  }
146
220
  }
147
221
  catch { /* scan is best-effort */ }
148
- return sanitizeForInjection(trimmed, 8000);
222
+ const content = sanitizeForInjection(trimmed, 8000);
223
+ _operatorRoleCache.set(path, { mtimeMs: st.mtimeMs, size: st.size, content });
224
+ return content;
225
+ }
226
+ /**
227
+ * Resolve the operator role definition injected into native's system prompt.
228
+ *
229
+ * T4 (instruction hierarchy, distilled from the agentic-harness Memory
230
+ * pattern's "local overrides win — always"). Instead of a single
231
+ * `<cwd>/AGENTS.md`, discover a layered stack within the native workspace
232
+ * and concatenate in ASCENDING priority so the most-local block appears last
233
+ * and gets the most model attention. LOCAL WINS:
234
+ *
235
+ * 1. project — <cwd>/AGENTS.md (the shared native-workspace role)
236
+ * 2. local — <cwd>/AGENTS.local.md (private override, not version-ctl'd)
237
+ *
238
+ * Both layers live under the native workspace cwd, so the stack is
239
+ * self-contained (no dependency on a global ~/.agim file — native has a
240
+ * single workspace, so "project" already IS the operator-global role; a
241
+ * cross-workspace user layer can be added later if that changes).
242
+ *
243
+ * IMHUB_NATIVE_AGENT_ROLE_FILE still forces a single explicit file (operators
244
+ * who pin one path bypass discovery entirely). Each layer is independently
245
+ * memoized + injection-scanned + capped at 8000 chars by readRoleFile.
246
+ */
247
+ export function readOperatorRole(cwd) {
248
+ const override = process.env.IMHUB_NATIVE_AGENT_ROLE_FILE;
249
+ if (override && override.length > 0) {
250
+ // An explicit pin is operator-chosen → trusted regardless of the gate.
251
+ return readRoleFile(override);
252
+ }
253
+ // T6 — workspace trust gate. Discovered workspace files are skipped wholesale
254
+ // when the workspace is marked untrusted (see isWorkspaceTrusted).
255
+ if (!isWorkspaceTrusted()) {
256
+ return '';
257
+ }
258
+ const parts = [];
259
+ for (const p of [pathJoin(cwd, 'AGENTS.md'), pathJoin(cwd, 'AGENTS.local.md')]) {
260
+ const c = readRoleFile(p);
261
+ if (c)
262
+ parts.push(c);
263
+ }
264
+ return parts.join('\n\n');
265
+ }
266
+ /**
267
+ * T6 — workspace trust gate (distilled from the agentic-harness Lifecycle
268
+ * pattern's "trust is all-or-nothing; an untrusted workspace disables the
269
+ * whole extension surface, not just suspicious parts").
270
+ *
271
+ * agim treats `<cwd>/AGENTS.md` + `AGENTS.local.md` as an operator-authored
272
+ * role definition injected into the system prompt — a privileged surface. In
273
+ * multi-tenant / A2A setups the native cwd can point at a directory whose
274
+ * contents are NOT fully operator-controlled, where an attacker-planted
275
+ * AGENTS.md is a prompt-injection vector. Setting
276
+ * `IMHUB_NATIVE_TRUST_WORKSPACE=off` (or 0/false/no) makes readOperatorRole
277
+ * skip ALL workspace-discovered role files at once. Default is trusted
278
+ * (on) for backward compatibility — operators opt into the stricter posture.
279
+ *
280
+ * An explicit `IMHUB_NATIVE_AGENT_ROLE_FILE` bypasses the gate: the operator
281
+ * pinned that exact file deliberately, so it stays trusted.
282
+ */
283
+ export function isWorkspaceTrusted() {
284
+ const raw = (process.env.IMHUB_NATIVE_TRUST_WORKSPACE ?? '').toLowerCase().trim();
285
+ return !(raw === 'off' || raw === '0' || raw === 'false' || raw === 'no');
149
286
  }
150
287
  /** Role priority for picking which LLM backend powers the native chat
151
288
  * turn. First found wins. Operators can override the role via
@@ -328,6 +465,44 @@ function composeOffTrackRecap(result, reason, redirect) {
328
465
  lines.push(' · 或 /cc / /oc / /cs 切到别的智能体接手');
329
466
  return lines.join('\n');
330
467
  }
468
+ /**
469
+ * v1.2.147 — recap for the "hallucinated tool-call" failure mode.
470
+ *
471
+ * Trigger: agent-loop detected the model's final text narrates a
472
+ * native_* tool invocation (e.g. "我现在调用 native_write_file:
473
+ * ```python ...```" or "let me invoke native_exec") but the response
474
+ * carried zero real toolCalls. The model promised an action it did not
475
+ * take. Without this branch the lie would get shipped to the user as a
476
+ * normal reply.
477
+ *
478
+ * Distinct from the empty / max-iter / stuck-loop recaps because the
479
+ * fail-mode is model-side rather than budget-side: the model is fine,
480
+ * just not in a tool-calling mood. The recap points the user at a
481
+ * backend swap as the most reliable next step, since prompt tweaking
482
+ * has limited leverage when the model has already drifted out of the
483
+ * function-calling schema. Pure formatting; no LLM call.
484
+ */
485
+ function composeHallucinatedToolRecap(result, backend) {
486
+ const lines = [];
487
+ lines.push('🧐 模型说要调用工具,但实际上没真的发起调用。');
488
+ lines.push('为了不让你看到空承诺,已经把这次回复挡下。');
489
+ lines.push('');
490
+ lines.push(`当前 native-chat 后端:${backend}`);
491
+ if (result.toolCalls.length > 0) {
492
+ lines.push(`本轮在此之前已真正完成了 ${result.toolCalls.length} 次工具调用,那部分有效。`);
493
+ }
494
+ else {
495
+ lines.push('本轮没有任何真实工具调用产出。');
496
+ }
497
+ lines.push('');
498
+ lines.push('怎么继续:');
499
+ lines.push(' · 直接回「重试」让我重新跑一遍这一步');
500
+ lines.push(' · 或在 /settings/llm 把 native-chat 角色换成工具调用更稳定的后端(Claude / GPT 系列)');
501
+ lines.push(' · 或 /cc / /oc / /cs 切到别的智能体接手这一步');
502
+ lines.push('');
503
+ lines.push('(检测器可用 IMHUB_NATIVE_HALLUCINATION_DETECT=off 关掉)');
504
+ return lines.join('\n');
505
+ }
331
506
  /**
332
507
  * v1.2.142 — Stage-report retry. Replaces v1.2.94's empty-only auto-
333
508
  * summary with a single helper used by all four "unhappy ending"
@@ -613,31 +788,6 @@ const PLAN_MODE_DENY_TOOLS = [
613
788
  export function isPlanModeOn(threadKey) {
614
789
  return effectivePlanModeOn(threadKey);
615
790
  }
616
- /**
617
- * v1.2.109 — names of native tools that are safe to dispatch in parallel
618
- * within a single iteration. Strict opt-in: only tools with no side
619
- * effects, no shared mutable state, and order-independent semantics.
620
- * MCP tools beyond explicit read-only ones are NOT listed because they
621
- * share session state with the agent loop's mcp-client and have caused
622
- * races in the past (see comment near agent-loop's old serial-only
623
- * note). Pure/read-only entries:
624
- * - native_{echo,now,random_uuid}: pure
625
- * - native_{read_file,list_dir,glob,grep}: read-only fs
626
- * - native_{web_fetch,web_search}: read-only network (SSRF-checked)
627
- * - mcp__imhub__{read_skill,list_skills,search_memos}: read-only
628
- * - mcp__imhub__memory_{list,query}: read-only memory
629
- * Everything else (native_exec, native_write_file, save/update/delete
630
- * memo, push_message, ask_user, call_agent, long_task, complete_goal,
631
- * other MCP) remains serial.
632
- */
633
- const NATIVE_PARALLEL_SAFE_TOOLS = new Set([
634
- 'native_echo', 'native_now', 'native_random_uuid',
635
- 'native_read_file', 'native_list_dir', 'native_glob', 'native_grep',
636
- 'native_web_fetch', 'native_web_search',
637
- 'mcp__imhub__read_skill', 'mcp__imhub__list_skills',
638
- 'mcp__imhub__search_memos',
639
- 'mcp__imhub__memory_list', 'mcp__imhub__memory_query',
640
- ]);
641
791
  /**
642
792
  * v1.2.60 — bridge native's policy gate to the approval-bus so tools
643
793
  * that the gate would otherwise silently deny instead surface an IM
@@ -903,13 +1053,15 @@ class NativeAgentAdapter {
903
1053
  // the per-thread workspace subtree. Was previously resolved AFTER
904
1054
  // dispatch composition; moved up so fs tools see the right root.
905
1055
  const cwd = resolveAgentCwd('native', opts) || defaultAgentCwd('native');
906
- const dispatch = combineDispatchers(buildBuiltinDispatcher(), buildFsDispatcher({ cwd }), buildWebDispatcher(), buildExecDispatcher({
1056
+ // T2 (single tool registry): assemble the advertised tool list, the
1057
+ // dispatch chain, AND the per-call concurrency classifier from ONE
1058
+ // source-of-truth (see tool-registry.ts). This replaced three
1059
+ // hand-maintained parallel lists (tools[] / combineDispatchers / the
1060
+ // static parallelSafeTools Set) that silently drifted when a tool was
1061
+ // added. The plan-exit dispatcher is always wired (it self-refuses off
1062
+ // plan mode); its tool is advertised only when plan mode is on.
1063
+ const assembled = assembleNativeTools({
907
1064
  cwd,
908
- // v1.2.97 — when this thread has a usable RunContext, hand it
909
- // to exec-dispatcher so every spawned child inherits a fresh
910
- // RPC token bound to the thread. Enables the Python-RPC bridge
911
- // (bgjob worker → agim parent → memo/skill/push). Tokens are
912
- // per-spawn, expire in 24h, and only authorise THIS thread.
913
1065
  rpcCtx: opts.platform && opts.threadId
914
1066
  ? {
915
1067
  platform: opts.platform,
@@ -918,45 +1070,20 @@ class NativeAgentAdapter {
918
1070
  userId: opts.userId ?? '',
919
1071
  }
920
1072
  : undefined,
921
- }),
922
- // v1.2.124 — TodoWrite-style per-thread plan tracker. Sits
923
- // before imhub so a stray tool name collision (none today) would
924
- // be caught early. Uses the same composite thread key the PlanMode
925
- // resolver does so users can find their list in audit.
926
- buildTodoDispatcher(planThreadKey ?? `native:${sessionId}`),
927
- // v1.2.131 — ExitPlanMode handshake. Always wired; the dispatcher
928
- // itself refuses calls when the thread is NOT in plan mode, so
929
- // the model can't accidentally exit something it didn't enter.
930
- buildPlanExitDispatcher({
931
- threadKey: planThreadKey ?? `native:${sessionId}`,
932
- runId: sessionId,
933
- platform: opts.platform,
934
- channelId: opts.channelId,
935
- threadId: opts.threadId,
936
- userId: opts.userId,
937
- }), buildImhubDispatcher(imhubCtx), buildMcpDispatcher());
938
- // Compose tool list the same way (built-in → fs → web → exec → todo → imhub → external MCP).
939
- // Provider tool roster wins on duplicate names via "later
940
- // definition wins" semantics inside the provider, but our naming
941
- // is namespaced (`native_*`, `mcp__imhub__*`, `mcp_<server>_*`)
942
- // so collisions are not expected in practice.
943
- const tools = [
944
- ...getBuiltinTools(),
945
- ...getFsTools(),
946
- ...getWebTools(),
947
- ...getExecTools(),
948
- ...getTodoTools(),
949
- ...getImhubTools(),
950
- ...getAllMcpTools(),
951
- ];
952
- // v1.2.131 — when this thread is in Plan Mode, advertise the
953
- // dedicated exit tool so the model knows the handshake exists. We
954
- // don't always advertise it: a model in normal mode shouldn't be
955
- // tempted to call it (the dispatcher would refuse anyway, but
956
- // keeping the roster honest is cleaner).
957
- if (planThreadKey && effectivePlanModeOn(planThreadKey)) {
958
- tools.push(...getPlanExitTools());
959
- }
1073
+ todoThreadKey: planThreadKey ?? `native:${sessionId}`,
1074
+ planExitCtx: {
1075
+ threadKey: planThreadKey ?? `native:${sessionId}`,
1076
+ runId: sessionId,
1077
+ platform: opts.platform,
1078
+ channelId: opts.channelId,
1079
+ threadId: opts.threadId,
1080
+ userId: opts.userId,
1081
+ },
1082
+ advertisePlanExit: !!(planThreadKey && effectivePlanModeOn(planThreadKey)),
1083
+ imhubCtx,
1084
+ });
1085
+ const tools = assembled.tools;
1086
+ const dispatch = assembled.dispatch;
960
1087
  const policy = resolvePolicy(planThreadKey);
961
1088
  // v1.2.60 — when the policy would silently deny a tool call,
962
1089
  // escalate to the user via an IM approval card instead. Only
@@ -989,7 +1116,12 @@ class NativeAgentAdapter {
989
1116
  // Was previously resolved here — that was fine before native had
990
1117
  // fs tools, but fs-dispatcher now needs the value at dispatch
991
1118
  // build time.
992
- const traceId = `native-${sessionId}-${Date.now()}`;
1119
+ // ADR-0002 prefer the inbound turn's trace id (plumbed via opts.traceId
1120
+ // from the router) so the native iteration / turn audit rows correlate
1121
+ // back to the originating IM message in SIEM joins. Fall back to a
1122
+ // self-minted id only for entry points that don't carry one (e.g. an A2A
1123
+ // in-process spawn or a direct CLI/smoke invocation).
1124
+ const traceId = opts.traceId || `native-${sessionId}-${Date.now()}`;
993
1125
  // Wall-clock cap for the agent loop. agim's IM-layer enforces a 30-
994
1126
  // minute hard ceiling per turn (DEFAULT_TIMEOUT_MS in agent-base.ts);
995
1127
  // we set the inner loop a hair below so the loop's own abort fires
@@ -1064,11 +1196,12 @@ class NativeAgentAdapter {
1064
1196
  traceId,
1065
1197
  },
1066
1198
  hooks: heartbeats.hooks,
1067
- // v1.2.109 — declare read-only / pure tools parallel-safe so
1199
+ // v1.2.109 / T2 — declare read-only / pure tools parallel-safe so
1068
1200
  // multi-call iterations (e.g. "read these 3 files") run
1069
- // concurrently. Conservative allowlist; see the constant for
1070
- // what's in / out and why.
1071
- parallelSafeTools: NATIVE_PARALLEL_SAFE_TOOLS,
1201
+ // concurrently. Now a PER-CALL classifier owned by the tool
1202
+ // registry (fail-closed: unknown / throwing serial), replacing
1203
+ // the static per-name set.
1204
+ parallelSafeClassifier: assembled.isParallelSafe,
1072
1205
  // v1.2.112 — stream provider responses so partial assistant
1073
1206
  // text survives the IM 30-min hard timeout. Env-gated kill
1074
1207
  // switch (`IMHUB_NATIVE_STREAM_PARTIAL=off` + global
@@ -1222,6 +1355,24 @@ class NativeAgentAdapter {
1222
1355
  else if (result.finishReason === 'aborted') {
1223
1356
  body = '⏹ native agent aborted before completion.';
1224
1357
  }
1358
+ else if (result.finishReason === 'hallucinated_tools') {
1359
+ // v1.2.147 — agent-loop detected the model narrated a tool
1360
+ // invocation ("我现在调用 native_write_file: ```python …```")
1361
+ // without actually emitting toolCalls. Surface a recap that
1362
+ // names the failure mode + suggests a backend switch, instead
1363
+ // of shipping the lie as a normal reply.
1364
+ log.warn({
1365
+ event: 'native.turn.hallucinated_tools',
1366
+ sessionId,
1367
+ backend: usedProvider.name,
1368
+ role: usedRole,
1369
+ iterations: result.iterations,
1370
+ toolCallCount: result.toolCalls.length,
1371
+ textLen: (result.text || '').length,
1372
+ elapsedMs: Date.now() - startedAt,
1373
+ }, `native turn ended with hallucinated tool-call narration (no real toolCalls emitted)`);
1374
+ body = composeHallucinatedToolRecap(result, usedProvider.name);
1375
+ }
1225
1376
  else if (!body) {
1226
1377
  // Normal `stop` finish but the model didn't write anything. Most
1227
1378
  // often the model completed a tool chain and forgot to close