@zhin.js/agent 0.1.29 → 0.1.31

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 (428) hide show
  1. package/README.md +48 -23
  2. package/lib/agent-prompt/resolver.d.ts.map +1 -1
  3. package/lib/agent-prompt/resolver.js +0 -1
  4. package/lib/agent-prompt/resolver.js.map +1 -1
  5. package/lib/assistant/config.d.ts +67 -0
  6. package/lib/assistant/config.d.ts.map +1 -0
  7. package/lib/assistant/config.js +47 -0
  8. package/lib/assistant/config.js.map +1 -0
  9. package/lib/assistant/domains/home-assistant.d.ts +37 -0
  10. package/lib/assistant/domains/home-assistant.d.ts.map +1 -0
  11. package/lib/assistant/domains/home-assistant.js +113 -0
  12. package/lib/assistant/domains/home-assistant.js.map +1 -0
  13. package/lib/assistant/event-ingress.d.ts +28 -0
  14. package/lib/assistant/event-ingress.d.ts.map +1 -0
  15. package/lib/assistant/event-ingress.js +132 -0
  16. package/lib/assistant/event-ingress.js.map +1 -0
  17. package/lib/assistant/event-types.d.ts +28 -0
  18. package/lib/assistant/event-types.d.ts.map +1 -0
  19. package/lib/assistant/event-types.js +2 -0
  20. package/lib/assistant/event-types.js.map +1 -0
  21. package/lib/assistant/home-config.d.ts +36 -0
  22. package/lib/assistant/home-config.d.ts.map +1 -0
  23. package/lib/assistant/home-config.js +21 -0
  24. package/lib/assistant/home-config.js.map +1 -0
  25. package/lib/assistant/home-mcp-bridge.d.ts +12 -0
  26. package/lib/assistant/home-mcp-bridge.d.ts.map +1 -0
  27. package/lib/assistant/home-mcp-bridge.js +26 -0
  28. package/lib/assistant/home-mcp-bridge.js.map +1 -0
  29. package/lib/assistant/home-policy.d.ts +16 -0
  30. package/lib/assistant/home-policy.d.ts.map +1 -0
  31. package/lib/assistant/home-policy.js +46 -0
  32. package/lib/assistant/home-policy.js.map +1 -0
  33. package/lib/assistant/home-tools.d.ts +15 -0
  34. package/lib/assistant/home-tools.d.ts.map +1 -0
  35. package/lib/assistant/home-tools.js +141 -0
  36. package/lib/assistant/home-tools.js.map +1 -0
  37. package/lib/assistant/index.d.ts +31 -0
  38. package/lib/assistant/index.d.ts.map +1 -0
  39. package/lib/assistant/index.js +18 -0
  40. package/lib/assistant/index.js.map +1 -0
  41. package/lib/assistant/job-engine.d.ts +40 -0
  42. package/lib/assistant/job-engine.d.ts.map +1 -0
  43. package/lib/assistant/job-engine.js +155 -0
  44. package/lib/assistant/job-engine.js.map +1 -0
  45. package/lib/assistant/job-scheduler.d.ts +6 -0
  46. package/lib/assistant/job-scheduler.d.ts.map +1 -0
  47. package/lib/assistant/job-scheduler.js +59 -0
  48. package/lib/assistant/job-scheduler.js.map +1 -0
  49. package/lib/assistant/job-store.d.ts +55 -0
  50. package/lib/assistant/job-store.d.ts.map +1 -0
  51. package/lib/assistant/job-store.js +280 -0
  52. package/lib/assistant/job-store.js.map +1 -0
  53. package/lib/assistant/job-worker.d.ts +20 -0
  54. package/lib/assistant/job-worker.d.ts.map +1 -0
  55. package/lib/assistant/job-worker.js +73 -0
  56. package/lib/assistant/job-worker.js.map +1 -0
  57. package/lib/assistant/legacy-convert.d.ts +48 -0
  58. package/lib/assistant/legacy-convert.d.ts.map +1 -0
  59. package/lib/assistant/legacy-convert.js +108 -0
  60. package/lib/assistant/legacy-convert.js.map +1 -0
  61. package/lib/assistant/notification-router.d.ts +34 -0
  62. package/lib/assistant/notification-router.d.ts.map +1 -0
  63. package/lib/assistant/notification-router.js +96 -0
  64. package/lib/assistant/notification-router.js.map +1 -0
  65. package/lib/assistant/profile-loader.d.ts +29 -0
  66. package/lib/assistant/profile-loader.d.ts.map +1 -0
  67. package/lib/assistant/profile-loader.js +168 -0
  68. package/lib/assistant/profile-loader.js.map +1 -0
  69. package/lib/assistant/profile-types.d.ts +56 -0
  70. package/lib/assistant/profile-types.d.ts.map +1 -0
  71. package/lib/assistant/profile-types.js +9 -0
  72. package/lib/assistant/profile-types.js.map +1 -0
  73. package/lib/assistant/runtime-registry.d.ts +20 -0
  74. package/lib/assistant/runtime-registry.d.ts.map +1 -0
  75. package/lib/assistant/runtime-registry.js +19 -0
  76. package/lib/assistant/runtime-registry.js.map +1 -0
  77. package/lib/assistant/types.d.ts +75 -0
  78. package/lib/assistant/types.d.ts.map +1 -0
  79. package/lib/assistant/types.js +6 -0
  80. package/lib/assistant/types.js.map +1 -0
  81. package/lib/bootstrap.d.ts +1 -5
  82. package/lib/bootstrap.d.ts.map +1 -1
  83. package/lib/bootstrap.js +1 -34
  84. package/lib/bootstrap.js.map +1 -1
  85. package/lib/builtin/analyze-media-tool.d.ts +13 -0
  86. package/lib/builtin/analyze-media-tool.d.ts.map +1 -0
  87. package/lib/builtin/analyze-media-tool.js +100 -0
  88. package/lib/builtin/analyze-media-tool.js.map +1 -0
  89. package/lib/builtin/ask-user-tool.d.ts +1 -1
  90. package/lib/builtin/ask-user-tool.d.ts.map +1 -1
  91. package/lib/builtin/ask-user-tool.js +1 -1
  92. package/lib/builtin/ask-user-tool.js.map +1 -1
  93. package/lib/builtin/edit-file-tool.d.ts.map +1 -1
  94. package/lib/builtin/edit-file-tool.js +5 -1
  95. package/lib/builtin/edit-file-tool.js.map +1 -1
  96. package/lib/builtin/generate-image-tool.d.ts +21 -0
  97. package/lib/builtin/generate-image-tool.d.ts.map +1 -0
  98. package/lib/builtin/generate-image-tool.js +138 -0
  99. package/lib/builtin/generate-image-tool.js.map +1 -0
  100. package/lib/builtin/read-file-tool.d.ts +1 -1
  101. package/lib/builtin/read-file-tool.d.ts.map +1 -1
  102. package/lib/builtin/read-file-tool.js +2 -7
  103. package/lib/builtin/read-file-tool.js.map +1 -1
  104. package/lib/builtin/run-deferred-task-tool.d.ts +1 -1
  105. package/lib/builtin/run-deferred-task-tool.d.ts.map +1 -1
  106. package/lib/builtin/run-deferred-task-tool.js +1 -1
  107. package/lib/builtin/run-deferred-task-tool.js.map +1 -1
  108. package/lib/builtin/spawn-task-tool.d.ts +1 -1
  109. package/lib/builtin/spawn-task-tool.d.ts.map +1 -1
  110. package/lib/builtin/spawn-task-tool.js +25 -4
  111. package/lib/builtin/spawn-task-tool.js.map +1 -1
  112. package/lib/builtin/write-file-tool.d.ts.map +1 -1
  113. package/lib/builtin/write-file-tool.js +6 -1
  114. package/lib/builtin/write-file-tool.js.map +1 -1
  115. package/lib/builtin-tools.d.ts.map +1 -1
  116. package/lib/builtin-tools.js +2 -0
  117. package/lib/builtin-tools.js.map +1 -1
  118. package/lib/config/agent-binding-registry.d.ts +14 -0
  119. package/lib/config/agent-binding-registry.d.ts.map +1 -0
  120. package/lib/config/agent-binding-registry.js +30 -0
  121. package/lib/config/agent-binding-registry.js.map +1 -0
  122. package/lib/config/agent-bindings.d.ts +4 -0
  123. package/lib/config/agent-bindings.d.ts.map +1 -0
  124. package/lib/config/agent-bindings.js +16 -0
  125. package/lib/config/agent-bindings.js.map +1 -0
  126. package/lib/config/normalize-ai-config.d.ts +15 -0
  127. package/lib/config/normalize-ai-config.d.ts.map +1 -0
  128. package/lib/config/normalize-ai-config.js +72 -0
  129. package/lib/config/normalize-ai-config.js.map +1 -0
  130. package/lib/config/provider-instance.d.ts +5 -0
  131. package/lib/config/provider-instance.d.ts.map +1 -0
  132. package/lib/config/provider-instance.js +65 -0
  133. package/lib/config/provider-instance.js.map +1 -0
  134. package/lib/config/types.d.ts +36 -0
  135. package/lib/config/types.d.ts.map +1 -0
  136. package/lib/config/types.js +2 -0
  137. package/lib/config/types.js.map +1 -0
  138. package/lib/config/validate-ai-config.d.ts +3 -0
  139. package/lib/config/validate-ai-config.d.ts.map +1 -0
  140. package/lib/config/validate-ai-config.js +40 -0
  141. package/lib/config/validate-ai-config.js.map +1 -0
  142. package/lib/cron-engine.d.ts +20 -18
  143. package/lib/cron-engine.d.ts.map +1 -1
  144. package/lib/cron-engine.js +42 -18
  145. package/lib/cron-engine.js.map +1 -1
  146. package/lib/deferred-worker-runner.d.ts +2 -0
  147. package/lib/deferred-worker-runner.d.ts.map +1 -1
  148. package/lib/deferred-worker-runner.js +17 -35
  149. package/lib/deferred-worker-runner.js.map +1 -1
  150. package/lib/discovery/agents.d.ts +2 -0
  151. package/lib/discovery/agents.d.ts.map +1 -1
  152. package/lib/discovery/agents.js +5 -0
  153. package/lib/discovery/agents.js.map +1 -1
  154. package/lib/hooks.d.ts +0 -1
  155. package/lib/hooks.d.ts.map +1 -1
  156. package/lib/hooks.js.map +1 -1
  157. package/lib/index.d.ts +14 -5
  158. package/lib/index.d.ts.map +1 -1
  159. package/lib/index.js +8 -3
  160. package/lib/index.js.map +1 -1
  161. package/lib/init/activate-ai-database-storage.d.ts +1 -1
  162. package/lib/init/activate-ai-database-storage.d.ts.map +1 -1
  163. package/lib/init/activate-ai-database-storage.js +16 -13
  164. package/lib/init/activate-ai-database-storage.js.map +1 -1
  165. package/lib/init/create-zhin-agent.d.ts.map +1 -1
  166. package/lib/init/create-zhin-agent.js +180 -69
  167. package/lib/init/create-zhin-agent.js.map +1 -1
  168. package/lib/init/introspection-commands-format.d.ts +40 -0
  169. package/lib/init/introspection-commands-format.d.ts.map +1 -0
  170. package/lib/init/introspection-commands-format.js +93 -0
  171. package/lib/init/introspection-commands-format.js.map +1 -0
  172. package/lib/init/message-media.d.ts.map +1 -1
  173. package/lib/init/message-media.js +12 -0
  174. package/lib/init/message-media.js.map +1 -1
  175. package/lib/init/output-renderer.d.ts +4 -0
  176. package/lib/init/output-renderer.d.ts.map +1 -1
  177. package/lib/init/output-renderer.js +6 -0
  178. package/lib/init/output-renderer.js.map +1 -1
  179. package/lib/init/register-ai-trigger.d.ts.map +1 -1
  180. package/lib/init/register-ai-trigger.js +155 -8
  181. package/lib/init/register-ai-trigger.js.map +1 -1
  182. package/lib/init/register-builtin-tools.d.ts.map +1 -1
  183. package/lib/init/register-builtin-tools.js +13 -3
  184. package/lib/init/register-builtin-tools.js.map +1 -1
  185. package/lib/init/register-db-models.d.ts.map +1 -1
  186. package/lib/init/register-db-models.js +7 -8
  187. package/lib/init/register-db-models.js.map +1 -1
  188. package/lib/init/register-db-upgrade.d.ts +1 -1
  189. package/lib/init/register-db-upgrade.js +2 -2
  190. package/lib/init/register-db-upgrade.js.map +1 -1
  191. package/lib/init/register-home-tools.d.ts +2 -0
  192. package/lib/init/register-home-tools.d.ts.map +1 -0
  193. package/lib/init/register-home-tools.js +51 -0
  194. package/lib/init/register-home-tools.js.map +1 -0
  195. package/lib/init/register-introspection-commands.d.ts +3 -0
  196. package/lib/init/register-introspection-commands.d.ts.map +1 -0
  197. package/lib/init/register-introspection-commands.js +97 -0
  198. package/lib/init/register-introspection-commands.js.map +1 -0
  199. package/lib/init/register-management-tools.d.ts +1 -1
  200. package/lib/init/register-management-tools.js +3 -3
  201. package/lib/init/register-management-tools.js.map +1 -1
  202. package/lib/init/register-mcp-memory-default.d.ts.map +1 -1
  203. package/lib/init/register-mcp-memory-default.js +1 -0
  204. package/lib/init/register-mcp-memory-default.js.map +1 -1
  205. package/lib/init/register-owner-approve-commands.d.ts.map +1 -1
  206. package/lib/init/register-owner-approve-commands.js +103 -111
  207. package/lib/init/register-owner-approve-commands.js.map +1 -1
  208. package/lib/init/register-typing-indicator.d.ts.map +1 -1
  209. package/lib/init/register-typing-indicator.js +111 -115
  210. package/lib/init/register-typing-indicator.js.map +1 -1
  211. package/lib/init.d.ts.map +1 -1
  212. package/lib/init.js +4 -0
  213. package/lib/init.js.map +1 -1
  214. package/lib/media/constants.d.ts +3 -0
  215. package/lib/media/constants.d.ts.map +1 -0
  216. package/lib/media/constants.js +3 -0
  217. package/lib/media/constants.js.map +1 -0
  218. package/lib/media/deliver-subagent-result.d.ts +15 -0
  219. package/lib/media/deliver-subagent-result.d.ts.map +1 -0
  220. package/lib/media/deliver-subagent-result.js +35 -0
  221. package/lib/media/deliver-subagent-result.js.map +1 -0
  222. package/lib/media/index.d.ts +14 -0
  223. package/lib/media/index.d.ts.map +1 -0
  224. package/lib/media/index.js +14 -0
  225. package/lib/media/index.js.map +1 -0
  226. package/lib/media/media-capabilities.d.ts +3 -0
  227. package/lib/media/media-capabilities.d.ts.map +1 -0
  228. package/lib/media/media-capabilities.js +15 -0
  229. package/lib/media/media-capabilities.js.map +1 -0
  230. package/lib/media/media-normalize.d.ts +10 -0
  231. package/lib/media/media-normalize.d.ts.map +1 -0
  232. package/lib/media/media-normalize.js +108 -0
  233. package/lib/media/media-normalize.js.map +1 -0
  234. package/lib/media/media-publisher.d.ts +8 -0
  235. package/lib/media/media-publisher.d.ts.map +1 -0
  236. package/lib/media/media-publisher.js +254 -0
  237. package/lib/media/media-publisher.js.map +1 -0
  238. package/lib/media/media-router.d.ts +7 -0
  239. package/lib/media/media-router.d.ts.map +1 -0
  240. package/lib/media/media-router.js +59 -0
  241. package/lib/media/media-router.js.map +1 -0
  242. package/lib/media/media-spool.d.ts +3 -0
  243. package/lib/media/media-spool.d.ts.map +1 -0
  244. package/lib/media/media-spool.js +25 -0
  245. package/lib/media/media-spool.js.map +1 -0
  246. package/lib/media/media-tool-bridge.d.ts +9 -0
  247. package/lib/media/media-tool-bridge.d.ts.map +1 -0
  248. package/lib/media/media-tool-bridge.js +61 -0
  249. package/lib/media/media-tool-bridge.js.map +1 -0
  250. package/lib/media/media-types.d.ts +50 -0
  251. package/lib/media/media-types.d.ts.map +1 -0
  252. package/lib/media/media-types.js +18 -0
  253. package/lib/media/media-types.js.map +1 -0
  254. package/lib/media/resolve-config.d.ts +3 -0
  255. package/lib/media/resolve-config.d.ts.map +1 -0
  256. package/lib/media/resolve-config.js +32 -0
  257. package/lib/media/resolve-config.js.map +1 -0
  258. package/lib/media/subagent-inbound.d.ts +20 -0
  259. package/lib/media/subagent-inbound.d.ts.map +1 -0
  260. package/lib/media/subagent-inbound.js +88 -0
  261. package/lib/media/subagent-inbound.js.map +1 -0
  262. package/lib/media/subagent-user-delivery.d.ts +16 -0
  263. package/lib/media/subagent-user-delivery.d.ts.map +1 -0
  264. package/lib/media/subagent-user-delivery.js +62 -0
  265. package/lib/media/subagent-user-delivery.js.map +1 -0
  266. package/lib/media/vision-capability.d.ts +5 -0
  267. package/lib/media/vision-capability.d.ts.map +1 -0
  268. package/lib/media/vision-capability.js +19 -0
  269. package/lib/media/vision-capability.js.map +1 -0
  270. package/lib/memory-layers.d.ts +57 -0
  271. package/lib/memory-layers.d.ts.map +1 -0
  272. package/lib/memory-layers.js +327 -0
  273. package/lib/memory-layers.js.map +1 -0
  274. package/lib/orchestrator/agent-dispatcher.d.ts.map +1 -1
  275. package/lib/orchestrator/agent-dispatcher.js +2 -0
  276. package/lib/orchestrator/agent-dispatcher.js.map +1 -1
  277. package/lib/orchestrator/mcp-lifecycle.d.ts +3 -0
  278. package/lib/orchestrator/mcp-lifecycle.d.ts.map +1 -1
  279. package/lib/orchestrator/mcp-lifecycle.js +34 -0
  280. package/lib/orchestrator/mcp-lifecycle.js.map +1 -1
  281. package/lib/orchestrator/resolve-subagent-tools.d.ts +20 -0
  282. package/lib/orchestrator/resolve-subagent-tools.d.ts.map +1 -0
  283. package/lib/orchestrator/resolve-subagent-tools.js +52 -0
  284. package/lib/orchestrator/resolve-subagent-tools.js.map +1 -0
  285. package/lib/orchestrator/task-queue.d.ts +4 -0
  286. package/lib/orchestrator/task-queue.d.ts.map +1 -1
  287. package/lib/orchestrator/task-queue.js +24 -0
  288. package/lib/orchestrator/task-queue.js.map +1 -1
  289. package/lib/orchestrator/tool-selection.d.ts +1 -1
  290. package/lib/orchestrator/tool-selection.d.ts.map +1 -1
  291. package/lib/orchestrator/tool-selection.js +2 -0
  292. package/lib/orchestrator/tool-selection.js.map +1 -1
  293. package/lib/routing/route-matcher.d.ts +12 -0
  294. package/lib/routing/route-matcher.d.ts.map +1 -0
  295. package/lib/routing/route-matcher.js +72 -0
  296. package/lib/routing/route-matcher.js.map +1 -0
  297. package/lib/routing/runtime-binding.d.ts +10 -0
  298. package/lib/routing/runtime-binding.d.ts.map +1 -0
  299. package/lib/routing/runtime-binding.js +6 -0
  300. package/lib/routing/runtime-binding.js.map +1 -0
  301. package/lib/routing/subagent-summarize.d.ts +6 -0
  302. package/lib/routing/subagent-summarize.d.ts.map +1 -0
  303. package/lib/routing/subagent-summarize.js +23 -0
  304. package/lib/routing/subagent-summarize.js.map +1 -0
  305. package/lib/security/file-policy.d.ts +7 -1
  306. package/lib/security/file-policy.d.ts.map +1 -1
  307. package/lib/security/file-policy.js +34 -1
  308. package/lib/security/file-policy.js.map +1 -1
  309. package/lib/service.d.ts +44 -25
  310. package/lib/service.d.ts.map +1 -1
  311. package/lib/service.js +93 -56
  312. package/lib/service.js.map +1 -1
  313. package/lib/subagent-ai-events.d.ts +30 -0
  314. package/lib/subagent-ai-events.d.ts.map +1 -0
  315. package/lib/subagent-ai-events.js +110 -0
  316. package/lib/subagent-ai-events.js.map +1 -0
  317. package/lib/subagent-goal-notify.d.ts +27 -6
  318. package/lib/subagent-goal-notify.d.ts.map +1 -1
  319. package/lib/subagent-goal-notify.js +46 -12
  320. package/lib/subagent-goal-notify.js.map +1 -1
  321. package/lib/subagent.d.ts +43 -3
  322. package/lib/subagent.d.ts.map +1 -1
  323. package/lib/subagent.js +191 -88
  324. package/lib/subagent.js.map +1 -1
  325. package/lib/task-executor.d.ts +10 -18
  326. package/lib/task-executor.d.ts.map +1 -1
  327. package/lib/task-executor.js +11 -34
  328. package/lib/task-executor.js.map +1 -1
  329. package/lib/typing-indicator/adapter-integration.d.ts.map +1 -1
  330. package/lib/typing-indicator/adapter-integration.js +2 -1
  331. package/lib/typing-indicator/adapter-integration.js.map +1 -1
  332. package/lib/typing-indicator/index.d.ts.map +1 -1
  333. package/lib/typing-indicator/index.js +18 -5
  334. package/lib/typing-indicator/index.js.map +1 -1
  335. package/lib/zhin-agent/agent-loop-standalone.d.ts +32 -0
  336. package/lib/zhin-agent/agent-loop-standalone.d.ts.map +1 -0
  337. package/lib/zhin-agent/agent-loop-standalone.js +144 -0
  338. package/lib/zhin-agent/agent-loop-standalone.js.map +1 -0
  339. package/lib/zhin-agent/agent-loop-turn.d.ts +51 -0
  340. package/lib/zhin-agent/agent-loop-turn.d.ts.map +1 -0
  341. package/lib/zhin-agent/agent-loop-turn.js +287 -0
  342. package/lib/zhin-agent/agent-loop-turn.js.map +1 -0
  343. package/lib/zhin-agent/builtin-tools.d.ts +3 -2
  344. package/lib/zhin-agent/builtin-tools.d.ts.map +1 -1
  345. package/lib/zhin-agent/builtin-tools.js +15 -16
  346. package/lib/zhin-agent/builtin-tools.js.map +1 -1
  347. package/lib/zhin-agent/config.d.ts +20 -14
  348. package/lib/zhin-agent/config.d.ts.map +1 -1
  349. package/lib/zhin-agent/config.js +19 -13
  350. package/lib/zhin-agent/config.js.map +1 -1
  351. package/lib/zhin-agent/deferred-delivery.d.ts +5 -0
  352. package/lib/zhin-agent/deferred-delivery.d.ts.map +1 -0
  353. package/lib/zhin-agent/deferred-delivery.js +29 -0
  354. package/lib/zhin-agent/deferred-delivery.js.map +1 -0
  355. package/lib/zhin-agent/event-emitter.d.ts.map +1 -1
  356. package/lib/zhin-agent/event-emitter.js +3 -2
  357. package/lib/zhin-agent/event-emitter.js.map +1 -1
  358. package/lib/zhin-agent/index.d.ts +50 -8
  359. package/lib/zhin-agent/index.d.ts.map +1 -1
  360. package/lib/zhin-agent/index.js +204 -17
  361. package/lib/zhin-agent/index.js.map +1 -1
  362. package/lib/zhin-agent/model-resolver.d.ts +0 -1
  363. package/lib/zhin-agent/model-resolver.d.ts.map +1 -1
  364. package/lib/zhin-agent/model-resolver.js.map +1 -1
  365. package/lib/zhin-agent/multimodal-message.d.ts +9 -0
  366. package/lib/zhin-agent/multimodal-message.d.ts.map +1 -0
  367. package/lib/zhin-agent/multimodal-message.js +69 -0
  368. package/lib/zhin-agent/multimodal-message.js.map +1 -0
  369. package/lib/zhin-agent/prompt-access.d.ts +4 -0
  370. package/lib/zhin-agent/prompt-access.d.ts.map +1 -0
  371. package/lib/zhin-agent/prompt-access.js +7 -0
  372. package/lib/zhin-agent/prompt-access.js.map +1 -0
  373. package/lib/zhin-agent/prompt-assembly.d.ts.map +1 -1
  374. package/lib/zhin-agent/prompt-assembly.js +0 -2
  375. package/lib/zhin-agent/prompt-assembly.js.map +1 -1
  376. package/lib/zhin-agent/prompt-controller.d.ts +53 -0
  377. package/lib/zhin-agent/prompt-controller.d.ts.map +1 -0
  378. package/lib/zhin-agent/prompt-controller.js +165 -0
  379. package/lib/zhin-agent/prompt-controller.js.map +1 -0
  380. package/lib/zhin-agent/prompt-input.d.ts +3 -0
  381. package/lib/zhin-agent/prompt-input.d.ts.map +1 -0
  382. package/lib/zhin-agent/prompt-input.js +8 -0
  383. package/lib/zhin-agent/prompt-input.js.map +1 -0
  384. package/lib/zhin-agent/prompt.d.ts.map +1 -1
  385. package/lib/zhin-agent/prompt.js +49 -16
  386. package/lib/zhin-agent/prompt.js.map +1 -1
  387. package/lib/zhin-agent/session-io.d.ts +12 -7
  388. package/lib/zhin-agent/session-io.d.ts.map +1 -1
  389. package/lib/zhin-agent/session-io.js +18 -15
  390. package/lib/zhin-agent/session-io.js.map +1 -1
  391. package/lib/zhin-agent/session-message-queue.d.ts +20 -0
  392. package/lib/zhin-agent/session-message-queue.d.ts.map +1 -0
  393. package/lib/zhin-agent/session-message-queue.js +52 -0
  394. package/lib/zhin-agent/session-message-queue.js.map +1 -0
  395. package/lib/zhin-agent/tool-calls-user-format.d.ts.map +1 -1
  396. package/lib/zhin-agent/tool-calls-user-format.js +8 -1
  397. package/lib/zhin-agent/tool-calls-user-format.js.map +1 -1
  398. package/lib/zhin-agent/tool-orchestration.d.ts.map +1 -1
  399. package/lib/zhin-agent/tool-orchestration.js +43 -10
  400. package/lib/zhin-agent/tool-orchestration.js.map +1 -1
  401. package/lib/zhin-agent/tool-runtime.d.ts +2 -4
  402. package/lib/zhin-agent/tool-runtime.d.ts.map +1 -1
  403. package/lib/zhin-agent/tool-runtime.js +4 -7
  404. package/lib/zhin-agent/tool-runtime.js.map +1 -1
  405. package/lib/zhin-agent/tool-search-orchestrator.d.ts +3 -0
  406. package/lib/zhin-agent/tool-search-orchestrator.d.ts.map +1 -1
  407. package/lib/zhin-agent/tool-search-orchestrator.js +8 -3
  408. package/lib/zhin-agent/tool-search-orchestrator.js.map +1 -1
  409. package/lib/zhin-agent/turn-context.d.ts +9 -0
  410. package/lib/zhin-agent/turn-context.d.ts.map +1 -0
  411. package/lib/zhin-agent/turn-context.js +12 -0
  412. package/lib/zhin-agent/turn-context.js.map +1 -0
  413. package/lib/zhin-agent/turn-pipeline.d.ts +5 -1
  414. package/lib/zhin-agent/turn-pipeline.d.ts.map +1 -1
  415. package/lib/zhin-agent/turn-pipeline.js +128 -401
  416. package/lib/zhin-agent/turn-pipeline.js.map +1 -1
  417. package/lib/zhin-agent/turn-tracker.d.ts +2 -0
  418. package/lib/zhin-agent/turn-tracker.d.ts.map +1 -1
  419. package/lib/zhin-agent/turn-tracker.js +11 -6
  420. package/lib/zhin-agent/turn-tracker.js.map +1 -1
  421. package/lib/zhin-agent/zhin-agent-private.d.ts +18 -5
  422. package/lib/zhin-agent/zhin-agent-private.d.ts.map +1 -1
  423. package/lib/zhin-agent/zhin-agent-private.js.map +1 -1
  424. package/package.json +3 -3
  425. package/lib/zhin-agent/llm-runner.d.ts +0 -16
  426. package/lib/zhin-agent/llm-runner.d.ts.map +0 -1
  427. package/lib/zhin-agent/llm-runner.js +0 -85
  428. package/lib/zhin-agent/llm-runner.js.map +0 -1
@@ -1,34 +1,32 @@
1
- import { getPlugin } from '@zhin.js/core';
2
- import { formatCompact, truncatePreview, Logger } from '@zhin.js/logger';
3
- import { createAgent } from '@zhin.js/ai';
1
+ import { formatCompact, Logger } from '@zhin.js/logger';
2
+ import { createUserMessage } from '@zhin.js/ai';
4
3
  import { resolveIMSessionIdFromToolContext } from '@zhin.js/ai';
5
4
  import { parseOutput } from '@zhin.js/ai';
6
5
  import { detectTone } from '@zhin.js/ai';
7
- import { ensureMcpConnections } from '../orchestrator/mcp-lifecycle.js';
8
- import { applyExecPolicyToTools } from '../security/exec-policy.js';
9
- import { runWithBashToolContext } from '../security/bash-tool-context.js';
6
+ import { ensureMcpConnectionsForBinding, getMcpToolsForBinding, } from '../orchestrator/mcp-lifecycle.js';
10
7
  import { triggerAIHook, createAIHookEvent } from '../hooks.js';
11
8
  import { resolveModelCandidates } from './model-resolver.js';
12
- import { streamChatWithHistory } from './llm-runner.js';
13
- import { buildSessionCreateInput, touchSession, buildHistoryMessages, formatUserContentForSession, resolveSessionIsNewBeforeCreate, } from './session-io.js';
9
+ import { mergeToolOutboundElements } from '../media/media-tool-bridge.js';
10
+ import { providerSupportsVision } from '../media/vision-capability.js';
11
+ import { touchSession, formatUserContentForSession, resolveSessionIsNewBeforeCreate, beginTurnSession as beginTurnSessionIO, } from './session-io.js';
12
+ import { runAgentLoopTextTurn, runAgentLoopVisionTurn } from './agent-loop-turn.js';
14
13
  import { buildEnhancedPersona } from './prompt.js';
15
- import { collectRuntimeTools, planToolRun } from './tool-runtime.js';
16
- import { sanitizeAssistantReply, stripThinkBlocks } from './text-sanitize.js';
17
- import { pruneHistoryWithBudget } from './context-budget.js';
14
+ import { collectRuntimeTools } from './tool-runtime.js';
18
15
  import { buildMultimodalVisionSystemPrompt } from './prompt-assembly.js';
19
- import { formatToolCallsForUser } from './tool-calls-user-format.js';
20
- import { resolveModelHarness } from './model-harness.js';
21
- import { RESERVED_TOOL_NAMES, RESERVED_TOOL_NAME_PREFIXES } from '../reserved-tools.js';
22
- import { createOwnerOrchestratedToolResultTransform } from '../orchestrator/owner-confirm-orchestration.js';
23
- import { logPhase, usageLogFields } from './phase-trace.js';
24
16
  import { attachWebSearchLocale } from './web-search-locale-attach.js';
25
17
  import { EMPTY_USAGE } from './turn-metrics.js';
26
18
  import { resolveAgentToolsForTurn } from './tool-orchestration.js';
27
- import { buildAgentPathSystemPrompt, buildChatPathSystemPrompt, buildFastPathSystemPrompt, buildAgentUserMessage, } from './prompt-assembly.js';
19
+ import { logPhase } from './phase-trace.js';
20
+ import { buildVisionUserMessage, summarizeMultimodalParts } from './multimodal-message.js';
21
+ import { DEFAULT_MULTIMODAL_CONFIG } from '../media/media-types.js';
28
22
  const logger = new Logger(null, 'ZhinAgent');
29
23
  const now = () => performance.now();
30
24
  function sessionDeps(host) {
31
- return { chatHistory: host.chatHistory, imSessionStore: host.imSessionStore };
25
+ return {
26
+ imSessionStore: host.imSessionStore,
27
+ agentSessionStore: host.agentSessionStore,
28
+ contextRepository: host.contextRepository,
29
+ };
32
30
  }
33
31
  async function beginTurnSession(host, context) {
34
32
  const sessionKey = resolveIMSessionIdFromToolContext({
@@ -38,22 +36,21 @@ async function beginTurnSession(host, context) {
38
36
  sceneId: context.sceneId,
39
37
  senderId: context.senderId,
40
38
  });
41
- const imSession = await host.imSessionStore.getOrCreateActive(buildSessionCreateInput(sessionKey, context));
42
- return { sessionKey, sessionId: imSession.session_id };
39
+ return beginTurnSessionIO(sessionDeps(host), sessionKey, context);
43
40
  }
44
- export async function processTextTurn(host, content, context, externalTools = [], onChunk) {
41
+ export async function processTextTurn(host, content, context, externalTools = [], onChunk, extras) {
45
42
  const t0 = now();
46
- const { senderId, sceneId, platform, botId, messageId } = context;
43
+ const { senderId, platform, botId } = context;
47
44
  const sessionUserContent = formatUserContentForSession(context, content);
48
45
  const userId = senderId || 'unknown';
49
46
  const sessionKey = resolveIMSessionIdFromToolContext({
50
47
  platform,
51
48
  botId,
52
49
  scope: context.scope,
53
- sceneId,
50
+ sceneId: context.sceneId,
54
51
  senderId,
55
52
  });
56
- const isNewSession = await resolveSessionIsNewBeforeCreate(sessionDeps(host), sessionKey, context);
53
+ const isNewSession = await resolveSessionIsNewBeforeCreate(sessionDeps(host), sessionKey);
57
54
  await host.waitForMemoryPersistence();
58
55
  const { sessionId } = await beginTurnSession(host, context);
59
56
  await host.emitter.dispatch('ai.processing.start', host.emitter.createPayload(sessionId, context, 'text', {
@@ -61,9 +58,8 @@ export async function processTextTurn(host, content, context, externalTools = []
61
58
  }));
62
59
  logPhase(host.phaseConfig, 'turn.start', sessionId, {
63
60
  mode: 'text',
64
- provider: host.provider.name,
61
+ provider: host.getTurnProvider().name,
65
62
  });
66
- // 0. Rate limit
67
63
  const rateCheck = host.rateLimiter.check(userId);
68
64
  if (!rateCheck.allowed) {
69
65
  logPhase(host.phaseConfig, 'turn.rate_limited', sessionId, { userId });
@@ -76,21 +72,20 @@ export async function processTextTurn(host, content, context, externalTools = []
76
72
  await host.finalizeActiveTurn({ usage: EMPTY_USAGE, path: 'rate_limited' });
77
73
  return parseOutput(rateCheck.message || '请稍后再试');
78
74
  }
79
- // 0.1 发射 typing 事件,由适配器插件自行决定如何响应
80
75
  host.emitter.emit('ai.typing.start', host.emitter.createPayload(sessionId, context, 'text', {
81
76
  reason: 'processing',
82
77
  }));
83
78
  host.beginActiveTurn();
84
- // 0.5 工具上下文:web_search 语言(档案 preferred_language / language,否则默认中文)
85
79
  const contextForTools = await attachWebSearchLocale(context, userId, host.userProfiles);
86
80
  triggerAIHook(createAIHookEvent('message', 'received', sessionId, {
87
81
  userId,
88
82
  content,
89
83
  platform: platform || '',
90
84
  })).catch(() => { });
91
- // 0.9 Lazy-connect configured MCP servers before tool collection
92
- if (host.orchestrator) {
93
- await ensureMcpConnections(host.orchestrator.mcps, (event) => {
85
+ const turnBinding = host.activeBinding;
86
+ const mcpServerNames = turnBinding?.mcpServers ?? [];
87
+ if (host.orchestrator && mcpServerNames.length > 0) {
88
+ await ensureMcpConnectionsForBinding(host.orchestrator.mcps, mcpServerNames, (event) => {
94
89
  const payload = host.emitter.createPayload(sessionId, contextForTools, 'text', {
95
90
  path: 'agent',
96
91
  serverName: event.serverName,
@@ -109,9 +104,10 @@ export async function processTextTurn(host, content, context, externalTools = []
109
104
  }
110
105
  });
111
106
  }
112
- // 1. Collect tools
113
107
  const tFilter = now();
114
- const mcpTools = host.orchestrator?.mcps.getAllMcpTools() ?? [];
108
+ const mcpTools = host.orchestrator && mcpServerNames.length > 0
109
+ ? getMcpToolsForBinding(host.orchestrator.mcps, mcpServerNames)
110
+ : [];
115
111
  const allTools = collectRuntimeTools({
116
112
  content,
117
113
  context: contextForTools,
@@ -121,9 +117,8 @@ export async function processTextTurn(host, content, context, externalTools = []
121
117
  externalRegistered: host.externalTools,
122
118
  sessionId,
123
119
  userId,
124
- chatHistory: host.chatHistory,
120
+ imTranscriptStore: host.imTranscriptStore,
125
121
  userProfiles: host.userProfiles,
126
- subagentManager: host.subagentManager,
127
122
  mcpTools,
128
123
  });
129
124
  const { tools: resolvedTools, deferredStats } = resolveAgentToolsForTurn(host, allTools, contextForTools);
@@ -132,289 +127,94 @@ export async function processTextTurn(host, content, context, externalTools = []
132
127
  logPhase(host.phaseConfig, 'tools.collected', sessionId, { count: resolvedTools.length });
133
128
  logger.debug(formatCompact({
134
129
  tools: resolvedTools.length,
135
- tool_search: host.config.toolSearch || undefined,
130
+ tool_search: true,
136
131
  names: resolvedTools.map(t => t.name).join(',') || '(none)',
137
132
  }));
138
- // 2. History + profile (parallel)
139
- const tMem = now();
140
- const [rawHistoryMessages, profileSummary] = await Promise.all([
141
- buildHistoryMessages(sessionDeps(host), sessionId, context, sessionUserContent),
142
- host.userProfiles.buildProfileSummary(userId),
143
- ]);
144
- const chatCandidates = resolveModelCandidates(host.provider.models, host.modelRegistry, host.provider.name, host.config, 'chat');
145
- const { messages: historyMessages, result: pruneResult, budget: contextBudget, } = pruneHistoryWithBudget({
146
- messages: rawHistoryMessages,
147
- config: host.config,
148
- provider: host.provider,
149
- modelRegistry: host.modelRegistry,
150
- model: chatCandidates[0],
151
- });
152
- if (pruneResult.droppedCount > 0) {
153
- logger.debug(`[上下文窗口] 丢弃 ${pruneResult.droppedCount} 条历史消息 (${pruneResult.droppedTokens} tokens)`);
154
- }
155
- const memMs = (now() - tMem).toFixed(0);
156
- logPhase(host.phaseConfig, 'context.ready', sessionId, { historyCount: historyMessages.length });
157
- // 2.5 Tone + persona
133
+ const profileSummary = await host.userProfiles.buildProfileSummary(userId);
158
134
  const toneHint = host.config.toneAwareness ? detectTone(content).hint : '';
159
135
  const personaEnhanced = buildEnhancedPersona(host.config, profileSummary, toneHint);
160
- // 3. No tools chat path (prefer per-session model, then lightweight model)
161
- if (allTools.length === 0) {
162
- logPhase(host.phaseConfig, 'path.chat', sessionId, { toolCount: 0 });
163
- const liteModel = host.config.chatLiteModel || undefined;
164
- const chatSystemPrompt = buildChatPathSystemPrompt(host, personaEnhanced, contextForTools);
165
- logger.debug(formatCompact({
166
- mode: 'chat',
167
- prompt_chars: chatSystemPrompt.length,
168
- model: liteModel || chatCandidates[0] || undefined,
169
- }));
170
- const tLLM = now();
171
- logPhase(host.phaseConfig, 'chat.llm.start', sessionId, { model: liteModel || chatCandidates[0] || '' });
172
- const chatResult = await streamChatWithHistory({ provider: host.provider, modelRegistry: host.modelRegistry, config: host.config }, sessionUserContent, chatSystemPrompt, historyMessages, onChunk, liteModel);
173
- let reply = sanitizeAssistantReply(chatResult.content);
174
- await host.emitter.dispatch('ai.response', host.emitter.createPayload(sessionId, context, 'text', {
175
- path: 'chat',
176
- model: chatResult.model,
177
- reply,
178
- }));
179
- const llmMs = (now() - tLLM).toFixed(0);
180
- logPhase(host.phaseConfig, 'chat.llm.end', sessionId, {
181
- durationMs: Number(llmMs),
182
- ...usageLogFields(chatResult.usage ?? undefined),
183
- });
184
- logger.debug(formatCompact({
185
- mode: 'chat',
186
- filter_ms: filterMs,
187
- mem_ms: memMs,
188
- llm_ms: llmMs,
189
- total_ms: Math.round(now() - t0),
190
- }));
191
- await touchSession(sessionDeps(host), sessionId);
192
- if (isNewSession) {
193
- host.emitSessionNewEvent(sessionId, context, 'text', sessionUserContent, reply);
194
- }
195
- await host.finalizeActiveTurn({
196
- usage: chatResult.usage ?? EMPTY_USAGE,
197
- path: 'chat',
198
- model: chatResult.model,
199
- });
200
- await host.emitter.dispatch('ai.processing.finish', host.emitter.createPayload(sessionId, context, 'text', {
201
- path: 'chat',
202
- model: chatResult.model,
203
- reply,
204
- }));
205
- host.emitter.emit('ai.typing.stop', host.emitter.createPayload(sessionId, context, 'text', {
206
- reason: 'processing_complete',
207
- }));
208
- logPhase(host.phaseConfig, 'turn.end', sessionId, { path: 'chat' });
209
- return parseOutput(reply);
210
- }
211
- logger.debug(`[工具路径] 过滤=${filterMs}ms, 记忆=${memMs}ms, ${allTools.length} 工具 (${allTools.map(t => t.name).join(', ')})`);
212
- // 4. Pre-executable tools
213
- const preExecCandidates = allTools.filter(tool => tool.preExecutable);
214
- const tPre = preExecCandidates.length > 0 ? now() : 0;
215
- if (preExecCandidates.length > 0) {
216
- logger.debug(`预执行: ${preExecCandidates.map(t => t.name).join(', ')}`);
217
- }
218
- const toolRun = await planToolRun(resolvedTools, host.config.preExecTimeout);
219
- logPhase(host.phaseConfig, 'preexec.done', sessionId, {
220
- mode: toolRun.mode,
221
- preExecutedTools: toolRun.preExecution.tools.length,
222
- });
223
- const preData = toolRun.preExecution.data;
224
- if (tPre > 0) {
225
- logger.debug(`预执行耗时: ${(now() - tPre).toFixed(0)}ms`);
226
- }
227
- // 6. Path selection
228
- let reply;
229
- if (toolRun.mode === 'pre-exec-fast-path') {
230
- logPhase(host.phaseConfig, 'path.pre_exec_fast', sessionId, { toolCount: allTools.length });
231
- // Fast path
232
- const tLLM = now();
233
- const prompt = buildFastPathSystemPrompt(host, personaEnhanced, preData, contextForTools);
234
- logger.debug(formatCompact({ mode: 'fast', prompt_chars: prompt.length }));
235
- logPhase(host.phaseConfig, 'fast.llm.start', sessionId, { model: chatCandidates[0] || '' });
236
- const fastResult = await streamChatWithHistory({ provider: host.provider, modelRegistry: host.modelRegistry, config: host.config }, sessionUserContent, prompt, historyMessages, onChunk);
237
- reply = sanitizeAssistantReply(fastResult.content);
238
- logPhase(host.phaseConfig, 'fast.llm.end', sessionId, {
239
- durationMs: Math.round(now() - tLLM),
240
- ...usageLogFields(fastResult.usage ?? undefined),
241
- });
242
- logger.debug(`[快速路径] 过滤=${filterMs}ms, 记忆=${memMs}ms, LLM=${(now() - tLLM).toFixed(0)}ms, 总=${(now() - t0).toFixed(0)}ms`);
243
- await host.finalizeActiveTurn({
244
- usage: fastResult.usage ?? EMPTY_USAGE,
245
- path: 'fast',
246
- model: fastResult.model,
247
- });
248
- }
249
- else {
250
- logPhase(host.phaseConfig, 'path.agent', sessionId, { toolCount: allTools.length });
251
- const tAgent = now();
252
- logger.debug(`Agent 路径: ${allTools.length} 个工具`);
253
- const systemPrompt = await buildAgentPathSystemPrompt(host, {
254
- content,
255
- context: contextForTools,
136
+ const chatCandidates = resolveModelCandidates(host.getTurnProvider().models, host.modelRegistry, host.getTurnProvider().name, host.config, 'chat');
137
+ logPhase(host.phaseConfig, 'path.agent_loop', sessionId, { toolCount: allTools.length });
138
+ const userMessages = extras?.prebuiltMessages?.length
139
+ ? extras.prebuiltMessages
140
+ : [createUserMessage(sessionUserContent)];
141
+ const loopResult = await host.promptController.schedule({
142
+ sessionKey,
143
+ sessionId,
144
+ userMessages,
145
+ context,
146
+ onChunk,
147
+ execute: (initialMessages, hooks, signal, _turnId) => runAgentLoopTextTurn({
148
+ host,
256
149
  sessionId,
150
+ sessionUserContent,
151
+ rawContent: content,
152
+ context,
153
+ contextForTools,
154
+ allTools,
155
+ resolvedTools,
257
156
  personaEnhanced,
258
- preData,
157
+ modelId: chatCandidates[0] || host.getTurnProvider().models[0] || 'gpt-4o-mini',
158
+ modelCandidates: chatCandidates,
159
+ onChunk,
160
+ initialMessages,
161
+ promptHooks: hooks,
162
+ signal,
259
163
  deferredStats,
260
- });
261
- logger.debug(formatCompact({ mode: 'agent', prompt_chars: systemPrompt.length }));
262
- logger.debug(`[System Prompt Full]\n${systemPrompt}\n---END---`);
263
- const agentTools = applyExecPolicyToTools(host.config, resolvedTools, {
264
- approvalMode: host.config.execApprovalMode,
265
- });
266
- // Adaptive maxIterations: boost when skills are active (multi-step skill flows)
267
- const SKILL_ITERATION_BOOST = 3;
268
- const hasSkillActivation = agentTools.some(t => t.name === 'activate_skill' || t.name === 'install_skill');
269
- const harness = resolveModelHarness(host.provider.name, chatCandidates[0] || '', host.config.modelHarness);
270
- const baseIterations = harness.maxIterations ?? host.config.maxIterations;
271
- const effectiveMaxIterations = hasSkillActivation
272
- ? baseIterations + SKILL_ITERATION_BOOST
273
- : baseIterations;
274
- logPhase(host.phaseConfig, 'harness.resolved', sessionId, {
275
- model: chatCandidates[0] || '',
276
- harnessMaxIterations: harness.maxIterations ?? null,
277
- effectiveMaxIterations,
278
- });
279
- let orchestrationPlugin = host.emitter.getHostPlugin() ?? undefined;
280
- if (!orchestrationPlugin) {
281
- try {
282
- orchestrationPlugin = getPlugin().root ?? getPlugin();
283
- }
284
- catch {
285
- logger.warn(formatCompact({ warn: 'no_host_plugin' }));
286
- }
287
- }
288
- const llmAgent = createAgent(host.provider, {
289
- model: chatCandidates[0],
290
- modelFallbacks: chatCandidates.slice(1),
291
- systemPrompt,
292
- tools: agentTools,
293
- maxIterations: effectiveMaxIterations,
294
- turnTimeout: host.config.timeout,
295
- contextWindow: contextBudget.contextWindow,
296
- reservedToolNames: RESERVED_TOOL_NAMES,
297
- reservedToolNamePrefixes: RESERVED_TOOL_NAME_PREFIXES,
298
- transformToolResult: createOwnerOrchestratedToolResultTransform({
299
- toolContext: contextForTools,
300
- disableHardOrchestration: false,
301
- plugin: orchestrationPlugin,
302
- }),
303
- policyDenialStopAfter: host.config.policyDenialStopAfter,
304
- });
305
- llmAgent.on('thinking', (message) => {
306
- host.emitter.emit('ai.thinking', host.emitter.createPayload(sessionId, contextForTools, 'text', {
307
- path: 'agent',
308
- thinking: message,
309
- }));
310
- });
311
- llmAgent.on('tool_call', (toolName, args) => {
312
- host.emitter.emit('ai.tool.call', host.emitter.createPayload(sessionId, contextForTools, 'text', {
313
- path: 'agent',
314
- toolName,
315
- args,
316
- }));
317
- });
318
- llmAgent.on('tool_result', (toolName, result) => {
319
- host.emitter.emit('ai.tool.result', host.emitter.createPayload(sessionId, contextForTools, 'text', {
320
- path: 'agent',
321
- toolName,
322
- result,
323
- }));
324
- });
325
- llmAgent.on('compaction', (info) => {
326
- host.emitSessionCompactEvent(sessionId, contextForTools, 'text', info);
327
- });
328
- const userMessageWithHistory = buildAgentUserMessage(historyMessages, sessionUserContent);
329
- let result;
330
- try {
331
- await host.emitter.dispatch('ai.agent.start', host.emitter.createPayload(sessionId, contextForTools, 'text', {
332
- path: 'agent',
333
- model: chatCandidates[0] || undefined,
334
- }));
335
- logPhase(host.phaseConfig, 'agent.run.start', sessionId, { model: chatCandidates[0] || '' });
336
- result = await runWithBashToolContext(contextForTools, () => llmAgent.run(userMessageWithHistory, []));
337
- logPhase(host.phaseConfig, 'agent.run.end', sessionId, {
338
- iterations: result.iterations,
339
- durationMs: Math.round(now() - tAgent),
340
- ...usageLogFields(result.usage),
341
- });
342
- await host.emitter.dispatch('ai.agent.finish', host.emitter.createPayload(sessionId, contextForTools, 'text', {
343
- path: 'agent',
344
- model: result.model ?? (chatCandidates[0] || undefined),
345
- iterations: result.iterations,
346
- }));
347
- }
348
- catch (error) {
349
- await host.emitter.dispatch('ai.processing.error', host.emitter.createPayload(sessionId, contextForTools, 'text', {
350
- path: 'agent',
351
- error: error instanceof Error ? error.message : String(error),
352
- }));
353
- throw error;
354
- }
355
- finally {
356
- llmAgent.dispose();
357
- }
358
- reply = sanitizeAssistantReply(result.content, {
359
- toolSummary: formatToolCallsForUser(result.toolCalls),
360
- });
361
- await host.emitter.dispatch('ai.response', host.emitter.createPayload(sessionId, contextForTools, 'text', {
362
- path: 'agent',
363
- model: result.model ?? (chatCandidates[0] || undefined),
364
- iterations: result.iterations,
365
- reply,
366
- }));
367
- logger.debug(formatCompact({
368
- agent_answer: truncatePreview(reply, 480),
369
- tool_calls: result.toolCalls.length,
370
- ...(result.toolCalls.length
371
- ? { tools: result.toolCalls.map(tc => tc.tool).join(',') }
372
- : {}),
373
- }));
374
- for (const tc of result.toolCalls) {
375
- logger.debug(formatCompact({
376
- tool_result: tc.tool,
377
- preview: truncatePreview(typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result), 480),
378
- }));
379
- }
380
- logger.debug(`[Agent 路径] 过滤=${filterMs}ms, 记忆=${memMs}ms, Agent=${(now() - tAgent).toFixed(0)}ms, 总=${(now() - t0).toFixed(0)}ms`);
381
- await host.finalizeActiveTurn({
382
- usage: result.usage,
383
- path: 'agent',
384
- iterations: result.iterations,
385
- model: result.model ?? (chatCandidates[0] || undefined),
386
- });
387
- }
164
+ }),
165
+ });
166
+ const reply = loopResult.reply;
167
+ await host.emitter.dispatch('ai.response', host.emitter.createPayload(sessionId, context, 'text', {
168
+ path: loopResult.path,
169
+ model: loopResult.model,
170
+ iterations: loopResult.iterations,
171
+ reply,
172
+ }));
388
173
  await touchSession(sessionDeps(host), sessionId);
389
174
  if (isNewSession) {
390
175
  host.emitSessionNewEvent(sessionId, context, 'text', sessionUserContent, reply);
391
176
  }
177
+ await host.finalizeActiveTurn({
178
+ usage: loopResult.usage,
179
+ path: loopResult.path,
180
+ iterations: loopResult.iterations,
181
+ model: loopResult.model,
182
+ });
392
183
  triggerAIHook(createAIHookEvent('message', 'sent', sessionId, {
393
184
  userId,
394
185
  content: reply,
395
186
  platform: platform || '',
396
187
  })).catch(() => { });
397
188
  await host.emitter.dispatch('ai.processing.finish', host.emitter.createPayload(sessionId, context, 'text', {
398
- path: toolRun.mode === 'pre-exec-fast-path' ? 'fast' : 'agent',
189
+ path: loopResult.path,
190
+ model: loopResult.model,
399
191
  reply,
400
192
  }));
401
- logPhase(host.phaseConfig, 'turn.end', sessionId, { path: toolRun.mode === 'pre-exec-fast-path' ? 'fast' : 'agent' });
402
193
  host.emitter.emit('ai.typing.stop', host.emitter.createPayload(sessionId, context, 'text', {
403
194
  reason: 'processing_complete',
404
195
  }));
405
- return parseOutput(reply);
196
+ logPhase(host.phaseConfig, 'turn.end', sessionId, {
197
+ path: loopResult.path,
198
+ filter_ms: filterMs,
199
+ total_ms: Math.round(now() - t0),
200
+ });
201
+ const outbound = mergeToolOutboundElements(parseOutput(reply), loopResult.toolCalls);
202
+ if (!reply.trim() && outbound.length === 0)
203
+ return [];
204
+ return outbound;
406
205
  }
206
+ /** @deprecated 仅保留类型引用;多模态 turn 已统一 agentLoop */
407
207
  export async function processMultimodalTurn(host, parts, context, onChunk) {
408
- const { senderId, sceneId, platform, botId } = context;
208
+ const { senderId, platform, botId } = context;
409
209
  const userId = senderId || 'unknown';
410
210
  const sessionKey = resolveIMSessionIdFromToolContext({
411
211
  platform,
412
212
  botId,
413
213
  scope: context.scope,
414
- sceneId,
214
+ sceneId: context.sceneId,
415
215
  senderId,
416
216
  });
417
- const isNewSession = await resolveSessionIsNewBeforeCreate(sessionDeps(host), sessionKey, context);
217
+ const isNewSession = await resolveSessionIsNewBeforeCreate(sessionDeps(host), sessionKey);
418
218
  await host.waitForMemoryPersistence();
419
219
  const { sessionId } = await beginTurnSession(host, context);
420
220
  await host.emitter.dispatch('ai.processing.start', host.emitter.createPayload(sessionId, context, 'multimodal'));
@@ -432,129 +232,55 @@ export async function processMultimodalTurn(host, parts, context, onChunk) {
432
232
  reason: 'processing',
433
233
  }));
434
234
  host.beginActiveTurn();
435
- const textFragmentsEarly = [];
436
- for (const p of parts) {
437
- if (p.type === 'text')
438
- textFragmentsEarly.push(p.text);
439
- }
440
- const textContentEarly = textFragmentsEarly.join(' ') || '[多模态消息]';
441
- const sessionUserContentEarly = formatUserContentForSession(context, textContentEarly);
442
- const rawHistoryMessages = await buildHistoryMessages(sessionDeps(host), sessionId, context, sessionUserContentEarly);
235
+ const supportsVision = providerSupportsVision(host.getTurnProvider());
236
+ const textContent = summarizeMultimodalParts(parts, supportsVision);
237
+ const sessionUserContent = formatUserContentForSession(context, textContent);
443
238
  const profileSummary = await host.userProfiles.buildProfileSummary(userId);
444
239
  const personaEnhanced = host.buildDisciplinedPrompt(buildEnhancedPersona(host.config, profileSummary, ''));
445
- const textFragments = [];
446
- const llmParts = [];
447
- for (const p of parts) {
448
- switch (p.type) {
449
- case 'text':
450
- textFragments.push(p.text);
451
- llmParts.push(p);
452
- break;
453
- case 'image_url':
454
- textFragments.push('[图片]');
455
- llmParts.push(p);
456
- break;
457
- case 'video_url':
458
- textFragments.push('[视频]');
459
- llmParts.push({ type: 'text', text: `[用户发送了一个视频: ${p.video_url.url}]` });
460
- break;
461
- case 'audio':
462
- textFragments.push('[音频]');
463
- llmParts.push(p);
464
- break;
465
- case 'face':
466
- textFragments.push(p.face.text || `[表情:${p.face.id}]`);
467
- llmParts.push({
468
- type: 'text',
469
- text: p.face.text ? `[表情: ${p.face.text}]` : `[表情ID: ${p.face.id}]`,
470
- });
471
- break;
472
- }
473
- }
474
- const textContent = textFragments.join(' ') || '[多模态消息]';
475
- const sessionUserContent = formatUserContentForSession(context, textContent);
476
240
  const visionSystemPrompt = await buildMultimodalVisionSystemPrompt(host, {
477
241
  context,
478
242
  sessionId,
479
243
  textContent,
480
244
  personaEnhanced,
481
245
  });
482
- const visionCandidates = resolveModelCandidates(host.provider.models, host.modelRegistry, host.provider.name, host.config, 'vision');
483
- const { messages: historyMessages, result: pruneResult } = pruneHistoryWithBudget({
484
- messages: rawHistoryMessages,
485
- config: host.config,
486
- provider: host.provider,
487
- modelRegistry: host.modelRegistry,
488
- model: visionCandidates[0],
489
- });
490
- if (pruneResult.droppedCount > 0) {
491
- logger.debug(`[多模态上下文窗口] 丢弃 ${pruneResult.droppedCount} 条历史消息 (${pruneResult.droppedTokens} tokens)`);
246
+ const visionCandidates = resolveModelCandidates(host.getTurnProvider().models, host.modelRegistry, host.getTurnProvider().name, host.config, 'vision');
247
+ logPhase(host.phaseConfig, 'path.agent_loop', sessionId, { mode: 'multimodal' });
248
+ const userMessage = await buildVisionUserMessage(sessionUserContent, parts, supportsVision, DEFAULT_MULTIMODAL_CONFIG.maxFileBytes);
249
+ let loopResult;
250
+ try {
251
+ loopResult = await host.promptController.schedule({
252
+ sessionKey,
253
+ sessionId,
254
+ userMessages: [userMessage],
255
+ context,
256
+ onChunk,
257
+ execute: (initialMessages, hooks, signal, _turnId) => runAgentLoopVisionTurn({
258
+ host,
259
+ sessionId,
260
+ context,
261
+ visionSystemPrompt,
262
+ userMessages: initialMessages,
263
+ modelCandidates: visionCandidates,
264
+ onChunk,
265
+ promptHooks: hooks,
266
+ signal,
267
+ }),
268
+ });
492
269
  }
493
- const messages = [
494
- { role: 'system', content: visionSystemPrompt },
495
- ...historyMessages,
496
- { role: 'user', content: llmParts },
497
- ];
498
- let reply = '';
499
- let lastUsage = null;
500
- let usedVisionModel = visionCandidates[0] || '';
501
- for (let i = 0; i < visionCandidates.length; i++) {
502
- const visionModel = visionCandidates[i];
503
- usedVisionModel = visionModel;
504
- try {
505
- reply = '';
506
- for await (const chunk of host.provider.chatStream({ model: visionModel, messages })) {
507
- if (chunk.usage)
508
- lastUsage = chunk.usage;
509
- const delta = chunk.choices?.[0]?.delta;
510
- if (!delta)
511
- continue;
512
- const text = typeof delta.content === 'string' ? delta.content : '';
513
- if (text) {
514
- reply += text;
515
- if (onChunk)
516
- onChunk(text, reply);
517
- }
518
- }
519
- reply = stripThinkBlocks(reply);
520
- if (!reply) {
521
- logger.warn(formatCompact({ mode: 'multimodal', fallback: visionModel, reason: 'empty_stream' }));
522
- const response = await host.provider.chat({ model: visionModel, messages });
523
- if (response.usage)
524
- lastUsage = response.usage;
525
- const msg = response.choices[0]?.message?.content;
526
- reply = stripThinkBlocks(typeof msg === 'string' ? msg : '');
527
- }
528
- if (reply)
529
- break;
530
- }
531
- catch (err) {
532
- const isLast = i === visionCandidates.length - 1;
533
- if (isLast) {
534
- try {
535
- const response = await host.provider.chat({ model: visionModel, messages });
536
- if (response.usage)
537
- lastUsage = response.usage;
538
- const msg = response.choices[0]?.message?.content;
539
- reply = stripThinkBlocks(typeof msg === 'string' ? msg : '');
540
- }
541
- catch { /* all candidates exhausted */ }
542
- }
543
- else {
544
- logger.warn(formatCompact({
545
- mode: 'multimodal',
546
- fallback: `${visionModel}→${visionCandidates[i + 1]}`,
547
- error: truncatePreview(err.message),
548
- }));
549
- }
550
- }
270
+ catch {
271
+ loopResult = {
272
+ reply: '抱歉,我无法理解这条消息。',
273
+ usage: EMPTY_USAGE,
274
+ path: 'multimodal',
275
+ iterations: 0,
276
+ model: visionCandidates[0] || '',
277
+ toolCalls: [],
278
+ };
551
279
  }
552
- if (!reply)
553
- reply = '抱歉,我无法理解这条消息。';
554
- reply = sanitizeAssistantReply(reply);
280
+ const reply = loopResult.reply;
555
281
  await host.emitter.dispatch('ai.response', host.emitter.createPayload(sessionId, context, 'multimodal', {
556
282
  path: 'multimodal',
557
- model: usedVisionModel,
283
+ model: loopResult.model,
558
284
  reply,
559
285
  }));
560
286
  await touchSession(sessionDeps(host), sessionId);
@@ -562,13 +288,14 @@ export async function processMultimodalTurn(host, parts, context, onChunk) {
562
288
  host.emitSessionNewEvent(sessionId, context, 'multimodal', sessionUserContent, reply);
563
289
  }
564
290
  await host.finalizeActiveTurn({
565
- usage: lastUsage ?? EMPTY_USAGE,
291
+ usage: loopResult.usage,
566
292
  path: 'multimodal',
567
- model: usedVisionModel,
293
+ model: loopResult.model,
294
+ iterations: loopResult.iterations,
568
295
  });
569
296
  await host.emitter.dispatch('ai.processing.finish', host.emitter.createPayload(sessionId, context, 'multimodal', {
570
297
  path: 'multimodal',
571
- model: usedVisionModel,
298
+ model: loopResult.model,
572
299
  reply,
573
300
  }));
574
301
  host.emitter.emit('ai.typing.stop', host.emitter.createPayload(sessionId, context, 'multimodal', {