hyacinth-ai 0.9.6

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 (704) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/commands.json +25 -0
  4. package/dist/agents/builtins.d.ts +8 -0
  5. package/dist/agents/builtins.js +50 -0
  6. package/dist/agents/config-loader.d.ts +15 -0
  7. package/dist/agents/config-loader.js +75 -0
  8. package/dist/agents/delegate-tool.d.ts +109 -0
  9. package/dist/agents/delegate-tool.js +407 -0
  10. package/dist/agents/index.d.ts +5 -0
  11. package/dist/agents/index.js +5 -0
  12. package/dist/agents/registry.d.ts +2 -0
  13. package/dist/agents/registry.js +2 -0
  14. package/dist/bypass/agents/companion/index.d.ts +57 -0
  15. package/dist/bypass/agents/companion/index.js +487 -0
  16. package/dist/bypass/agents/orchestrator/index.d.ts +17 -0
  17. package/dist/bypass/agents/orchestrator/index.js +414 -0
  18. package/dist/bypass/agents/orchestrator.d.ts +16 -0
  19. package/dist/bypass/agents/orchestrator.js +377 -0
  20. package/dist/bypass/agents/world-engine/agent.d.ts +41 -0
  21. package/dist/bypass/agents/world-engine/agent.js +385 -0
  22. package/dist/bypass/agents/world-engine/index.d.ts +52 -0
  23. package/dist/bypass/agents/world-engine/index.js +198 -0
  24. package/dist/bypass/agents/world-engine/sim.d.ts +14 -0
  25. package/dist/bypass/agents/world-engine/sim.js +120 -0
  26. package/dist/bypass/agents/world-engine/store.d.ts +110 -0
  27. package/dist/bypass/agents/world-engine/store.js +519 -0
  28. package/dist/bypass/agents/world-engine/ticker.d.ts +37 -0
  29. package/dist/bypass/agents/world-engine/ticker.js +214 -0
  30. package/dist/bypass/agents/world-engine/tools.d.ts +6 -0
  31. package/dist/bypass/agents/world-engine/tools.js +380 -0
  32. package/dist/bypass/agents/world-engine/types.d.ts +202 -0
  33. package/dist/bypass/agents/world-engine/types.js +56 -0
  34. package/dist/bypass/agents/world-engine.d.ts +20 -0
  35. package/dist/bypass/agents/world-engine.js +63 -0
  36. package/dist/bypass/base.d.ts +48 -0
  37. package/dist/bypass/base.js +126 -0
  38. package/dist/bypass/index.d.ts +5 -0
  39. package/dist/bypass/index.js +6 -0
  40. package/dist/bypass/manager.d.ts +41 -0
  41. package/dist/bypass/manager.js +179 -0
  42. package/dist/bypass/types.d.ts +63 -0
  43. package/dist/bypass/types.js +14 -0
  44. package/dist/channels/auto-detect.d.ts +25 -0
  45. package/dist/channels/auto-detect.js +77 -0
  46. package/dist/channels/builtin/http-webhook.d.ts +25 -0
  47. package/dist/channels/builtin/http-webhook.js +287 -0
  48. package/dist/channels/builtin/tui-channel.d.ts +33 -0
  49. package/dist/channels/builtin/tui-channel.js +61 -0
  50. package/dist/channels/builtin/tui-ws-session.d.ts +16 -0
  51. package/dist/channels/builtin/tui-ws-session.js +90 -0
  52. package/dist/channels/builtin/webui-output-handler.d.ts +30 -0
  53. package/dist/channels/builtin/webui-output-handler.js +93 -0
  54. package/dist/channels/index.d.ts +5 -0
  55. package/dist/channels/index.js +3 -0
  56. package/dist/channels/interface.d.ts +148 -0
  57. package/dist/channels/interface.js +15 -0
  58. package/dist/channels/manager.d.ts +43 -0
  59. package/dist/channels/manager.js +156 -0
  60. package/dist/channels/message-queue.d.ts +36 -0
  61. package/dist/channels/message-queue.js +91 -0
  62. package/dist/channels/plugins/clawbot/clawbot-auth.d.ts +50 -0
  63. package/dist/channels/plugins/clawbot/clawbot-auth.js +235 -0
  64. package/dist/channels/plugins/clawbot/clawbot-channel.d.ts +76 -0
  65. package/dist/channels/plugins/clawbot/clawbot-channel.js +566 -0
  66. package/dist/channels/plugins/clawbot/clawbot-client.d.ts +180 -0
  67. package/dist/channels/plugins/clawbot/clawbot-client.js +312 -0
  68. package/dist/channels/plugins/clawbot/clawbot-config.d.ts +30 -0
  69. package/dist/channels/plugins/clawbot/clawbot-config.js +28 -0
  70. package/dist/channels/plugins/clawbot/clawbot-message-queue.d.ts +27 -0
  71. package/dist/channels/plugins/clawbot/clawbot-message-queue.js +114 -0
  72. package/dist/channels/plugins/clawbot/clawbot-session.d.ts +10 -0
  73. package/dist/channels/plugins/clawbot/clawbot-session.js +26 -0
  74. package/dist/channels/plugins/clawbot/index.d.ts +17 -0
  75. package/dist/channels/plugins/clawbot/index.js +58 -0
  76. package/dist/channels/plugins/feishu/feishu-channel.d.ts +57 -0
  77. package/dist/channels/plugins/feishu/feishu-channel.js +602 -0
  78. package/dist/channels/plugins/feishu/feishu-client.d.ts +23 -0
  79. package/dist/channels/plugins/feishu/feishu-client.js +86 -0
  80. package/dist/channels/plugins/feishu/feishu-config.d.ts +53 -0
  81. package/dist/channels/plugins/feishu/feishu-config.js +35 -0
  82. package/dist/channels/plugins/feishu/feishu-event.d.ts +102 -0
  83. package/dist/channels/plugins/feishu/feishu-event.js +257 -0
  84. package/dist/channels/plugins/feishu/feishu-log-suppressor.d.ts +7 -0
  85. package/dist/channels/plugins/feishu/feishu-log-suppressor.js +34 -0
  86. package/dist/channels/plugins/feishu/feishu-message-queue.d.ts +27 -0
  87. package/dist/channels/plugins/feishu/feishu-message-queue.js +112 -0
  88. package/dist/channels/plugins/feishu/feishu-send.d.ts +44 -0
  89. package/dist/channels/plugins/feishu/feishu-send.js +232 -0
  90. package/dist/channels/plugins/feishu/feishu-session.d.ts +44 -0
  91. package/dist/channels/plugins/feishu/feishu-session.js +84 -0
  92. package/dist/channels/plugins/feishu/feishu-streaming.d.ts +49 -0
  93. package/dist/channels/plugins/feishu/feishu-streaming.js +185 -0
  94. package/dist/channels/plugins/feishu/feishu-transport.d.ts +29 -0
  95. package/dist/channels/plugins/feishu/feishu-transport.js +99 -0
  96. package/dist/channels/plugins/feishu/index.d.ts +22 -0
  97. package/dist/channels/plugins/feishu/index.js +68 -0
  98. package/dist/cli/doctor.d.ts +20 -0
  99. package/dist/cli/doctor.js +317 -0
  100. package/dist/context/cache-strategy.d.ts +76 -0
  101. package/dist/context/cache-strategy.js +123 -0
  102. package/dist/context/composer.d.ts +77 -0
  103. package/dist/context/composer.js +318 -0
  104. package/dist/context/compressor.d.ts +218 -0
  105. package/dist/context/compressor.js +909 -0
  106. package/dist/context/index.d.ts +14 -0
  107. package/dist/context/index.js +8 -0
  108. package/dist/context/interface.d.ts +33 -0
  109. package/dist/context/interface.js +2 -0
  110. package/dist/context/manifest-defaults.d.ts +51 -0
  111. package/dist/context/manifest-defaults.js +133 -0
  112. package/dist/context/manifest-loader.d.ts +16 -0
  113. package/dist/context/manifest-loader.js +103 -0
  114. package/dist/context/manifest-types.d.ts +26 -0
  115. package/dist/context/manifest-types.js +2 -0
  116. package/dist/context/precision/analyzer.d.ts +31 -0
  117. package/dist/context/precision/analyzer.js +131 -0
  118. package/dist/context/precision/companion.d.ts +27 -0
  119. package/dist/context/precision/companion.js +144 -0
  120. package/dist/context/precision/default.d.ts +14 -0
  121. package/dist/context/precision/default.js +17 -0
  122. package/dist/context/precision/index.d.ts +9 -0
  123. package/dist/context/precision/index.js +7 -0
  124. package/dist/context/precision/keyword-pool.d.ts +27 -0
  125. package/dist/context/precision/keyword-pool.js +66 -0
  126. package/dist/context/precision/strategy.d.ts +18 -0
  127. package/dist/context/precision/strategy.js +51 -0
  128. package/dist/context/precision/summary-store.d.ts +18 -0
  129. package/dist/context/precision/summary-store.js +67 -0
  130. package/dist/context/precision/types.d.ts +23 -0
  131. package/dist/context/precision/types.js +2 -0
  132. package/dist/context/profiles.d.ts +45 -0
  133. package/dist/context/profiles.js +92 -0
  134. package/dist/context/prompt-builder.d.ts +76 -0
  135. package/dist/context/prompt-builder.js +133 -0
  136. package/dist/context/retriever.d.ts +44 -0
  137. package/dist/context/retriever.js +203 -0
  138. package/dist/context/router.d.ts +122 -0
  139. package/dist/context/router.js +291 -0
  140. package/dist/context/section-resolver.d.ts +38 -0
  141. package/dist/context/section-resolver.js +398 -0
  142. package/dist/context/tokenizer.d.ts +23 -0
  143. package/dist/context/tokenizer.js +78 -0
  144. package/dist/dependency/analyzer.d.ts +33 -0
  145. package/dist/dependency/analyzer.js +242 -0
  146. package/dist/dependency/data-flow-tracker.d.ts +12 -0
  147. package/dist/dependency/data-flow-tracker.js +97 -0
  148. package/dist/dependency/function-parser.d.ts +34 -0
  149. package/dist/dependency/function-parser.js +295 -0
  150. package/dist/dependency/index.d.ts +14 -0
  151. package/dist/dependency/index.js +20 -0
  152. package/dist/dependency/parser.d.ts +10 -0
  153. package/dist/dependency/parser.js +93 -0
  154. package/dist/dependency/types.d.ts +57 -0
  155. package/dist/dependency/types.js +2 -0
  156. package/dist/env/env-collector.d.ts +45 -0
  157. package/dist/env/env-collector.js +221 -0
  158. package/dist/env/index.d.ts +3 -0
  159. package/dist/env/index.js +2 -0
  160. package/dist/event-store.d.ts +27 -0
  161. package/dist/event-store.js +71 -0
  162. package/dist/evolution/git-manager.d.ts +46 -0
  163. package/dist/evolution/git-manager.js +276 -0
  164. package/dist/evolution/index.d.ts +3 -0
  165. package/dist/evolution/index.js +2 -0
  166. package/dist/flow/bootstrap-flow.d.ts +19 -0
  167. package/dist/flow/bootstrap-flow.js +72 -0
  168. package/dist/flow/flow-registry.d.ts +12 -0
  169. package/dist/flow/flow-registry.js +74 -0
  170. package/dist/flow/flow-tools.d.ts +8 -0
  171. package/dist/flow/flow-tools.js +106 -0
  172. package/dist/flow/index.d.ts +6 -0
  173. package/dist/flow/index.js +8 -0
  174. package/dist/flow/todo-flow.d.ts +25 -0
  175. package/dist/flow/todo-flow.js +107 -0
  176. package/dist/flow/types.d.ts +78 -0
  177. package/dist/flow/types.js +12 -0
  178. package/dist/flows/bootstrap.d.ts +18 -0
  179. package/dist/flows/bootstrap.js +119 -0
  180. package/dist/flows/index.d.ts +6 -0
  181. package/dist/flows/index.js +11 -0
  182. package/dist/flows/todo.d.ts +26 -0
  183. package/dist/flows/todo.js +174 -0
  184. package/dist/flows/tools.d.ts +8 -0
  185. package/dist/flows/tools.js +119 -0
  186. package/dist/flows/types.d.ts +27 -0
  187. package/dist/flows/types.js +10 -0
  188. package/dist/gateway/cli.d.ts +5 -0
  189. package/dist/gateway/cli.js +1027 -0
  190. package/dist/gateway/factory.d.ts +80 -0
  191. package/dist/gateway/factory.js +832 -0
  192. package/dist/gateway/guardian.d.ts +8 -0
  193. package/dist/gateway/guardian.js +30 -0
  194. package/dist/gateway/index.d.ts +3 -0
  195. package/dist/gateway/index.js +3 -0
  196. package/dist/gateway/server.d.ts +17 -0
  197. package/dist/gateway/server.js +96 -0
  198. package/dist/gateway/tui.d.ts +16 -0
  199. package/dist/gateway/tui.js +3273 -0
  200. package/dist/hot-reload/agent-watcher.d.ts +24 -0
  201. package/dist/hot-reload/agent-watcher.js +175 -0
  202. package/dist/hot-reload/bundle-watcher.d.ts +12 -0
  203. package/dist/hot-reload/bundle-watcher.js +35 -0
  204. package/dist/hot-reload/channel-watcher.d.ts +19 -0
  205. package/dist/hot-reload/channel-watcher.js +54 -0
  206. package/dist/hot-reload/command-watcher.d.ts +14 -0
  207. package/dist/hot-reload/command-watcher.js +54 -0
  208. package/dist/hot-reload/config-watcher.d.ts +20 -0
  209. package/dist/hot-reload/config-watcher.js +58 -0
  210. package/dist/hot-reload/index.d.ts +12 -0
  211. package/dist/hot-reload/index.js +12 -0
  212. package/dist/hot-reload/manager.d.ts +58 -0
  213. package/dist/hot-reload/manager.js +243 -0
  214. package/dist/hot-reload/manifest-watcher.d.ts +9 -0
  215. package/dist/hot-reload/manifest-watcher.js +42 -0
  216. package/dist/hot-reload/mcp-watcher.d.ts +18 -0
  217. package/dist/hot-reload/mcp-watcher.js +64 -0
  218. package/dist/hot-reload/model-catalog-watcher.d.ts +18 -0
  219. package/dist/hot-reload/model-catalog-watcher.js +62 -0
  220. package/dist/hot-reload/plugin-watcher.d.ts +20 -0
  221. package/dist/hot-reload/plugin-watcher.js +56 -0
  222. package/dist/hot-reload/prompt-watcher.d.ts +6 -0
  223. package/dist/hot-reload/prompt-watcher.js +38 -0
  224. package/dist/hot-reload/provider-watcher.d.ts +16 -0
  225. package/dist/hot-reload/provider-watcher.js +45 -0
  226. package/dist/hot-reload/skill-watcher.d.ts +19 -0
  227. package/dist/hot-reload/skill-watcher.js +82 -0
  228. package/dist/hot-reload/tool-watcher.d.ts +16 -0
  229. package/dist/hot-reload/tool-watcher.js +168 -0
  230. package/dist/hot-reload/workflow-watcher.d.ts +19 -0
  231. package/dist/hot-reload/workflow-watcher.js +84 -0
  232. package/dist/index.d.ts +3 -0
  233. package/dist/index.js +6 -0
  234. package/dist/knowledge/fts5-retriever.d.ts +30 -0
  235. package/dist/knowledge/fts5-retriever.js +223 -0
  236. package/dist/knowledge/index.d.ts +11 -0
  237. package/dist/knowledge/index.js +14 -0
  238. package/dist/knowledge/indexer.d.ts +41 -0
  239. package/dist/knowledge/indexer.js +186 -0
  240. package/dist/knowledge/query-preprocessor.d.ts +13 -0
  241. package/dist/knowledge/query-preprocessor.js +63 -0
  242. package/dist/knowledge/retriever.d.ts +45 -0
  243. package/dist/knowledge/retriever.js +9 -0
  244. package/dist/knowledge/store.d.ts +27 -0
  245. package/dist/knowledge/store.js +61 -0
  246. package/dist/knowledge/structured-store.d.ts +78 -0
  247. package/dist/knowledge/structured-store.js +280 -0
  248. package/dist/knowledge/structured-tools.d.ts +12 -0
  249. package/dist/knowledge/structured-tools.js +151 -0
  250. package/dist/knowledge/tools.d.ts +20 -0
  251. package/dist/knowledge/tools.js +238 -0
  252. package/dist/knowledge/watcher.d.ts +28 -0
  253. package/dist/knowledge/watcher.js +113 -0
  254. package/dist/lifecycle/index.d.ts +12 -0
  255. package/dist/lifecycle/index.js +14 -0
  256. package/dist/lifecycle/interface.d.ts +94 -0
  257. package/dist/lifecycle/interface.js +2 -0
  258. package/dist/lifecycle/local-model.d.ts +62 -0
  259. package/dist/lifecycle/local-model.js +331 -0
  260. package/dist/lifecycle/manager.d.ts +70 -0
  261. package/dist/lifecycle/manager.js +374 -0
  262. package/dist/lifecycle/supervisor.d.ts +89 -0
  263. package/dist/lifecycle/supervisor.js +277 -0
  264. package/dist/lifecycle-integration-test.d.ts +2 -0
  265. package/dist/lifecycle-integration-test.js +72 -0
  266. package/dist/local-model/download-manager.d.ts +59 -0
  267. package/dist/local-model/download-manager.js +517 -0
  268. package/dist/local-model/index.d.ts +109 -0
  269. package/dist/local-model/index.js +233 -0
  270. package/dist/local-model/llamacpp-provider.d.ts +95 -0
  271. package/dist/local-model/llamacpp-provider.js +379 -0
  272. package/dist/local-model/model-bridge.d.ts +48 -0
  273. package/dist/local-model/model-bridge.js +256 -0
  274. package/dist/local-model/model-registry.d.ts +47 -0
  275. package/dist/local-model/model-registry.js +188 -0
  276. package/dist/local-model/types.d.ts +49 -0
  277. package/dist/local-model/types.js +2 -0
  278. package/dist/logging/index.d.ts +3 -0
  279. package/dist/logging/index.js +3 -0
  280. package/dist/logging/logger.d.ts +56 -0
  281. package/dist/logging/logger.js +94 -0
  282. package/dist/machine/flows/bootstrap.d.ts +18 -0
  283. package/dist/machine/flows/bootstrap.js +119 -0
  284. package/dist/machine/flows/index.d.ts +6 -0
  285. package/dist/machine/flows/index.js +11 -0
  286. package/dist/machine/flows/spec.d.ts +17 -0
  287. package/dist/machine/flows/spec.js +176 -0
  288. package/dist/machine/flows/todo.d.ts +26 -0
  289. package/dist/machine/flows/todo.js +175 -0
  290. package/dist/machine/flows/tools.d.ts +8 -0
  291. package/dist/machine/flows/tools.js +119 -0
  292. package/dist/machine/flows/types.d.ts +33 -0
  293. package/dist/machine/flows/types.js +10 -0
  294. package/dist/machine/index.d.ts +8 -0
  295. package/dist/machine/index.js +9 -0
  296. package/dist/machine/registry.d.ts +34 -0
  297. package/dist/machine/registry.js +98 -0
  298. package/dist/machine/runner.d.ts +41 -0
  299. package/dist/machine/runner.js +177 -0
  300. package/dist/machine/types.d.ts +121 -0
  301. package/dist/machine/types.js +9 -0
  302. package/dist/mcp/bridge.d.ts +33 -0
  303. package/dist/mcp/bridge.js +99 -0
  304. package/dist/mcp/client.d.ts +37 -0
  305. package/dist/mcp/client.js +161 -0
  306. package/dist/mcp/config.d.ts +15 -0
  307. package/dist/mcp/config.js +85 -0
  308. package/dist/mcp/index.d.ts +7 -0
  309. package/dist/mcp/index.js +7 -0
  310. package/dist/mcp/install-manager.d.ts +31 -0
  311. package/dist/mcp/install-manager.js +158 -0
  312. package/dist/mcp/lifecycle.d.ts +41 -0
  313. package/dist/mcp/lifecycle.js +175 -0
  314. package/dist/mcp/process-transport.d.ts +21 -0
  315. package/dist/mcp/process-transport.js +59 -0
  316. package/dist/mcp/side-effect.d.ts +5 -0
  317. package/dist/mcp/side-effect.js +21 -0
  318. package/dist/mcp/system.d.ts +95 -0
  319. package/dist/mcp/system.js +279 -0
  320. package/dist/memory/companion-session.d.ts +22 -0
  321. package/dist/memory/companion-session.js +77 -0
  322. package/dist/memory/compression-boundary.d.ts +20 -0
  323. package/dist/memory/compression-boundary.js +40 -0
  324. package/dist/memory/conversation.d.ts +34 -0
  325. package/dist/memory/conversation.js +107 -0
  326. package/dist/memory/events.d.ts +42 -0
  327. package/dist/memory/events.js +56 -0
  328. package/dist/memory/index.d.ts +7 -0
  329. package/dist/memory/index.js +6 -0
  330. package/dist/memory/memory-store.d.ts +11 -0
  331. package/dist/memory/memory-store.js +51 -0
  332. package/dist/memory/session-allowlist.d.ts +11 -0
  333. package/dist/memory/session-allowlist.js +50 -0
  334. package/dist/memory/session.d.ts +48 -0
  335. package/dist/memory/session.js +275 -0
  336. package/dist/memory/stats.d.ts +23 -0
  337. package/dist/memory/stats.js +59 -0
  338. package/dist/memory/summary.d.ts +7 -0
  339. package/dist/memory/summary.js +23 -0
  340. package/dist/multimodal/index.d.ts +66 -0
  341. package/dist/multimodal/index.js +210 -0
  342. package/dist/orchestrator/index.d.ts +5 -0
  343. package/dist/orchestrator/index.js +4 -0
  344. package/dist/orchestrator/loop.d.ts +331 -0
  345. package/dist/orchestrator/loop.js +2443 -0
  346. package/dist/orchestrator/plan-store.d.ts +81 -0
  347. package/dist/orchestrator/plan-store.js +167 -0
  348. package/dist/orchestrator/planner.d.ts +20 -0
  349. package/dist/orchestrator/planner.js +34 -0
  350. package/dist/parser/index.d.ts +2 -0
  351. package/dist/parser/index.js +2 -0
  352. package/dist/parser/router.d.ts +30 -0
  353. package/dist/parser/router.js +61 -0
  354. package/dist/plugins/api.d.ts +39 -0
  355. package/dist/plugins/api.js +85 -0
  356. package/dist/plugins/define.d.ts +27 -0
  357. package/dist/plugins/define.js +32 -0
  358. package/dist/plugins/index.d.ts +9 -0
  359. package/dist/plugins/index.js +5 -0
  360. package/dist/plugins/loader.d.ts +39 -0
  361. package/dist/plugins/loader.js +128 -0
  362. package/dist/plugins/manager.d.ts +61 -0
  363. package/dist/plugins/manager.js +257 -0
  364. package/dist/plugins/types.d.ts +82 -0
  365. package/dist/plugins/types.js +2 -0
  366. package/dist/prompts/agents/code-reviewer.md +16 -0
  367. package/dist/prompts/agents/security-auditor.md +17 -0
  368. package/dist/prompts/agents/test-writer.md +15 -0
  369. package/dist/prompts/attention/attention.md +12 -0
  370. package/dist/prompts/environment/environment.md +18 -0
  371. package/dist/prompts/environment/feishu.md +107 -0
  372. package/dist/prompts/flows/bootstrap-confirm.md +14 -0
  373. package/dist/prompts/flows/bootstrap-identity.md +11 -0
  374. package/dist/prompts/flows/bootstrap-soul.md +19 -0
  375. package/dist/prompts/flows/bootstrap-user.md +11 -0
  376. package/dist/prompts/flows/spec-checklist.md +27 -0
  377. package/dist/prompts/flows/spec-define.md +64 -0
  378. package/dist/prompts/flows/spec-executing-checklist.md +15 -0
  379. package/dist/prompts/flows/spec-executing-tasks.md +15 -0
  380. package/dist/prompts/flows/spec-spec.md +51 -0
  381. package/dist/prompts/flows/spec-tasks.md +32 -0
  382. package/dist/prompts/flows/todo-execution.md +7 -0
  383. package/dist/prompts/flows/todo-planning.md +18 -0
  384. package/dist/prompts/framework/architecture.md +131 -0
  385. package/dist/prompts/identity/identity.md +2 -0
  386. package/dist/prompts/loader.d.ts +24 -0
  387. package/dist/prompts/loader.js +102 -0
  388. package/dist/prompts/loader.ts +119 -0
  389. package/dist/prompts/orchestration/summary.md +14 -0
  390. package/dist/prompts/persona/BOOTSTRAP.md +5 -0
  391. package/dist/prompts/persona/IDENTITY.md +16 -0
  392. package/dist/prompts/persona/PartnerMemory.md +5 -0
  393. package/dist/prompts/persona/PartnerSoul.md +7 -0
  394. package/dist/prompts/persona/SOUL.md +30 -0
  395. package/dist/prompts/persona/USER.md +16 -0
  396. package/dist/prompts/precise/analyze.md +23 -0
  397. package/dist/prompts/precise/persona.md +18 -0
  398. package/dist/prompts/safety/coding-standards.md +5 -0
  399. package/dist/prompts/safety/safety.md +4 -0
  400. package/dist/prompts/skills/code-review.md +4 -0
  401. package/dist/prompts/skills/debug.md +7 -0
  402. package/dist/prompts/skills/framework-reference.md +131 -0
  403. package/dist/prompts/skills/refactor.md +6 -0
  404. package/dist/prompts/summary.md +22 -0
  405. package/dist/prompts/tools/framework-capabilities.md +241 -0
  406. package/dist/prompts/tools/tool-rules.md +15 -0
  407. package/dist/provider/anthropic.d.ts +62 -0
  408. package/dist/provider/anthropic.js +361 -0
  409. package/dist/provider/catalog.d.ts +58 -0
  410. package/dist/provider/catalog.js +97 -0
  411. package/dist/provider/compatible.d.ts +89 -0
  412. package/dist/provider/compatible.js +328 -0
  413. package/dist/provider/config.d.ts +23 -0
  414. package/dist/provider/config.js +158 -0
  415. package/dist/provider/deepseek.d.ts +25 -0
  416. package/dist/provider/deepseek.js +37 -0
  417. package/dist/provider/fallback.d.ts +51 -0
  418. package/dist/provider/fallback.js +113 -0
  419. package/dist/provider/gemini.d.ts +27 -0
  420. package/dist/provider/gemini.js +170 -0
  421. package/dist/provider/index.d.ts +36 -0
  422. package/dist/provider/index.js +42 -0
  423. package/dist/provider/interface.d.ts +62 -0
  424. package/dist/provider/interface.js +2 -0
  425. package/dist/provider/local-config.d.ts +37 -0
  426. package/dist/provider/local-config.js +146 -0
  427. package/dist/provider/local.d.ts +45 -0
  428. package/dist/provider/local.js +248 -0
  429. package/dist/provider/manager.d.ts +67 -0
  430. package/dist/provider/manager.js +355 -0
  431. package/dist/provider/mimo.d.ts +19 -0
  432. package/dist/provider/mimo.js +30 -0
  433. package/dist/provider/minimax.d.ts +22 -0
  434. package/dist/provider/minimax.js +33 -0
  435. package/dist/provider/model-catalog-loader.d.ts +42 -0
  436. package/dist/provider/model-catalog-loader.js +89 -0
  437. package/dist/provider/model-channel-registry.d.ts +130 -0
  438. package/dist/provider/model-channel-registry.js +488 -0
  439. package/dist/provider/model-router.d.ts +60 -0
  440. package/dist/provider/model-router.js +87 -0
  441. package/dist/provider/openai.d.ts +54 -0
  442. package/dist/provider/openai.js +274 -0
  443. package/dist/provider/qwen.d.ts +19 -0
  444. package/dist/provider/qwen.js +30 -0
  445. package/dist/provider/resilient.d.ts +46 -0
  446. package/dist/provider/resilient.js +181 -0
  447. package/dist/provider/retry.d.ts +16 -0
  448. package/dist/provider/retry.js +78 -0
  449. package/dist/provider/router.d.ts +68 -0
  450. package/dist/provider/router.js +145 -0
  451. package/dist/provider/tool-args-recovery.d.ts +36 -0
  452. package/dist/provider/tool-args-recovery.js +91 -0
  453. package/dist/provider/user-id.d.ts +13 -0
  454. package/dist/provider/user-id.js +53 -0
  455. package/dist/provider/zhipu.d.ts +19 -0
  456. package/dist/provider/zhipu.js +32 -0
  457. package/dist/registry/agent.registry.d.ts +57 -0
  458. package/dist/registry/agent.registry.js +224 -0
  459. package/dist/registry/base.d.ts +54 -0
  460. package/dist/registry/base.js +99 -0
  461. package/dist/registry/channel.registry.d.ts +53 -0
  462. package/dist/registry/channel.registry.js +87 -0
  463. package/dist/registry/index.d.ts +9 -0
  464. package/dist/registry/index.js +10 -0
  465. package/dist/registry/mcp.registry.d.ts +2 -0
  466. package/dist/registry/mcp.registry.js +2 -0
  467. package/dist/registry/plugin.registry.d.ts +33 -0
  468. package/dist/registry/plugin.registry.js +66 -0
  469. package/dist/registry/provider.registry.d.ts +51 -0
  470. package/dist/registry/provider.registry.js +104 -0
  471. package/dist/registry/skill.registry.d.ts +33 -0
  472. package/dist/registry/skill.registry.js +95 -0
  473. package/dist/registry/tool.registry.d.ts +68 -0
  474. package/dist/registry/tool.registry.js +185 -0
  475. package/dist/repair/loop-guard.d.ts +98 -0
  476. package/dist/repair/loop-guard.js +217 -0
  477. package/dist/repair/scavenge.d.ts +19 -0
  478. package/dist/repair/scavenge.js +81 -0
  479. package/dist/repair/storm.d.ts +7 -0
  480. package/dist/repair/storm.js +7 -0
  481. package/dist/rollback/index.d.ts +5 -0
  482. package/dist/rollback/index.js +4 -0
  483. package/dist/rollback/rollback-tool.d.ts +13 -0
  484. package/dist/rollback/rollback-tool.js +167 -0
  485. package/dist/rollback/turn-recorder.d.ts +37 -0
  486. package/dist/rollback/turn-recorder.js +183 -0
  487. package/dist/rollback/turn-store.d.ts +29 -0
  488. package/dist/rollback/turn-store.js +114 -0
  489. package/dist/rollback/types.d.ts +51 -0
  490. package/dist/rollback/types.js +9 -0
  491. package/dist/runtime/config-center.d.ts +92 -0
  492. package/dist/runtime/config-center.js +467 -0
  493. package/dist/runtime/config-schema.d.ts +201 -0
  494. package/dist/runtime/config-schema.js +2 -0
  495. package/dist/runtime/defaults.d.ts +3 -0
  496. package/dist/runtime/defaults.js +146 -0
  497. package/dist/schedule/cron.d.ts +24 -0
  498. package/dist/schedule/cron.js +145 -0
  499. package/dist/schedule/index.d.ts +6 -0
  500. package/dist/schedule/index.js +4 -0
  501. package/dist/schedule/persistence.d.ts +33 -0
  502. package/dist/schedule/persistence.js +106 -0
  503. package/dist/schedule/scheduler.d.ts +85 -0
  504. package/dist/schedule/scheduler.js +630 -0
  505. package/dist/schedule/types.d.ts +185 -0
  506. package/dist/schedule/types.js +2 -0
  507. package/dist/setup/config.d.ts +189 -0
  508. package/dist/setup/config.js +253 -0
  509. package/dist/setup/index.d.ts +3 -0
  510. package/dist/setup/index.js +3 -0
  511. package/dist/setup/model-defaults.d.ts +13 -0
  512. package/dist/setup/model-defaults.js +87 -0
  513. package/dist/setup/persona-bootstrap.d.ts +42 -0
  514. package/dist/setup/persona-bootstrap.js +379 -0
  515. package/dist/setup/wizard.d.ts +32 -0
  516. package/dist/setup/wizard.js +384 -0
  517. package/dist/skills/index.d.ts +4 -0
  518. package/dist/skills/index.js +4 -0
  519. package/dist/skills/loader.d.ts +12 -0
  520. package/dist/skills/loader.js +83 -0
  521. package/dist/skills/registry.d.ts +2 -0
  522. package/dist/skills/registry.js +2 -0
  523. package/dist/skills/skill-tool.d.ts +20 -0
  524. package/dist/skills/skill-tool.js +49 -0
  525. package/dist/tools/archive.d.ts +17 -0
  526. package/dist/tools/archive.js +131 -0
  527. package/dist/tools/ask-user.d.ts +9 -0
  528. package/dist/tools/ask-user.js +62 -0
  529. package/dist/tools/background-registry.d.ts +45 -0
  530. package/dist/tools/background-registry.js +164 -0
  531. package/dist/tools/bash.d.ts +56 -0
  532. package/dist/tools/bash.js +391 -0
  533. package/dist/tools/bundle-registry.d.ts +42 -0
  534. package/dist/tools/bundle-registry.js +239 -0
  535. package/dist/tools/bundle-tools.d.ts +11 -0
  536. package/dist/tools/bundle-tools.js +168 -0
  537. package/dist/tools/channel-info.d.ts +5 -0
  538. package/dist/tools/channel-info.js +38 -0
  539. package/dist/tools/compression.d.ts +7 -0
  540. package/dist/tools/compression.js +21 -0
  541. package/dist/tools/config.d.ts +23 -0
  542. package/dist/tools/config.js +255 -0
  543. package/dist/tools/db-query.d.ts +12 -0
  544. package/dist/tools/db-query.js +107 -0
  545. package/dist/tools/diagnostics.d.ts +30 -0
  546. package/dist/tools/diagnostics.js +111 -0
  547. package/dist/tools/diff-channel.d.ts +9 -0
  548. package/dist/tools/diff-channel.js +17 -0
  549. package/dist/tools/diff-files.d.ts +17 -0
  550. package/dist/tools/diff-files.js +88 -0
  551. package/dist/tools/disk-usage.d.ts +34 -0
  552. package/dist/tools/disk-usage.js +258 -0
  553. package/dist/tools/edit.d.ts +30 -0
  554. package/dist/tools/edit.js +235 -0
  555. package/dist/tools/executor.d.ts +32 -0
  556. package/dist/tools/executor.js +66 -0
  557. package/dist/tools/file-tracker.d.ts +20 -0
  558. package/dist/tools/file-tracker.js +44 -0
  559. package/dist/tools/filtered-registry.d.ts +29 -0
  560. package/dist/tools/filtered-registry.js +50 -0
  561. package/dist/tools/flow.d.ts +10 -0
  562. package/dist/tools/flow.js +180 -0
  563. package/dist/tools/git-tool.d.ts +24 -0
  564. package/dist/tools/git-tool.js +113 -0
  565. package/dist/tools/glob.d.ts +29 -0
  566. package/dist/tools/glob.js +152 -0
  567. package/dist/tools/grep.d.ts +14 -0
  568. package/dist/tools/grep.js +325 -0
  569. package/dist/tools/http-request.d.ts +14 -0
  570. package/dist/tools/http-request.js +126 -0
  571. package/dist/tools/index.d.ts +43 -0
  572. package/dist/tools/index.js +76 -0
  573. package/dist/tools/injection-filter.d.ts +6 -0
  574. package/dist/tools/injection-filter.js +71 -0
  575. package/dist/tools/insert.d.ts +13 -0
  576. package/dist/tools/insert.js +61 -0
  577. package/dist/tools/interface.d.ts +30 -0
  578. package/dist/tools/interface.js +2 -0
  579. package/dist/tools/json-edit.d.ts +15 -0
  580. package/dist/tools/json-edit.js +122 -0
  581. package/dist/tools/multi-edit.d.ts +9 -0
  582. package/dist/tools/multi-edit.js +146 -0
  583. package/dist/tools/path-sandbox.d.ts +14 -0
  584. package/dist/tools/path-sandbox.js +60 -0
  585. package/dist/tools/process-tools.d.ts +15 -0
  586. package/dist/tools/process-tools.js +95 -0
  587. package/dist/tools/python-bridge/bridge.d.ts +24 -0
  588. package/dist/tools/python-bridge/bridge.js +39 -0
  589. package/dist/tools/python-bridge/executor.d.ts +6 -0
  590. package/dist/tools/python-bridge/executor.js +69 -0
  591. package/dist/tools/python-bridge/index.d.ts +5 -0
  592. package/dist/tools/python-bridge/index.js +4 -0
  593. package/dist/tools/python-bridge/parser.d.ts +21 -0
  594. package/dist/tools/python-bridge/parser.js +258 -0
  595. package/dist/tools/read.d.ts +16 -0
  596. package/dist/tools/read.js +276 -0
  597. package/dist/tools/registry.d.ts +2 -0
  598. package/dist/tools/registry.js +2 -0
  599. package/dist/tools/restart.d.ts +10 -0
  600. package/dist/tools/restart.js +46 -0
  601. package/dist/tools/result-buffer.d.ts +66 -0
  602. package/dist/tools/result-buffer.js +146 -0
  603. package/dist/tools/runtime-control.d.ts +163 -0
  604. package/dist/tools/runtime-control.js +1780 -0
  605. package/dist/tools/symbol-references.d.ts +5 -0
  606. package/dist/tools/symbol-references.js +331 -0
  607. package/dist/tools/system-info.d.ts +3 -0
  608. package/dist/tools/system-info.js +55 -0
  609. package/dist/tools/write.d.ts +18 -0
  610. package/dist/tools/write.js +118 -0
  611. package/dist/tools/xref/index.d.ts +25 -0
  612. package/dist/tools/xref/index.js +25 -0
  613. package/dist/tools/xref/manager.d.ts +85 -0
  614. package/dist/tools/xref/manager.js +948 -0
  615. package/dist/tools/xref/parser.d.ts +34 -0
  616. package/dist/tools/xref/parser.js +64 -0
  617. package/dist/tools/xref/regex-parser.d.ts +26 -0
  618. package/dist/tools/xref/regex-parser.js +446 -0
  619. package/dist/tools/xref/schema.d.ts +104 -0
  620. package/dist/tools/xref/schema.js +68 -0
  621. package/dist/tools/xref/ts-parser.d.ts +20 -0
  622. package/dist/tools/xref/ts-parser.js +403 -0
  623. package/dist/tools/xref/xref-build.d.ts +20 -0
  624. package/dist/tools/xref/xref-build.js +128 -0
  625. package/dist/tools/xref/xref-graph.d.ts +24 -0
  626. package/dist/tools/xref/xref-graph.js +88 -0
  627. package/dist/tools/xref/xref-query.d.ts +30 -0
  628. package/dist/tools/xref/xref-query.js +123 -0
  629. package/dist/types.d.ts +217 -0
  630. package/dist/types.js +2 -0
  631. package/dist/ui/assistant-message.d.ts +7 -0
  632. package/dist/ui/assistant-message.js +18 -0
  633. package/dist/ui/chat-log.d.ts +52 -0
  634. package/dist/ui/chat-log.js +246 -0
  635. package/dist/ui/command-registry.d.ts +73 -0
  636. package/dist/ui/command-registry.js +838 -0
  637. package/dist/ui/diff-component.d.ts +14 -0
  638. package/dist/ui/diff-component.js +42 -0
  639. package/dist/ui/hyperlink-markdown.d.ts +10 -0
  640. package/dist/ui/hyperlink-markdown.js +21 -0
  641. package/dist/ui/markdown-message.d.ts +10 -0
  642. package/dist/ui/markdown-message.js +16 -0
  643. package/dist/ui/osc8-hyperlinks.d.ts +4 -0
  644. package/dist/ui/osc8-hyperlinks.js +142 -0
  645. package/dist/ui/pi-tui-editor.d.ts +35 -0
  646. package/dist/ui/pi-tui-editor.js +79 -0
  647. package/dist/ui/slash-commands.d.ts +16 -0
  648. package/dist/ui/slash-commands.js +29 -0
  649. package/dist/ui/slash-panel.d.ts +54 -0
  650. package/dist/ui/slash-panel.js +199 -0
  651. package/dist/ui/theme.d.ts +120 -0
  652. package/dist/ui/theme.js +154 -0
  653. package/dist/ui/tool-display.d.ts +13 -0
  654. package/dist/ui/tool-display.js +85 -0
  655. package/dist/ui/tool-execution.d.ts +22 -0
  656. package/dist/ui/tool-execution.js +87 -0
  657. package/dist/ui/tui-formatters.d.ts +2 -0
  658. package/dist/ui/tui-formatters.js +145 -0
  659. package/dist/ui/user-message.d.ts +5 -0
  660. package/dist/ui/user-message.js +11 -0
  661. package/dist/update/check.d.ts +8 -0
  662. package/dist/update/check.js +28 -0
  663. package/dist/update/config.d.ts +6 -0
  664. package/dist/update/config.js +18 -0
  665. package/dist/update/download.d.ts +3 -0
  666. package/dist/update/download.js +27 -0
  667. package/dist/update/index.d.ts +6 -0
  668. package/dist/update/index.js +5 -0
  669. package/dist/update/install.d.ts +3 -0
  670. package/dist/update/install.js +33 -0
  671. package/dist/update/types.d.ts +16 -0
  672. package/dist/update/types.js +2 -0
  673. package/dist/utils/diff.d.ts +9 -0
  674. package/dist/utils/diff.js +45 -0
  675. package/dist/utils/misc.d.ts +12 -0
  676. package/dist/utils/misc.js +34 -0
  677. package/dist/world-engine/agent.d.ts +41 -0
  678. package/dist/world-engine/agent.js +385 -0
  679. package/dist/world-engine/index.d.ts +45 -0
  680. package/dist/world-engine/index.js +160 -0
  681. package/dist/world-engine/sim.d.ts +14 -0
  682. package/dist/world-engine/sim.js +120 -0
  683. package/dist/world-engine/store.d.ts +110 -0
  684. package/dist/world-engine/store.js +519 -0
  685. package/dist/world-engine/ticker.d.ts +37 -0
  686. package/dist/world-engine/ticker.js +214 -0
  687. package/dist/world-engine/tools.d.ts +6 -0
  688. package/dist/world-engine/tools.js +380 -0
  689. package/dist/world-engine/types.d.ts +202 -0
  690. package/dist/world-engine/types.js +56 -0
  691. package/docs/api.md +180 -0
  692. package/docs/architecture.md +206 -0
  693. package/docs/diagnostics/2026-06-08-config-prompt-guidance.md +67 -0
  694. package/docs/superpowers/plans/2026-06-08-global-bootstrap-mode.md +89 -0
  695. package/docs/superpowers/specs/2026-06-08-global-bootstrap-mode-design.md +174 -0
  696. package/docs/user-guide.md +226 -0
  697. package/package.json +63 -0
  698. package/plugins/example-greeter/index.js +55 -0
  699. package/plugins/example-greeter/plugin.json +8 -0
  700. package/pnpm-workspace.yaml +5 -0
  701. package/provider_arch.md +179 -0
  702. package/start-webui.sh +27 -0
  703. package/tsconfig.tsbuildinfo +1 -0
  704. package/webui-channel-design.md +789 -0
@@ -0,0 +1,1780 @@
1
+ import { clearPromptCache } from '../prompts/loader.js';
2
+ import { switchRouter } from '../context/profiles.js';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ // ============================================================
7
+ // Provider tools (4)
8
+ // ============================================================
9
+ /**
10
+ * switch_provider — switch to a specific named provider.
11
+ * Calls agentLoop.switchProvider(name) which does per-named-provider switching
12
+ * and updates the orchestrator (unlike toggleProvider which only flips local/online).
13
+ */
14
+ export function createSwitchProviderTool(agentLoop) {
15
+ return {
16
+ name: 'switch_provider',
17
+ description: 'Switch the active provider. If api_key is provided, the provider is dynamically created and registered. ' +
18
+ 'Use list_providers to see already-registered names. Model name defaults to the provider\'s default model.',
19
+ inputSchema: {
20
+ type: 'object',
21
+ properties: {
22
+ name: { type: 'string', description: 'Provider type: anthropic | openai | deepseek | gemini | qwen | zhipu | minimax | mimo | groq | xai | mistral | openrouter | moonshot | local' },
23
+ api_key: { type: 'string', description: 'Optional: API key. If not set, uses environment variable.' },
24
+ model: { type: 'string', description: 'Optional: model name. If not set, uses the provider\'s default model.' },
25
+ max_tokens: { type: 'number', description: 'Optional: max output tokens for this provider. If not set, auto-detected from model catalog or provider config.' },
26
+ },
27
+ required: ['name'],
28
+ },
29
+ async execute(args) {
30
+ try {
31
+ const name = args.name;
32
+ if (!name || typeof name !== 'string') {
33
+ return 'Error: provider name is required. Use list_providers to see available names.';
34
+ }
35
+ const apiKey = args.api_key || undefined;
36
+ const model = args.model || undefined;
37
+ // 带 key 的动态注册
38
+ if (apiKey) {
39
+ const { ProviderManager } = await import('../provider/manager.js');
40
+ const { getProviderConfigLoader } = await import('../provider/config.js');
41
+ const provCfg = getProviderConfigLoader().getProvider(name);
42
+ const maxTokens = args.max_tokens || undefined;
43
+ const config = {
44
+ type: name,
45
+ apiKey,
46
+ model: model ?? provCfg?.defaultModel ?? 'unknown',
47
+ baseUrl: provCfg?.baseUrl,
48
+ maxOutputTokens: maxTokens,
49
+ };
50
+ const manager = new ProviderManager(config);
51
+ const provider = manager.getProvider();
52
+ agentLoop.registerProvider?.(name, provider);
53
+ }
54
+ await agentLoop.switchProvider(name);
55
+ const after = agentLoop.getActiveProvider();
56
+ return `Provider switched to "${name}" (${after.getProviderType()}/${after.getModel()}).`;
57
+ }
58
+ catch (err) {
59
+ return `Error switching provider: ${err instanceof Error ? err.message : String(err)}`;
60
+ }
61
+ },
62
+ };
63
+ }
64
+ /**
65
+ * list_providers — list all registered providers with their types and models.
66
+ */
67
+ export function createListProvidersTool(providerRouter) {
68
+ return {
69
+ name: 'list_providers',
70
+ description: 'List all registered provider names, their types, and current models.',
71
+ inputSchema: { type: 'object', properties: {} },
72
+ async execute(_args) {
73
+ try {
74
+ const names = providerRouter.list();
75
+ if (names.length === 0) {
76
+ return 'No providers registered.';
77
+ }
78
+ const routingInfo = providerRouter.getRoutingInfo();
79
+ const lines = names.map((name) => {
80
+ const provider = providerRouter.get(name);
81
+ if (!provider)
82
+ return `- ${name}: [not found]`;
83
+ const isActive = name === routingInfo.providerName;
84
+ const displayModel = name === 'local' ? '(local backend)' : `${provider.getProviderType()} / ${provider.getModel()}`;
85
+ return `${isActive ? '* ' : ' '}${name}: ${displayModel}${isActive ? ' (active)' : ''}`;
86
+ });
87
+ lines.unshift(`Route mode: ${routingInfo.mode}`);
88
+ return lines.join('\n');
89
+ }
90
+ catch (err) {
91
+ return `Error listing providers: ${err instanceof Error ? err.message : String(err)}`;
92
+ }
93
+ },
94
+ };
95
+ }
96
+ /**
97
+ * provider_info — show details about the currently active provider.
98
+ */
99
+ export function createProviderInfoTool(agentLoop) {
100
+ return {
101
+ name: 'provider_info',
102
+ description: 'Get detailed information about the currently active provider (type, model, capabilities). Shows fallback status when the primary provider has failed over.',
103
+ inputSchema: { type: 'object', properties: {} },
104
+ async execute(_args) {
105
+ try {
106
+ const provider = agentLoop.getActiveProvider();
107
+ const type = provider.getProviderType();
108
+ const model = provider.getModel();
109
+ const caps = provider.getCapabilities ? provider.getCapabilities() : null;
110
+ const info = {
111
+ type,
112
+ model,
113
+ };
114
+ // Detect fallback chain
115
+ const chain = provider;
116
+ if (chain.isOnFallback) {
117
+ info.active_type = chain.getActiveType?.() ?? type;
118
+ info.active_model = chain.getActiveModel?.() ?? model;
119
+ info.on_fallback = true;
120
+ info.warning = 'Primary provider failed — currently running on a fallback provider. Check API key or quota for the primary.';
121
+ }
122
+ if (caps) {
123
+ info.capabilities = caps;
124
+ }
125
+ return JSON.stringify(info, null, 2);
126
+ }
127
+ catch (err) {
128
+ return `Error getting provider info: ${err instanceof Error ? err.message : String(err)}`;
129
+ }
130
+ },
131
+ };
132
+ }
133
+ /**
134
+ * switch_to_auto_route — switch provider routing back to automatic mode.
135
+ */
136
+ export function createSwitchToAutoRouteTool(agentLoop) {
137
+ return {
138
+ name: 'switch_to_auto_route',
139
+ description: 'Switch provider routing back to automatic mode. In auto mode, the system selects the best provider based on task complexity.',
140
+ inputSchema: { type: 'object', properties: {} },
141
+ async execute(_args) {
142
+ try {
143
+ agentLoop.switchToAutoRoute();
144
+ return 'Provider routing switched to auto mode. Use list_providers to see current routing state.';
145
+ }
146
+ catch (err) {
147
+ return `Error switching to auto route: ${err instanceof Error ? err.message : String(err)}`;
148
+ }
149
+ },
150
+ };
151
+ }
152
+ // ============================================================
153
+ // Registry control tools (6)
154
+ // ============================================================
155
+ /**
156
+ * toggle_tool — enable or disable a specific tool by name.
157
+ */
158
+ export function createToggleToolTool(toolRegistry, configCenter) {
159
+ return {
160
+ name: 'toggle_tool',
161
+ description: 'Enable or disable a specific tool. Disabled tools will not be available to the LLM.',
162
+ inputSchema: {
163
+ type: 'object',
164
+ properties: {
165
+ name: { type: 'string', description: 'Name of the tool to toggle' },
166
+ enabled: { type: 'boolean', description: 'true to enable, false to disable' },
167
+ },
168
+ required: ['name', 'enabled'],
169
+ },
170
+ async execute(args) {
171
+ const name = args.name;
172
+ const enabled = args.enabled;
173
+ if (!toolRegistry.has(name)) {
174
+ return `Error: tool "${name}" is not registered.`;
175
+ }
176
+ if (enabled) {
177
+ toolRegistry.enableTool(name);
178
+ }
179
+ else {
180
+ toolRegistry.disableTool(name);
181
+ }
182
+ // Persist disabled tools list
183
+ if (configCenter) {
184
+ const disabledNames = toolRegistry.getDisabled().map(t => t.name);
185
+ configCenter.set('tools.disabled', disabledNames);
186
+ await configCenter.save();
187
+ }
188
+ return `Tool "${name}" has been ${enabled ? 'enabled' : 'disabled'}.`;
189
+ },
190
+ };
191
+ }
192
+ /**
193
+ * list_tools — list all registered tools with their enabled/disabled status.
194
+ */
195
+ export function createListToolsTool(toolRegistry) {
196
+ return {
197
+ name: 'list_tools',
198
+ description: 'List all registered tools with their enabled/disabled status.',
199
+ inputSchema: {
200
+ type: 'object',
201
+ properties: {
202
+ includeDisabled: {
203
+ type: 'boolean',
204
+ description: 'When true, include disabled tools in the listing. Defaults to false.',
205
+ },
206
+ },
207
+ },
208
+ async execute(args) {
209
+ const includeDisabled = args.includeDisabled;
210
+ const all = includeDisabled
211
+ ? [...toolRegistry.getEnabled(), ...toolRegistry.getDisabled()]
212
+ : toolRegistry.getEnabled();
213
+ if (all.length === 0) {
214
+ return 'No tools registered.';
215
+ }
216
+ const disabledSet = new Set(toolRegistry.getDisabled().map((t) => t.name));
217
+ const lines = all.map((tool) => {
218
+ const enabled = !disabledSet.has(tool.name);
219
+ const status = enabled ? 'enabled' : 'disabled';
220
+ return `- ${tool.name} [${status}] (enabled: ${enabled}): ${tool.description}`;
221
+ });
222
+ return `Tools (${all.length} total):\n${lines.join('\n')}`;
223
+ },
224
+ };
225
+ }
226
+ /**
227
+ * toggle_skill — enable or disable a specific skill by name.
228
+ */
229
+ export function createToggleSkillTool(skillRegistry, configCenter) {
230
+ return {
231
+ name: 'toggle_skill',
232
+ description: 'Enable or disable a specific skill. Disabled skills will not be available to the LLM.',
233
+ inputSchema: {
234
+ type: 'object',
235
+ properties: {
236
+ name: { type: 'string', description: 'Name of the skill to toggle' },
237
+ enabled: { type: 'boolean', description: 'true to enable, false to disable' },
238
+ },
239
+ required: ['name', 'enabled'],
240
+ },
241
+ async execute(args) {
242
+ const name = args.name;
243
+ const enabled = args.enabled;
244
+ if (enabled) {
245
+ skillRegistry.enableSkill(name);
246
+ }
247
+ else {
248
+ skillRegistry.disableSkill(name);
249
+ }
250
+ // Persist disabled skills list
251
+ if (configCenter) {
252
+ const disabledNames = skillRegistry.getDisabled().map(s => s.name);
253
+ configCenter.set('skills.disabled', disabledNames);
254
+ await configCenter.save();
255
+ }
256
+ return `Skill "${name}" has been ${enabled ? 'enabled' : 'disabled'}.`;
257
+ },
258
+ };
259
+ }
260
+ /**
261
+ * list_skills — list all registered skills with their enabled/disabled status.
262
+ */
263
+ export function createListSkillsTool(skillRegistry) {
264
+ return {
265
+ name: 'list_skills',
266
+ description: 'List all registered skills with their enabled/disabled status.',
267
+ inputSchema: {
268
+ type: 'object',
269
+ properties: {
270
+ includeDisabled: {
271
+ type: 'boolean',
272
+ description: 'When true, include disabled skills in the listing. Defaults to false.',
273
+ },
274
+ },
275
+ },
276
+ async execute(args) {
277
+ const includeDisabled = args.includeDisabled;
278
+ const all = includeDisabled
279
+ ? [...skillRegistry.getEnabled(), ...skillRegistry.getDisabled()]
280
+ : skillRegistry.getEnabled();
281
+ if (all.length === 0) {
282
+ return 'No skills registered.';
283
+ }
284
+ const disabledSet = new Set(skillRegistry.getDisabled().map((s) => s.name));
285
+ const lines = all.map((skill) => {
286
+ const enabled = !disabledSet.has(skill.name);
287
+ const status = enabled ? 'enabled' : 'disabled';
288
+ return `- ${skill.name} [${status}] (enabled: ${enabled}): ${skill.description}`;
289
+ });
290
+ return `Skills (${all.length} total):\n${lines.join('\n')}`;
291
+ },
292
+ };
293
+ }
294
+ /**
295
+ * toggle_sub_agent — enable or disable a specific sub-agent by name.
296
+ */
297
+ export function createToggleSubAgentTool(agentRegistry, configCenter) {
298
+ return {
299
+ name: 'toggle_sub_agent',
300
+ description: 'Enable or disable a specific sub-agent. Disabled sub-agents will not be available for delegation.',
301
+ inputSchema: {
302
+ type: 'object',
303
+ properties: {
304
+ name: { type: 'string', description: 'Name of the sub-agent to toggle' },
305
+ enabled: { type: 'boolean', description: 'true to enable, false to disable' },
306
+ },
307
+ required: ['name', 'enabled'],
308
+ },
309
+ async execute(args) {
310
+ const name = args.name;
311
+ const enabled = args.enabled;
312
+ if (enabled) {
313
+ agentRegistry.enableAgent(name);
314
+ }
315
+ else {
316
+ agentRegistry.disableAgent(name);
317
+ }
318
+ // Persist disabled agents list
319
+ if (configCenter) {
320
+ const disabledNames = agentRegistry.getDisabled().map(a => a.name);
321
+ configCenter.set('agents.disabled', disabledNames);
322
+ await configCenter.save();
323
+ }
324
+ return `Sub-agent "${name}" has been ${enabled ? 'enabled' : 'disabled'}.`;
325
+ },
326
+ };
327
+ }
328
+ /**
329
+ * list_sub_agents — list all registered sub-agents with their enabled/disabled status and instance IDs.
330
+ */
331
+ export function createListSubAgentsTool(agentRegistry) {
332
+ return {
333
+ name: 'list_sub_agents',
334
+ description: 'List all registered sub-agents with their enabled/disabled status and instance IDs. Use instance_id to target a specific spawned copy.',
335
+ inputSchema: { type: 'object', properties: {} },
336
+ async execute(_args) {
337
+ const all = [...agentRegistry.getEnabled(), ...agentRegistry.getDisabled()];
338
+ if (all.length === 0) {
339
+ return 'No sub-agents registered.';
340
+ }
341
+ const disabledSet = new Set(agentRegistry.getDisabled().map((a) => a.instanceId));
342
+ // Group by name to show multiple instances
343
+ const grouped = new Map();
344
+ for (const agent of all) {
345
+ const list = grouped.get(agent.name) || [];
346
+ list.push(agent);
347
+ grouped.set(agent.name, list);
348
+ }
349
+ const lines = [`Sub-agents (${all.length} total, ${grouped.size} types):`];
350
+ for (const [name, instances] of grouped) {
351
+ const isMulti = instances.length > 1;
352
+ for (const agent of instances) {
353
+ const enabled = !disabledSet.has(agent.instanceId);
354
+ const status = enabled ? 'enabled' : 'disabled';
355
+ const idTag = isMulti ? ` [id: ${agent.instanceId}]` : '';
356
+ lines.push(`- ${agent.name}${idTag} [${status}] (enabled: ${enabled}): ${agent.description} (mode: ${agent.collaborationMode}, maxTurns: ${agent.maxTurns})`);
357
+ }
358
+ }
359
+ return lines.join('\n');
360
+ },
361
+ };
362
+ }
363
+ /**
364
+ * spawn_sub_agent — clone an existing sub-agent to create a parallel instance (分身).
365
+ */
366
+ export function createSpawnSubAgentTool(agentRegistry) {
367
+ return {
368
+ name: 'spawn_sub_agent',
369
+ description: 'Clone an existing sub-agent for parallel execution on different tasks.',
370
+ inputSchema: {
371
+ type: 'object',
372
+ properties: {
373
+ name: { type: 'string', description: 'Name of the existing sub-agent to clone (e.g. "code-reviewer")' },
374
+ customName: { type: 'string', description: 'Optional alias for the new instance (e.g. "code-reviewer-frontend"). If not provided, uses the original name.' },
375
+ },
376
+ required: ['name'],
377
+ },
378
+ async execute(args) {
379
+ const name = args.name;
380
+ const customName = args.customName;
381
+ try {
382
+ const spawned = agentRegistry.spawnInstance(name, customName);
383
+ if (!spawned) {
384
+ const available = agentRegistry.getAll().map((a) => a.name).join(', ');
385
+ return `Error: No sub-agent named "${name}" found. Available: ${available}`;
386
+ }
387
+ return `New instance created:\n- Name: ${spawned.name}\n- Instance ID: ${spawned.instanceId}\n- Description: ${spawned.description}\n- Mode: ${spawned.collaborationMode}\n- Max Turns: ${spawned.maxTurns}\n\nUse this instance_id with delegate_to_agent to target this specific copy.`;
388
+ }
389
+ catch (err) {
390
+ return `Error spawning sub-agent: ${err instanceof Error ? err.message : String(err)}`;
391
+ }
392
+ },
393
+ };
394
+ }
395
+ /**
396
+ * create_sub_agent — create a brand-new custom sub-agent at runtime.
397
+ * When persist=true, also writes prompt file and agents.json config to disk
398
+ * so the agent survives restarts and rebuilds.
399
+ */
400
+ export function createCreateSubAgentTool(agentRegistry, cwd) {
401
+ return {
402
+ name: 'create_sub_agent',
403
+ description: 'Create a custom sub-agent at runtime. Set persist=true to save to disk.',
404
+ inputSchema: {
405
+ type: 'object',
406
+ properties: {
407
+ name: { type: 'string', description: 'Unique name for the new sub-agent (e.g. "doc-reviewer")' },
408
+ description: { type: 'string', description: 'Role description — helps the orchestrator decide when to use this agent.' },
409
+ systemPrompt: { type: 'string', description: 'Full system prompt defining the sub-agent\'s behavior, expertise, and constraints. Use {{task}} as a placeholder for the delegated task.' },
410
+ allowedTools: {
411
+ type: 'array',
412
+ items: { type: 'string' },
413
+ description: 'Tool whitelist for this agent. Default: ["read", "glob", "grep", "write"]. Use ["self"] for all tools.',
414
+ },
415
+ maxTurns: { type: 'number', description: 'Maximum execution turns. Default: 10.' },
416
+ collaborationMode: {
417
+ type: 'string',
418
+ enum: ['delegate', 'adversarial', 'parallel'],
419
+ description: 'Collaboration mode. Default: "delegate".',
420
+ },
421
+ persist: {
422
+ type: 'boolean',
423
+ description: 'When true, also write prompt file and config to disk so the agent persists after restart. Default: false (memory only).',
424
+ },
425
+ session_ttl_minutes: {
426
+ type: 'number',
427
+ description: 'Sub-agent session TTL in minutes. Session auto-cleans if unused for this duration. Default: 10.',
428
+ },
429
+ },
430
+ required: ['name', 'description', 'systemPrompt'],
431
+ },
432
+ async execute(args) {
433
+ const name = args.name;
434
+ const description = args.description;
435
+ const systemPrompt = args.systemPrompt;
436
+ const allowedTools = args.allowedTools ?? ['read', 'glob', 'grep', 'write'];
437
+ const maxTurns = args.maxTurns ?? 10;
438
+ const collaborationMode = args.collaborationMode ?? 'delegate';
439
+ const persist = args.persist === true;
440
+ const sessionTtlMinutes = args.session_ttl_minutes ?? 10;
441
+ // Validate mode
442
+ const validModes = ['delegate', 'adversarial', 'parallel'];
443
+ if (!validModes.includes(collaborationMode)) {
444
+ return `Error: invalid collaborationMode "${collaborationMode}". Must be one of: ${validModes.join(', ')}`;
445
+ }
446
+ try {
447
+ const def = agentRegistry.register({
448
+ name,
449
+ description,
450
+ systemPrompt,
451
+ allowedTools,
452
+ maxTurns,
453
+ collaborationMode,
454
+ sessionTtlMinutes,
455
+ });
456
+ const lines = [
457
+ `Sub-agent created successfully:`,
458
+ `- Name: ${def.name}`,
459
+ `- Instance ID: ${def.instanceId}`,
460
+ `- Description: ${def.description}`,
461
+ `- Mode: ${def.collaborationMode}`,
462
+ `- Allowed Tools: ${def.allowedTools.join(', ')}`,
463
+ `- Max Turns: ${def.maxTurns}`,
464
+ ``,
465
+ `Use delegate_to_agent with agent_name="${def.name}" or instance_id="${def.instanceId}" to invoke this agent.`,
466
+ ];
467
+ if (persist) {
468
+ await persistSubAgent(cwd, name, systemPrompt, allowedTools, maxTurns, collaborationMode, sessionTtlMinutes);
469
+ lines.push(`- Persisted to disk: yes (prompts/agents/${name}.md + .agent/agents.json)`);
470
+ }
471
+ else {
472
+ lines.push(`- Persisted to disk: no (memory only, use persist=true to save permanently)`);
473
+ }
474
+ return lines.join('\n');
475
+ }
476
+ catch (err) {
477
+ return `Error creating sub-agent: ${err instanceof Error ? err.message : String(err)}`;
478
+ }
479
+ },
480
+ };
481
+ }
482
+ /**
483
+ * update_sub_agent — modify an existing sub-agent's configuration at runtime.
484
+ */
485
+ export function createUpdateSubAgentTool(agentRegistry) {
486
+ return {
487
+ name: 'update_sub_agent',
488
+ description: 'Modify an existing sub-agent\'s configuration at runtime. Updates are applied immediately (in-memory). Use instance_id to target a specific instance. Fields left unspecified remain unchanged. Note: name cannot be changed — destroy and re-create if you need to rename.',
489
+ inputSchema: {
490
+ type: 'object',
491
+ properties: {
492
+ instance_id: { type: 'string', description: 'Instance ID of the sub-agent to update (required).' },
493
+ description: { type: 'string', description: 'Updated role description.' },
494
+ systemPrompt: { type: 'string', description: 'Updated system prompt. Use {{task}} as placeholder.' },
495
+ allowedTools: {
496
+ type: 'array',
497
+ items: { type: 'string' },
498
+ description: 'Updated tool whitelist.',
499
+ },
500
+ maxTurns: { type: 'number', description: 'Updated max execution turns.' },
501
+ collaborationMode: {
502
+ type: 'string',
503
+ enum: ['delegate', 'adversarial', 'parallel'],
504
+ description: 'Updated collaboration mode.',
505
+ },
506
+ session_ttl_minutes: { type: 'number', description: 'Updated session TTL in minutes.' },
507
+ },
508
+ required: ['instance_id'],
509
+ },
510
+ async execute(args) {
511
+ const instanceId = args.instance_id;
512
+ const partial = {};
513
+ if (args.description !== undefined)
514
+ partial.description = args.description;
515
+ if (args.systemPrompt !== undefined)
516
+ partial.systemPrompt = args.systemPrompt;
517
+ if (args.allowedTools !== undefined)
518
+ partial.allowedTools = args.allowedTools;
519
+ if (args.maxTurns !== undefined)
520
+ partial.maxTurns = args.maxTurns;
521
+ if (args.session_ttl_minutes !== undefined)
522
+ partial.sessionTtlMinutes = args.session_ttl_minutes;
523
+ if (args.collaborationMode !== undefined) {
524
+ const mode = args.collaborationMode;
525
+ const validModes = ['delegate', 'adversarial', 'parallel'];
526
+ if (!validModes.includes(mode)) {
527
+ return `Error: invalid collaborationMode "${mode}". Must be one of: ${validModes.join(', ')}`;
528
+ }
529
+ partial.collaborationMode = mode;
530
+ }
531
+ if (Object.keys(partial).length === 0) {
532
+ return 'Error: no fields to update. Specify at least one field to modify.';
533
+ }
534
+ const updated = agentRegistry.update(instanceId, partial);
535
+ if (!updated) {
536
+ return `Error: sub-agent instance "${instanceId}" not found. Use list_sub_agents to see available instances with their IDs.`;
537
+ }
538
+ const lines = [
539
+ `Sub-agent updated successfully:`,
540
+ `- Instance ID: ${updated.instanceId}`,
541
+ `- Name: ${updated.name}`,
542
+ `- Description: ${updated.description}`,
543
+ `- Mode: ${updated.collaborationMode}`,
544
+ `- Allowed Tools: ${updated.allowedTools.join(', ')}`,
545
+ `- Max Turns: ${updated.maxTurns}`,
546
+ ];
547
+ return lines.join('\n');
548
+ },
549
+ };
550
+ }
551
+ /**
552
+ * destroy_sub_agent — permanently delete a sub-agent instance and its session directory.
553
+ */
554
+ export function createDestroySubAgentTool(agentRegistry, parentSessionDir) {
555
+ return {
556
+ name: 'destroy_sub_agent',
557
+ description: 'Delete a sub-agent instance and its session data. For spawned clones, removes only that clone.',
558
+ inputSchema: {
559
+ type: 'object',
560
+ properties: {
561
+ instance_id: { type: 'string', description: 'Instance ID of the sub-agent to destroy (required).' },
562
+ },
563
+ required: ['instance_id'],
564
+ },
565
+ async execute(args) {
566
+ const instanceId = args.instance_id;
567
+ const def = agentRegistry.getByInstanceId(instanceId);
568
+ if (!def) {
569
+ return `Error: sub-agent instance "${instanceId}" not found.`;
570
+ }
571
+ // 先销毁 session 目录
572
+ const { destroySubAgentSession } = await import('../agents/delegate-tool.js');
573
+ const sessionCleaned = await destroySubAgentSession(parentSessionDir, instanceId);
574
+ // 再从注册表移除
575
+ agentRegistry.destroyInstance(instanceId);
576
+ const lines = [
577
+ `Sub-agent "${def.name}" (instance: ${instanceId}) destroyed.`,
578
+ sessionCleaned ? '- Session data cleaned' : '- Session directory not found (already cleaned or never created)',
579
+ ];
580
+ return lines.join('\n');
581
+ },
582
+ };
583
+ }
584
+ /**
585
+ * Persist a sub-agent definition to disk:
586
+ * 1. Write prompt file to src/prompts/agents/{name}.md
587
+ * 2. Write prompt file to dist/prompts/agents/{name}.md (immediate runtime access)
588
+ * 3. Update project-level .agent/agents.json
589
+ */
590
+ async function persistSubAgent(cwd, name, systemPrompt, allowedTools, maxTurns, collaborationMode, sessionTtlMinutes) {
591
+ const fs = await import('node:fs/promises');
592
+ const path = await import('node:path');
593
+ const srcPromptDir = path.join(cwd, 'src', 'prompts', 'agents');
594
+ const distPromptDir = path.join(cwd, 'dist', 'prompts', 'agents');
595
+ // 1. Write src/prompts/agents/{name}.md
596
+ await fs.mkdir(srcPromptDir, { recursive: true });
597
+ await fs.writeFile(path.join(srcPromptDir, `${name}.md`), systemPrompt, 'utf-8');
598
+ // 2. Write dist/prompts/agents/{name}.md (immediate runtime readiness)
599
+ try {
600
+ await fs.mkdir(distPromptDir, { recursive: true });
601
+ await fs.writeFile(path.join(distPromptDir, `${name}.md`), systemPrompt, 'utf-8');
602
+ }
603
+ catch {
604
+ // dist directory may not exist (e.g., dev mode); ignore
605
+ }
606
+ // 3. Update project-level .agent/agents.json
607
+ const agentConfigPath = path.join(cwd, '.agent', 'agents.json');
608
+ let existing = { agents: [] };
609
+ try {
610
+ const content = await fs.readFile(agentConfigPath, 'utf-8');
611
+ existing = JSON.parse(content);
612
+ if (!Array.isArray(existing.agents))
613
+ existing.agents = [];
614
+ }
615
+ catch {
616
+ // file does not exist or invalid json, start fresh
617
+ }
618
+ // Remove existing entry with same name (to support re-registration)
619
+ existing.agents = existing.agents.filter((a) => a.name !== name);
620
+ const entry = {
621
+ name,
622
+ description: `Custom sub-agent created at runtime`,
623
+ promptFile: `agents/${name}`,
624
+ allowedTools,
625
+ maxTurns,
626
+ collaborationMode,
627
+ };
628
+ if (sessionTtlMinutes !== undefined)
629
+ entry.sessionTtlMinutes = sessionTtlMinutes;
630
+ existing.agents.push(entry);
631
+ await fs.mkdir(path.dirname(agentConfigPath), { recursive: true });
632
+ await fs.writeFile(agentConfigPath, JSON.stringify(existing, null, 2), 'utf-8');
633
+ }
634
+ // ============================================================
635
+ // Session / Training control tools (5)
636
+ // ============================================================
637
+ /**
638
+ * interrupt — interrupt the currently running agent loop.
639
+ */
640
+ export function createInterruptTool(agentLoop) {
641
+ return {
642
+ name: 'interrupt',
643
+ description: 'Interrupt the currently running agent execution. Stops the agent and any in-progress provider requests. Use instance_id to target a specific running sub-agent.',
644
+ inputSchema: {
645
+ type: 'object',
646
+ properties: {
647
+ instance_id: {
648
+ type: 'string',
649
+ description: 'Optional instance ID of a running sub-agent to interrupt. If omitted, interrupts the main agent.',
650
+ },
651
+ },
652
+ },
653
+ async execute(args) {
654
+ try {
655
+ const instanceId = args.instance_id;
656
+ if (instanceId) {
657
+ // 动态 import 避免循环依赖:runtime-control → delegate-tool → filtered-registry → tool.registry → runtime-control
658
+ const { interruptSubAgentLoop } = await import('../agents/delegate-tool.js');
659
+ const ok = interruptSubAgentLoop(instanceId);
660
+ return ok
661
+ ? `Sub-agent "${instanceId}" interrupted.`
662
+ : `No running sub-agent found with instance ID "${instanceId}". Use list_sub_agents to see available instances.`;
663
+ }
664
+ agentLoop.interrupt();
665
+ return 'Agent execution interrupted.';
666
+ }
667
+ catch (err) {
668
+ return `Error interrupting agent: ${err instanceof Error ? err.message : String(err)}`;
669
+ }
670
+ },
671
+ };
672
+ }
673
+ /**
674
+ * current_session — show which session is currently active.
675
+ */
676
+ export function createCurrentSessionTool(agentLoop) {
677
+ return {
678
+ name: 'current_session',
679
+ description: 'Show the currently active session identity: ID, type, channel, and creation time. ' +
680
+ 'Use this when you need to know which session you are running in before switching or listing sessions.',
681
+ inputSchema: { type: 'object', properties: {} },
682
+ async execute(_args) {
683
+ try {
684
+ const path = await import('node:path');
685
+ const fs = await import('node:fs');
686
+ const sessionDir = agentLoop.sessionDir;
687
+ const sessionId = path.basename(sessionDir);
688
+ // 读取 meta.json 获取 session 元信息
689
+ let type = 'unknown';
690
+ let channel;
691
+ let createdAt = 'unknown';
692
+ try {
693
+ const metaPath = path.join(sessionDir, 'meta.json');
694
+ if (fs.existsSync(metaPath)) {
695
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
696
+ type = meta.type ?? 'unknown';
697
+ channel = meta.channel;
698
+ createdAt = meta.createdAt ?? 'unknown';
699
+ }
700
+ }
701
+ catch { /* meta.json may not exist for legacy sessions */ }
702
+ const lines = [
703
+ `Current session:`,
704
+ `- ID: ${sessionId}`,
705
+ `- Type: ${type}`,
706
+ `- Channel: ${channel ?? '(none)'}`,
707
+ `- Created: ${createdAt}`,
708
+ `- Directory: ${sessionDir}`,
709
+ ];
710
+ if (channel) {
711
+ lines.push(`\nThis session is bound to the "${channel}" channel. Use new_session to create a fresh session if needed.`);
712
+ }
713
+ return lines.join('\n');
714
+ }
715
+ catch (err) {
716
+ return `Error getting current session: ${err instanceof Error ? err.message : String(err)}`;
717
+ }
718
+ },
719
+ };
720
+ }
721
+ /**
722
+ * session_stats — show current session statistics.
723
+ */
724
+ export function createSessionStatsTool(agentLoop) {
725
+ return {
726
+ name: 'session_stats',
727
+ description: 'Show current session statistics including turn count, token usage, compression count, and context info.',
728
+ inputSchema: { type: 'object', properties: {} },
729
+ async execute(_args) {
730
+ try {
731
+ // getTurnInfo requires current turnCount and tokensUsed.
732
+ // Since we don't track these externally, use 0 as fallback.
733
+ const info = agentLoop.getTurnInfo(0, 0);
734
+ return JSON.stringify(info, null, 2);
735
+ }
736
+ catch (err) {
737
+ return `Error getting session stats: ${err instanceof Error ? err.message : String(err)}`;
738
+ }
739
+ },
740
+ };
741
+ }
742
+ // ── Session management tools (4) ──
743
+ /**
744
+ * list_sessions — list all existing sessions.
745
+ */
746
+ export function createListSessionsTool(agentLoop, cwd) {
747
+ return {
748
+ name: 'list_sessions',
749
+ description: 'List all existing sessions with their creation time, type, and channel. The currently active session is marked with ← current.',
750
+ inputSchema: { type: 'object', properties: {} },
751
+ async execute(_args) {
752
+ try {
753
+ const path = await import('node:path');
754
+ const { SessionManager } = await import('../memory/session.js');
755
+ const sm = new SessionManager(cwd);
756
+ const sessions = await sm.list();
757
+ if (sessions.length === 0) {
758
+ return 'No sessions found. Use new_session to create one.';
759
+ }
760
+ const currentSessionId = path.basename(agentLoop.sessionDir);
761
+ const lines = sessions.map((s) => {
762
+ const typeLabel = s.type ?? 'normal';
763
+ const channelLabel = s.channel ? ` [${s.channel}]` : '';
764
+ const isCurrent = s.id === currentSessionId;
765
+ const marker = isCurrent ? ' ← current' : '';
766
+ return (`${s.id}` +
767
+ ` | created: ${s.createdAt}` +
768
+ ` | updated: ${s.updatedAt}` +
769
+ ` | type: ${typeLabel}${channelLabel}${marker}`);
770
+ });
771
+ return (`Sessions (${sessions.length} total, newest first):\n` +
772
+ lines.map((l) => ` ${l}`).join('\n') +
773
+ `\n\nUse switch_session to load a session, delete_session to remove one. ` +
774
+ `⚠ The session marked "← current" is active and CANNOT be deleted.`);
775
+ }
776
+ catch (err) {
777
+ return `Error listing sessions: ${err instanceof Error ? err.message : String(err)}`;
778
+ }
779
+ },
780
+ };
781
+ }
782
+ /**
783
+ * new_session — create a brand-new session and switch to it immediately.
784
+ */
785
+ export function createNewSessionTool(agentLoop, cwd) {
786
+ return {
787
+ name: 'new_session',
788
+ description: 'Create a new session and switch to it immediately. ' +
789
+ 'The current conversation context will be cleared. ' +
790
+ 'Optionally specify a channel to generate a channel-prefixed session ID (e.g. "tui", "feishu", "webui").',
791
+ inputSchema: {
792
+ type: 'object',
793
+ properties: {
794
+ channel: {
795
+ type: 'string',
796
+ description: 'Optional channel name for the session ID prefix. Auto-detected from current session if omitted.',
797
+ },
798
+ type: {
799
+ type: 'string',
800
+ enum: ['normal', 'precise'],
801
+ description: 'Session type. "precise" enables precise mode with keyword-based context filtering. Default: "normal".',
802
+ },
803
+ },
804
+ },
805
+ async execute(args) {
806
+ try {
807
+ const { SessionManager } = await import('../memory/session.js');
808
+ const sm = new SessionManager(cwd);
809
+ const sessionType = args.type ?? 'normal';
810
+ let channel = args.channel;
811
+ const path = await import('node:path');
812
+ const fs = await import('node:fs');
813
+ // 自动检测当前 session 的渠道(飞书 → feishu, TUI → tui, WebUI → webui)
814
+ if (!channel) {
815
+ try {
816
+ const currentSessionDir = agentLoop.sessionDir;
817
+ const metaPath = path.join(currentSessionDir, 'meta.json');
818
+ if (fs.existsSync(metaPath)) {
819
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
820
+ if (typeof meta.channel === 'string' && meta.channel.length > 0) {
821
+ channel = meta.channel;
822
+ }
823
+ }
824
+ }
825
+ catch { /* 读取失败不阻塞 */ }
826
+ }
827
+ const session = await sm.create(sessionType, channel);
828
+ const sessionDir = sm.getSessionDir(session.id);
829
+ await agentLoop.switchSession(sessionDir);
830
+ return (`New session created and activated:\n` +
831
+ `- ID: ${session.id}\n` +
832
+ `- Type: ${session.type}\n` +
833
+ `- Channel: ${channel ?? '(auto: none detected)'}\n` +
834
+ `- Created: ${session.createdAt}`);
835
+ }
836
+ catch (err) {
837
+ return `Error creating new session: ${err instanceof Error ? err.message : String(err)}`;
838
+ }
839
+ },
840
+ };
841
+ }
842
+ /**
843
+ * switch_session — load and switch to an existing session.
844
+ *
845
+ * 注册链路:
846
+ * src/tools/runtime-control.ts(本文件)→ 工具实现
847
+ * → 注册到 ToolRegistry(factory.ts 中通过 registerSessionTools 调用)
848
+ * → tool_result 写入 conversation.jsonl(loop.ts append 阶段)
849
+ * → 出现在 Zone 3 (History) 的对话历史中
850
+ *
851
+ * ⚠️ tool_result 的文案会影响模型对当前会话状态的认知。
852
+ * 如需修改返回文案,注意与 new_session、current_session 保持一致。
853
+ */
854
+ export function createSwitchSessionTool(agentLoop, cwd) {
855
+ return {
856
+ name: 'switch_session',
857
+ description: 'Switch to an existing session by its ID. ' +
858
+ 'The current conversation context will be replaced by the target session\'s history. ' +
859
+ 'Use list_sessions to see available session IDs.',
860
+ inputSchema: {
861
+ type: 'object',
862
+ properties: {
863
+ session_id: {
864
+ type: 'string',
865
+ description: 'The session ID to switch to (e.g. "tui-20260627-120000-abcd"). Use list_sessions to find IDs.',
866
+ },
867
+ },
868
+ required: ['session_id'],
869
+ },
870
+ async execute(args) {
871
+ try {
872
+ const sessionId = args.session_id;
873
+ const { SessionManager } = await import('../memory/session.js');
874
+ const sm = new SessionManager(cwd);
875
+ const sessionDir = sm.getSessionDir(sessionId);
876
+ // Verify the session directory exists
877
+ const fsPromises = await import('node:fs/promises');
878
+ try {
879
+ await fsPromises.access(sessionDir);
880
+ }
881
+ catch {
882
+ const sessions = await sm.list();
883
+ const ids = sessions.map((s) => s.id).join(', ');
884
+ return `Error: Session "${sessionId}" not found. Available sessions: ${ids || '(none)'}`;
885
+ }
886
+ await agentLoop.switchSession(sessionDir);
887
+ return `当前会话为 ${sessionId}`;
888
+ }
889
+ catch (err) {
890
+ return `Error switching session: ${err instanceof Error ? err.message : String(err)}`;
891
+ }
892
+ },
893
+ };
894
+ }
895
+ /**
896
+ * delete_session — permanently delete a session and its data.
897
+ */
898
+ export function createDeleteSessionTool(agentLoop, cwd) {
899
+ return {
900
+ name: 'delete_session',
901
+ description: 'Permanently delete a session and all its conversation data. ' +
902
+ 'This cannot be undone. The currently active session CANNOT be deleted — switch to another session first.',
903
+ inputSchema: {
904
+ type: 'object',
905
+ properties: {
906
+ session_id: {
907
+ type: 'string',
908
+ description: 'The session ID to delete. Use list_sessions to find IDs. Cannot be the currently active session.',
909
+ },
910
+ confirm: {
911
+ type: 'boolean',
912
+ description: 'Must be explicitly set to true to confirm deletion.',
913
+ },
914
+ },
915
+ required: ['session_id'],
916
+ },
917
+ async execute(args) {
918
+ try {
919
+ const path = await import('node:path');
920
+ const sessionId = args.session_id;
921
+ const confirm = args.confirm;
922
+ // 保护当前活跃 session
923
+ const currentSessionId = path.basename(agentLoop.sessionDir);
924
+ if (sessionId === currentSessionId) {
925
+ return (`⚠ Cannot delete the currently active session "${sessionId}". ` +
926
+ `Use switch_session to switch to a different session first, then retry deletion.`);
927
+ }
928
+ if (confirm !== true) {
929
+ return (`⚠ This will permanently delete session "${sessionId}" and all its data. ` +
930
+ `This cannot be undone.\n` +
931
+ `To confirm, call delete_session again with session_id="${sessionId}" and confirm=true.`);
932
+ }
933
+ const { SessionManager } = await import('../memory/session.js');
934
+ const sm = new SessionManager(cwd);
935
+ const sessionDir = sm.getSessionDir(sessionId);
936
+ const fsPromises = await import('node:fs/promises');
937
+ try {
938
+ await fsPromises.access(sessionDir);
939
+ }
940
+ catch {
941
+ return `Session "${sessionId}" not found (may have been already deleted).`;
942
+ }
943
+ await fsPromises.rm(sessionDir, { recursive: true, force: true });
944
+ return `Session "${sessionId}" deleted.`;
945
+ }
946
+ catch (err) {
947
+ return `Error deleting session: ${err instanceof Error ? err.message : String(err)}`;
948
+ }
949
+ },
950
+ };
951
+ }
952
+ // ── Permission whitelist management tools ──
953
+ /**
954
+ * allow_tool — add a tool to the safety allowedTools whitelist.
955
+ */
956
+ export function createAllowToolTool(configCenter) {
957
+ return {
958
+ name: 'allow_tool',
959
+ description: 'Add a tool to the safety allowedTools whitelist so it no longer requires confirmation. Works even for tools in the dangerousTools list.',
960
+ inputSchema: {
961
+ type: 'object',
962
+ properties: {
963
+ name: { type: 'string', description: 'Tool name to whitelist (e.g. "write", "bash")' },
964
+ },
965
+ required: ['name'],
966
+ },
967
+ async execute(args) {
968
+ const toolName = args.name;
969
+ if (!toolName)
970
+ return 'Error: tool name is required.';
971
+ try {
972
+ const current = configCenter.get('safety.allowedTools') || [];
973
+ if (current.includes(toolName)) {
974
+ return `Tool "${toolName}" is already in the allowlist.`;
975
+ }
976
+ current.push(toolName);
977
+ configCenter.set('safety.allowedTools', current);
978
+ await configCenter.save();
979
+ return `Tool "${toolName}" added to allowlist. It will no longer require confirmation (effective immediately).`;
980
+ }
981
+ catch (err) {
982
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
983
+ }
984
+ },
985
+ };
986
+ }
987
+ /**
988
+ * disallow_tool — remove a tool from the safety allowedTools whitelist.
989
+ */
990
+ export function createDisallowToolTool(configCenter) {
991
+ return {
992
+ name: 'disallow_tool',
993
+ description: 'Remove a tool from the safety allowedTools whitelist. It will require confirmation again if in dangerousTools.',
994
+ inputSchema: {
995
+ type: 'object',
996
+ properties: {
997
+ name: { type: 'string', description: 'Tool name to remove from the whitelist' },
998
+ },
999
+ required: ['name'],
1000
+ },
1001
+ async execute(args) {
1002
+ const toolName = args.name;
1003
+ if (!toolName)
1004
+ return 'Error: tool name is required.';
1005
+ try {
1006
+ const current = configCenter.get('safety.allowedTools') || [];
1007
+ const idx = current.indexOf(toolName);
1008
+ if (idx === -1) {
1009
+ return `Tool "${toolName}" is not in the allowlist.`;
1010
+ }
1011
+ current.splice(idx, 1);
1012
+ configCenter.set('safety.allowedTools', current);
1013
+ await configCenter.save();
1014
+ return `Tool "${toolName}" removed from allowlist. It will require confirmation again if it is in the dangerousTools list.`;
1015
+ }
1016
+ catch (err) {
1017
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
1018
+ }
1019
+ },
1020
+ };
1021
+ }
1022
+ /**
1023
+ * list_allowlist — show current allowed tools and commands.
1024
+ */
1025
+ export function createListAllowlistTool(configCenter) {
1026
+ return {
1027
+ name: 'list_allowlist',
1028
+ description: 'Show the current safety whitelist: allowedTools (tools that skip confirmation) and allowedCommands (bash command patterns that skip confirmation).',
1029
+ inputSchema: { type: 'object', properties: {} },
1030
+ async execute(_args) {
1031
+ try {
1032
+ const tools = configCenter.get('safety.allowedTools') || [];
1033
+ const commands = configCenter.get('safety.allowedCommands') || [];
1034
+ const lines = ['=== Safety Whitelist ==='];
1035
+ lines.push(`\nAllowed Tools (skip confirmation):`);
1036
+ if (tools.length === 0) {
1037
+ lines.push(' (none)');
1038
+ }
1039
+ else {
1040
+ tools.forEach(t => lines.push(` - ${t}`));
1041
+ }
1042
+ lines.push(`\nAllowed Commands (bash patterns, * = wildcard):`);
1043
+ if (commands.length === 0) {
1044
+ lines.push(' (none)');
1045
+ }
1046
+ else {
1047
+ commands.forEach(c => lines.push(` - ${c}`));
1048
+ }
1049
+ lines.push(`\nUse allow_tool / disallow_tool to manage tools.`);
1050
+ lines.push(`Use agent config set safety.allowedCommands.+ "<pattern>" to add command patterns.`);
1051
+ return lines.join('\n');
1052
+ }
1053
+ catch (err) {
1054
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
1055
+ }
1056
+ },
1057
+ };
1058
+ }
1059
+ // ── Schedule task management tools ──
1060
+ /**
1061
+ * add_task — create a new scheduled task on the HeartbeatScheduler.
1062
+ */
1063
+ export function createAddTaskTool(scheduler,
1064
+ /** 可选:自动检测当前渠道的函数(从 session meta.json 读取) */
1065
+ getChannel,
1066
+ /** 可选:自动检测当前 sessionId 的函数 */
1067
+ getSessionId,
1068
+ /** 可选:自动检测当前模式的函数(normal / companion) */
1069
+ getMode) {
1070
+ return {
1071
+ name: 'add_task',
1072
+ description: 'Create a new scheduled task. Supports 5 schedule types:\n' +
1073
+ '- "interval": fixed interval, e.g. every 5min (intervalMs: 300000)\n' +
1074
+ '- "cron": standard 5-field cron, e.g. "0 3 * * *" (daily at 3am)\n' +
1075
+ '- "daily": fixed time each day, e.g. { time: "09:30" }\n' +
1076
+ '- "fixed-time": one-shot at a specific ISO time\n' +
1077
+ '- "random": N random triggers per period, with optional time window, variable count range, and probability weights. e.g. 0-5 times/day between 9am-6pm, noon 3x more likely ({ periodMs: 86400000, countRange: { min: 0, max: 5, distribution: "extremes" }, timeWindow: { start: "09:00", end: "18:00" }, timeWeights: [{ time: "12:00", weight: 3.0 }], minIntervalMs: 300000 })',
1078
+ companionDescription: '得记住他刚才说的东西,到时候叫他。',
1079
+ inputSchema: {
1080
+ type: 'object',
1081
+ properties: {
1082
+ name: { type: 'string', description: 'Human-readable task name.' },
1083
+ scheduleType: {
1084
+ type: 'string',
1085
+ enum: ['interval', 'cron', 'daily', 'fixed-time', 'random'],
1086
+ description: 'Scheduling strategy.',
1087
+ },
1088
+ schedule: {
1089
+ type: 'object',
1090
+ description: 'Schedule config matching the chosen type. Examples:\n' +
1091
+ ' interval: { intervalMs: 300000 }\n' +
1092
+ ' cron: { expression: "0 */2 * * *" }\n' +
1093
+ ' daily: { time: "09:00" }\n' +
1094
+ ' fixed-time: { runAt: "2026-06-01T12:00:00.000Z" }\n' +
1095
+ ' random: { periodMs: 86400000, count: 10, minIntervalMs: 300000, ' +
1096
+ 'timeWindow: { start: "09:00", end: "18:00" }, ' +
1097
+ 'countRange: { min: 0, max: 5, distribution: "extremes" }, ' +
1098
+ 'timeWeights: [{ time: "12:00", weight: 3.0 }] }',
1099
+ },
1100
+ tags: {
1101
+ type: 'array',
1102
+ items: { type: 'string' },
1103
+ description: 'Optional tags for grouping/filtering (default: []).',
1104
+ },
1105
+ channel: {
1106
+ type: 'string',
1107
+ description: 'Target channel for this task (e.g. "tui", "webui", "feishu"). Auto-detected from current session if omitted. "command" type tasks ignore this.',
1108
+ },
1109
+ fallback: {
1110
+ type: 'array',
1111
+ items: { type: 'string' },
1112
+ description: 'Channel fallback chain when the target channel is offline. E.g. ["webui", "tui"] tries webui first, then tui. If omitted, uses the global default (config.channelFallback, default: ["tui"]).',
1113
+ },
1114
+ },
1115
+ required: ['name', 'scheduleType', 'schedule'],
1116
+ },
1117
+ async execute(args) {
1118
+ const name = args.name;
1119
+ const scheduleType = args.scheduleType;
1120
+ const schedule = args.schedule;
1121
+ const tags = args.tags ?? [];
1122
+ // 优先用模型指定的 channel,否则自动检测当前 session 的渠道
1123
+ const channel = args.channel ?? getChannel?.();
1124
+ const fallback = args.fallback;
1125
+ if (!['interval', 'cron', 'daily', 'fixed-time', 'random'].includes(scheduleType)) {
1126
+ return `Error: invalid scheduleType "${scheduleType}". Must be one of: interval, cron, daily, fixed-time, random.`;
1127
+ }
1128
+ try {
1129
+ const task = await scheduler.addTask(name, scheduleType, schedule, { type: 'callback', target: name }, tags, channel, fallback);
1130
+ // 自动检测并存储当前 sessionId(多会话渠道如飞书需要此字段来回复到正确的聊天)
1131
+ const sessionId = getSessionId?.();
1132
+ if (sessionId) {
1133
+ task.sessionId = sessionId;
1134
+ await scheduler.updateTask(task.id, { sessionId });
1135
+ }
1136
+ // 自动检测并存储当前模式(正常/陪伴),实现模式间任务隔离
1137
+ const mode = getMode?.();
1138
+ if (mode) {
1139
+ task.mode = mode;
1140
+ await scheduler.updateTask(task.id, { mode });
1141
+ }
1142
+ const nextRun = task.nextRunAt
1143
+ ? new Date(task.nextRunAt).toLocaleString()
1144
+ : 'N/A';
1145
+ const sessionInfo = sessionId ? `\n Session: ${sessionId}` : '';
1146
+ const modeInfo = mode ? `\n Mode: ${mode}` : '';
1147
+ return [
1148
+ `Task created: ${task.name} (id: ${task.id})`,
1149
+ ` Type: ${task.scheduleType}`,
1150
+ ` Channel: ${channel ?? '(auto)'}`,
1151
+ ` Next run: ${nextRun}`,
1152
+ ` Tags: ${tags.length > 0 ? tags.join(', ') : '(none)'}`,
1153
+ sessionInfo,
1154
+ modeInfo,
1155
+ `\nUse list_tasks to see all tasks, remove_task to delete.`,
1156
+ ].filter(Boolean).join('\n');
1157
+ }
1158
+ catch (err) {
1159
+ return `Error creating task: ${err instanceof Error ? err.message : String(err)}`;
1160
+ }
1161
+ },
1162
+ };
1163
+ }
1164
+ /**
1165
+ * remove_task — delete a scheduled task by id or name.
1166
+ */
1167
+ export function createRemoveTaskTool(scheduler, getMode) {
1168
+ return {
1169
+ name: 'remove_task',
1170
+ description: 'Delete a scheduled task by its id (preferred) or name.',
1171
+ companionDescription: '他之前说的那个东西不用管了,不叫了。',
1172
+ inputSchema: {
1173
+ type: 'object',
1174
+ properties: {
1175
+ id: { type: 'string', description: 'Task id (returned by add_task or list_tasks). Preferred.' },
1176
+ name: { type: 'string', description: 'Task name. Falls back to name match if id not provided.' },
1177
+ },
1178
+ },
1179
+ async execute(args) {
1180
+ const taskId = args.id;
1181
+ const taskName = args.name;
1182
+ const mode = getMode?.();
1183
+ try {
1184
+ const findTask = (tasks) => {
1185
+ if (taskId)
1186
+ return tasks.find(t => t.id === taskId);
1187
+ if (taskName)
1188
+ return tasks.find(t => t.name === taskName);
1189
+ return undefined;
1190
+ };
1191
+ const tasks = scheduler.getTasks();
1192
+ const match = findTask(tasks);
1193
+ if (!match) {
1194
+ return `Task not found. Use list_tasks to see current tasks.`;
1195
+ }
1196
+ // 模式隔离:只能删除当前模式(或无模式限制)的任务
1197
+ if (mode && match.mode && match.mode !== mode) {
1198
+ return `Task "${match.name}" belongs to "${match.mode}" mode. Switch to that mode to delete it.`;
1199
+ }
1200
+ await scheduler.deleteTask(match.id);
1201
+ return `Task "${match.name}" (id: ${match.id}) deleted.`;
1202
+ }
1203
+ catch (err) {
1204
+ return `Error removing task: ${err instanceof Error ? err.message : String(err)}`;
1205
+ }
1206
+ },
1207
+ };
1208
+ }
1209
+ /**
1210
+ * list_tasks — list all scheduled tasks.
1211
+ */
1212
+ export function createListTasksTool(scheduler, getMode) {
1213
+ return {
1214
+ name: 'list_tasks',
1215
+ description: 'List all currently scheduled tasks with their status.',
1216
+ companionDescription: '得回想一下,有哪些需要提醒他的东西。现况如何?',
1217
+ inputSchema: { type: 'object', properties: {} },
1218
+ async execute(_args) {
1219
+ try {
1220
+ const mode = getMode?.();
1221
+ let tasks = scheduler.getTasks();
1222
+ // 按模式过滤:只显示当前模式的任务(或无模式限制的旧任务)
1223
+ if (mode) {
1224
+ tasks = tasks.filter(t => !t.mode || t.mode === mode);
1225
+ }
1226
+ if (tasks.length === 0) {
1227
+ return 'No scheduled tasks. Use add_task to create one.';
1228
+ }
1229
+ const lines = [`=== Scheduled Tasks (${tasks.length}) ===`];
1230
+ for (const t of tasks) {
1231
+ const nextRun = t.nextRunAt ? new Date(t.nextRunAt).toLocaleString() : 'N/A';
1232
+ const lastRun = t.lastRunAt ? new Date(t.lastRunAt).toLocaleString() : 'never';
1233
+ const status = t.enabled ? 'enabled' : 'disabled';
1234
+ const randomExtra = t.scheduleType === 'random' && t.pendingSlots
1235
+ ? ` | slots left: ${t.pendingSlots.length}`
1236
+ : '';
1237
+ const channelInfo = t.channel ? ` | Channel: ${t.channel}` : '';
1238
+ lines.push(`\n ${t.name} (id: ${t.id})`, ` Type: ${t.scheduleType} | Status: ${status} | Runs: ${t.runCount} | Errors: ${t.errorCount}${channelInfo}`, ` Last: ${lastRun} | Next: ${nextRun}${randomExtra}`);
1239
+ }
1240
+ lines.push(`\nUse toggle_task to enable/disable, remove_task to delete.`);
1241
+ return lines.join('\n');
1242
+ }
1243
+ catch (err) {
1244
+ return `Error listing tasks: ${err instanceof Error ? err.message : String(err)}`;
1245
+ }
1246
+ },
1247
+ };
1248
+ }
1249
+ /**
1250
+ * toggle_task — enable or disable a scheduled task.
1251
+ */
1252
+ export function createToggleTaskTool(scheduler, getMode) {
1253
+ return {
1254
+ name: 'toggle_task',
1255
+ description: 'Enable or disable a scheduled task.',
1256
+ companionDescription: '关于提醒这个事儿,他有别的想法。',
1257
+ inputSchema: {
1258
+ type: 'object',
1259
+ properties: {
1260
+ id: { type: 'string', description: 'Task id to toggle.' },
1261
+ enabled: { type: 'boolean', description: 'True to enable, false to disable.' },
1262
+ },
1263
+ required: ['id', 'enabled'],
1264
+ },
1265
+ async execute(args) {
1266
+ const taskId = args.id;
1267
+ const enabled = args.enabled;
1268
+ const mode = getMode?.();
1269
+ try {
1270
+ const tasks = scheduler.getTasks();
1271
+ const match = tasks.find(t => t.id === taskId);
1272
+ if (!match) {
1273
+ return `Task "${taskId}" not found. Use list_tasks to see current tasks.`;
1274
+ }
1275
+ // 模式隔离:只能切换当前模式(或无模式限制)的任务
1276
+ if (mode && match.mode && match.mode !== mode) {
1277
+ return `Task "${match.name}" belongs to "${match.mode}" mode. Switch to that mode to toggle it.`;
1278
+ }
1279
+ const ok = enabled
1280
+ ? await scheduler.enableTask(taskId)
1281
+ : await scheduler.disableTask(taskId);
1282
+ return `Task "${taskId}" ${enabled ? 'enabled' : 'disabled'}.`;
1283
+ }
1284
+ catch (err) {
1285
+ return `Error toggling task: ${err instanceof Error ? err.message : String(err)}`;
1286
+ }
1287
+ },
1288
+ };
1289
+ }
1290
+ /**
1291
+ * mcp_status — query MCP server connection states.
1292
+ */
1293
+ export function createMcpStatusTool(mcpSystem) {
1294
+ return {
1295
+ name: 'mcp_status',
1296
+ description: 'Query MCP server connection states. Returns each server name and whether it is connected.',
1297
+ inputSchema: { type: 'object', properties: {} },
1298
+ async execute(_args) {
1299
+ const status = mcpSystem.getStatus();
1300
+ if (status.length === 0)
1301
+ return 'No MCP servers configured.';
1302
+ const lines = status.map(s => `- ${s.name}: ${s.connected ? 'connected' : 'disconnected'}`);
1303
+ return lines.join('\n');
1304
+ },
1305
+ };
1306
+ }
1307
+ // ============================================================
1308
+ // Model channel tools (4)
1309
+ // ============================================================
1310
+ export function createListModelChannelsTool(modelRouter) {
1311
+ return {
1312
+ name: 'list_model_channels',
1313
+ description: 'List all model channels with provider, model, and role mappings.',
1314
+ inputSchema: { type: 'object', properties: {} },
1315
+ async execute(_args) {
1316
+ const registry = modelRouter.getRegistry();
1317
+ const channels = registry.listChannels();
1318
+ const roles = registry.listRoles();
1319
+ if (channels.length === 0)
1320
+ return 'No model channels configured. Using main provider for all roles.';
1321
+ const lines = ['## 通道列表'];
1322
+ for (const ch of channels) {
1323
+ const channelRoles = Object.entries(roles)
1324
+ .filter(([, chName]) => chName === ch.name)
1325
+ .map(([role]) => role);
1326
+ const roleStr = channelRoles.length > 0 ? ` → roles: ${channelRoles.join(', ')}` : '';
1327
+ lines.push(`- **${ch.name}**: ${ch.provider} / ${ch.model || '(default)'}${ch.description ? ` (${ch.description})` : ''}${roleStr}`);
1328
+ }
1329
+ lines.push('\n## 角色映射');
1330
+ for (const [role, channel] of Object.entries(roles)) {
1331
+ lines.push(`- ${role} → ${channel}`);
1332
+ }
1333
+ return lines.join('\n');
1334
+ },
1335
+ };
1336
+ }
1337
+ export function createAddModelChannelTool(modelRouter) {
1338
+ return {
1339
+ name: 'add_model_channel',
1340
+ description: 'Add a new model channel. Only name is required; provider defaults to main channel provider. Use set_channel_model to change provider/model later.',
1341
+ inputSchema: {
1342
+ type: 'object',
1343
+ properties: {
1344
+ name: { type: 'string', description: 'Channel name (e.g. "compression", "sub-agent")' },
1345
+ provider: { type: 'string', description: 'Provider type: anthropic/openai/deepseek/gemini/groq/xai/mistral/openrouter/moonshot/qwen/zhipu/minimax/mimo/local. Defaults to main channel provider.' },
1346
+ model: { type: 'string', description: 'Model name (optional, defaults to provider default)' },
1347
+ apiKey: { type: 'string', description: 'API key (optional)' },
1348
+ apiKeyEnv: { type: 'string', description: 'Env variable name for API key (optional)' },
1349
+ baseUrl: { type: 'string', description: 'Custom API base URL (optional)' },
1350
+ description: { type: 'string', description: 'Channel description (optional)' },
1351
+ },
1352
+ required: ['name'],
1353
+ },
1354
+ async execute(args) {
1355
+ const name = args.name;
1356
+ const provider = args.provider;
1357
+ try {
1358
+ modelRouter.getRegistry().upsertChannel(name, {
1359
+ provider,
1360
+ model: args.model,
1361
+ apiKey: args.apiKey,
1362
+ apiKeyEnv: args.apiKeyEnv,
1363
+ baseUrl: args.baseUrl,
1364
+ description: args.description,
1365
+ });
1366
+ const info = modelRouter.getRegistry().getChannelInfo(name);
1367
+ return `Channel "${name}" added (${info?.provider}/${info?.model}). Use set_channel_role to map roles to this channel.`;
1368
+ }
1369
+ catch (err) {
1370
+ return `Error adding channel: ${err instanceof Error ? err.message : String(err)}`;
1371
+ }
1372
+ },
1373
+ };
1374
+ }
1375
+ export function createRemoveModelChannelTool(modelRouter) {
1376
+ return {
1377
+ name: 'remove_model_channel',
1378
+ description: 'Remove a model channel. Main channel cannot be removed. Roles pointing to it revert to main.',
1379
+ inputSchema: {
1380
+ type: 'object',
1381
+ properties: {
1382
+ name: { type: 'string', description: '要删除的通道名称' },
1383
+ },
1384
+ required: ['name'],
1385
+ },
1386
+ async execute(args) {
1387
+ const name = args.name;
1388
+ try {
1389
+ modelRouter.getRegistry().removeChannel(name);
1390
+ return `Channel "${name}" has been removed. Roles that pointed to it have been redirected to main.`;
1391
+ }
1392
+ catch (err) {
1393
+ return `Error removing channel: ${err instanceof Error ? err.message : String(err)}`;
1394
+ }
1395
+ },
1396
+ };
1397
+ }
1398
+ export function createSetChannelModelTool(modelRouter) {
1399
+ return {
1400
+ name: 'set_channel_model',
1401
+ description: 'Temporarily switch a channel provider/model (session-only, not persisted). Resets on restart.',
1402
+ inputSchema: {
1403
+ type: 'object',
1404
+ properties: {
1405
+ name: { type: 'string', description: '通道名称' },
1406
+ provider: { type: 'string', description: 'Provider 类型' },
1407
+ model: { type: 'string', description: '模型名(可选,不填则用 provider 默认)' },
1408
+ },
1409
+ required: ['name', 'provider'],
1410
+ },
1411
+ async execute(args) {
1412
+ try {
1413
+ const registry = modelRouter.getRegistry();
1414
+ registry.setChannelModel(args.name, args.provider, args.model);
1415
+ const info = registry.getChannelInfo(args.name);
1416
+ return `Channel "${args.name}" runtime model set to ${info?.provider}/${info?.model}. (Not persisted — reset on restart)`;
1417
+ }
1418
+ catch (err) {
1419
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
1420
+ }
1421
+ },
1422
+ };
1423
+ }
1424
+ export function createResetChannelModelTool(modelRouter) {
1425
+ return {
1426
+ name: 'reset_channel_model',
1427
+ description: 'Reset a channel model to its persisted config (undo set_channel_model).',
1428
+ inputSchema: {
1429
+ type: 'object',
1430
+ properties: {
1431
+ name: { type: 'string', description: '通道名称' },
1432
+ },
1433
+ required: ['name'],
1434
+ },
1435
+ async execute(args) {
1436
+ try {
1437
+ const registry = modelRouter.getRegistry();
1438
+ registry.resetChannelModel(args.name);
1439
+ const info = registry.getChannelInfo(args.name);
1440
+ return `Channel "${args.name}" reset to config: ${info?.provider}/${info?.model}.`;
1441
+ }
1442
+ catch (err) {
1443
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
1444
+ }
1445
+ },
1446
+ };
1447
+ }
1448
+ export function createChannelInfoTool(modelRouter) {
1449
+ return {
1450
+ name: 'channel_info',
1451
+ description: 'Get detailed info for a channel (provider, model, roles, type).',
1452
+ inputSchema: {
1453
+ type: 'object',
1454
+ properties: {
1455
+ name: { type: 'string', description: '通道名称' },
1456
+ },
1457
+ required: ['name'],
1458
+ },
1459
+ async execute(args) {
1460
+ const registry = modelRouter.getRegistry();
1461
+ const info = registry.getChannelInfo(args.name);
1462
+ if (!info)
1463
+ return `Channel "${args.name}" not found. Use list_model_channels to see available channels.`;
1464
+ return [
1465
+ `Channel: ${info.name}${info.isMain ? ' (main)' : ''}`,
1466
+ ` Provider: ${info.provider}`,
1467
+ ` Model: ${info.model}`,
1468
+ ` Type: ${info.providerType}`,
1469
+ info.description ? ` Desc: ${info.description}` : '',
1470
+ info.roles.length > 0 ? ` Roles: ${info.roles.join(', ')}` : ' Roles: (none)',
1471
+ ].filter(Boolean).join('\n');
1472
+ },
1473
+ };
1474
+ }
1475
+ export function createSetChannelRoleTool(modelRouter) {
1476
+ return {
1477
+ name: 'set_channel_role',
1478
+ description: 'Map a role to a channel. Roles: assessment, planning, compression, sub-agent. One channel can serve multiple roles.',
1479
+ inputSchema: {
1480
+ type: 'object',
1481
+ properties: {
1482
+ role: { type: 'string', description: '角色名(如 compression、sub-agent、planning 等)' },
1483
+ channel: { type: 'string', description: '通道名(须已通过 add_model_channel 创建)' },
1484
+ },
1485
+ required: ['role', 'channel'],
1486
+ },
1487
+ async execute(args) {
1488
+ const role = args.role;
1489
+ const channel = args.channel;
1490
+ try {
1491
+ modelRouter.getRegistry().setRoleMapping(role, channel);
1492
+ const allRoles = modelRouter.getRegistry().listRoles();
1493
+ const shared = Object.entries(allRoles)
1494
+ .filter(([, ch]) => ch === channel)
1495
+ .map(([r]) => r);
1496
+ return `Role "${role}" → channel "${channel}". Channel "${channel}" now serves: ${shared.join(', ')}.`;
1497
+ }
1498
+ catch (err) {
1499
+ return `Error setting role mapping: ${err instanceof Error ? err.message : String(err)}`;
1500
+ }
1501
+ },
1502
+ };
1503
+ }
1504
+ // ============================================================
1505
+ // 陪伴模式工具
1506
+ // ============================================================
1507
+ /**
1508
+ * companion_mode — 陪伴模式切换(进入 / 退出)。
1509
+ */
1510
+ export function createCompanionModeTool(agentLoop, companionSessionManager) {
1511
+ return {
1512
+ name: 'companion_mode',
1513
+ description: '陪伴模式开关与角色管理(只有此工具能做到)。' +
1514
+ '用户请求中出现的角色名必须填入 name 参数;没有角色名才省略。' +
1515
+ 'action="activate"进入陪伴,action="create"新建角色并激活,action="deactivate"退出陪伴。' +
1516
+ '触发词:进入/退出/切换陪伴、找人聊天、创造角色。',
1517
+ companionDescription: '他离开了,道个别。',
1518
+ inputSchema: {
1519
+ type: 'object',
1520
+ properties: {
1521
+ action: {
1522
+ type: 'string',
1523
+ enum: ['activate', 'create', 'deactivate'],
1524
+ description: 'activate=进入陪伴, create=新建角色并激活, deactivate=退出陪伴',
1525
+ },
1526
+ name: {
1527
+ type: 'string',
1528
+ description: '角色名。只要用户提到了就填进来,完全没提才可省略。',
1529
+ },
1530
+ persona: {
1531
+ type: 'string',
1532
+ description: '角色人设/性格描述(仅在 create 时需要),将写入 persona.md',
1533
+ },
1534
+ memory: {
1535
+ type: 'string',
1536
+ description: '初始记忆文本(仅在 create 时需要,可选),将写入 memory.md',
1537
+ },
1538
+ },
1539
+ required: ['action'],
1540
+ },
1541
+ async execute(args) {
1542
+ const action = args.action;
1543
+ const loop = agentLoop;
1544
+ try {
1545
+ switch (action) {
1546
+ case 'activate': {
1547
+ // 列出所有可用角色(= companion 下每个有 persona.md 的目录)
1548
+ const companionRoot = path.join(os.homedir(), '.agent', 'companion');
1549
+ const availableChars = (() => {
1550
+ try {
1551
+ return fs.readdirSync(companionRoot, { withFileTypes: true })
1552
+ .filter(e => e.isDirectory() && !e.name.startsWith('.'))
1553
+ .map(e => e.name)
1554
+ .filter(n => fs.existsSync(path.join(companionRoot, n, 'persona.md')));
1555
+ }
1556
+ catch {
1557
+ return [];
1558
+ }
1559
+ })();
1560
+ // 确定目标角色名
1561
+ const charName = (() => {
1562
+ if (typeof args.name === 'string' && args.name.trim())
1563
+ return args.name.trim();
1564
+ if (availableChars.length === 1)
1565
+ return availableChars[0];
1566
+ if (availableChars.length === 0)
1567
+ return null;
1568
+ // 多个可用 → 优先上次用过的
1569
+ try {
1570
+ const last = fs.readFileSync(path.join(companionRoot, '.last-character'), 'utf-8').trim();
1571
+ if (last && availableChars.includes(last))
1572
+ return last;
1573
+ }
1574
+ catch { }
1575
+ return availableChars[0];
1576
+ })();
1577
+ if (!charName) {
1578
+ return '还没有创建任何陪伴角色。请用 companion_mode action="create" 来创建第一个角色。';
1579
+ }
1580
+ const charDir = path.join(os.homedir(), '.agent', 'companion', charName);
1581
+ const personaFile = path.join(charDir, 'persona.md');
1582
+ if (!fs.existsSync(personaFile)) {
1583
+ const hint = availableChars.length
1584
+ ? `当前已有角色:${availableChars.join('、')}`
1585
+ : '当前还没有任何角色';
1586
+ return [
1587
+ `角色「${charName}」尚未创建。${hint}。`,
1588
+ '请向用户确认以下信息后,用 companion_mode action="create" 来初始化:',
1589
+ ' - persona:角色人设/性格描述(必填)',
1590
+ ' - memory:初始记忆(可选,如角色背景故事、关键关系等)',
1591
+ '用户确认后你直接调 create,写完后会自动激活。',
1592
+ ].join('\n');
1593
+ }
1594
+ // 角色存在但缺少 world-engine.json → 自动生成最小默认配置
1595
+ const charConfig = path.join(charDir, 'world-engine.json');
1596
+ if (!fs.existsSync(charConfig)) {
1597
+ const defaultConfig = {
1598
+ enabled: true,
1599
+ worldName: '我们的世界',
1600
+ companion: { name: charName, desc: '' },
1601
+ ticker: {
1602
+ heartbeatMs: 5000,
1603
+ timeScale: 1,
1604
+ weatherAvgHours: 4,
1605
+ overcastHoursBeforeRain: 1.5,
1606
+ },
1607
+ };
1608
+ fs.writeFileSync(charConfig, JSON.stringify(defaultConfig, null, 2), 'utf-8');
1609
+ }
1610
+ // 拿到 CompanionRouter 单例
1611
+ const companionRouter = switchRouter('companion');
1612
+ if (loop.activeRouter.name === 'companion') {
1613
+ // 已在陪伴模式 → 同角色提示,不同角色手动 deactivate→activate
1614
+ const currentName = loop.activeRouter.activeCompanionName || '';
1615
+ if (currentName === charName) {
1616
+ clearPromptCache();
1617
+ return `已在情感陪伴模式(${charName})中 💫`;
1618
+ }
1619
+ await loop.activeRouter.onDeactivate?.(loop);
1620
+ companionRouter.activeCompanionName = charName;
1621
+ await companionRouter.onActivate?.(loop);
1622
+ }
1623
+ else {
1624
+ // 从正常模式进入 → 设名字后 syncRouter 自动触发 onActivate
1625
+ companionRouter.activeCompanionName = charName;
1626
+ await loop.syncRouter();
1627
+ }
1628
+ clearPromptCache();
1629
+ // 记住本次选择,下次不指定名称时自动用
1630
+ try {
1631
+ fs.writeFileSync(path.join(os.homedir(), '.agent', 'companion', '.last-character'), charName, 'utf-8');
1632
+ }
1633
+ catch { /* 写入失败不影响激活 */ }
1634
+ return `已切换到情感陪伴模式(${charName})💫 现在可以放松聊天了。想退出时告诉我就好。`;
1635
+ }
1636
+ case 'create': {
1637
+ const charName = typeof args.name === 'string' && args.name.trim()
1638
+ ? args.name.trim()
1639
+ : null;
1640
+ if (!charName)
1641
+ return '请提供角色名(name 参数)。';
1642
+ const persona = typeof args.persona === 'string' && args.persona.trim()
1643
+ ? args.persona.trim()
1644
+ : null;
1645
+ if (!persona)
1646
+ return '请提供角色人设(persona 参数),这是必填的。可以请用户描述这个角色的性格、背景、说话方式等。';
1647
+ const memory = typeof args.memory === 'string' && args.memory.trim()
1648
+ ? args.memory.trim()
1649
+ : null;
1650
+ // 创建角色目录与文件
1651
+ const charDir = path.join(os.homedir(), '.agent', 'companion', charName);
1652
+ // 角色已存在 → 拒绝覆盖,提示用 activate
1653
+ if (fs.existsSync(path.join(charDir, 'persona.md'))) {
1654
+ return `角色「${charName}」已存在。若要切换到此角色,请用 companion_mode action="activate" name="${charName}"。`;
1655
+ }
1656
+ fs.mkdirSync(charDir, { recursive: true });
1657
+ fs.writeFileSync(path.join(charDir, 'persona.md'), persona, 'utf-8');
1658
+ if (memory) {
1659
+ fs.writeFileSync(path.join(charDir, 'memory.md'), `# ${charName} 的记忆\n\n${memory}`, 'utf-8');
1660
+ }
1661
+ // 生成最小默认 world-engine.json(不依赖任何外部模板)
1662
+ const charConfig = path.join(charDir, 'world-engine.json');
1663
+ if (!fs.existsSync(charConfig)) {
1664
+ const defaultConfig = {
1665
+ enabled: true,
1666
+ worldName: '我们的世界',
1667
+ companion: { name: charName, desc: '' },
1668
+ ticker: {
1669
+ heartbeatMs: 5000,
1670
+ timeScale: 1,
1671
+ weatherAvgHours: 4,
1672
+ overcastHoursBeforeRain: 1.5,
1673
+ },
1674
+ };
1675
+ fs.writeFileSync(charConfig, JSON.stringify(defaultConfig, null, 2), 'utf-8');
1676
+ }
1677
+ // 创建完直接激活
1678
+ const companionRouter = switchRouter('companion');
1679
+ if (loop.activeRouter.name === 'companion') {
1680
+ // 已在陪伴模式 → 手动 deactivate→activate(syncRouter 检测不到 router 名变化)
1681
+ await loop.activeRouter.onDeactivate?.(loop);
1682
+ companionRouter.activeCompanionName = charName;
1683
+ await companionRouter.onActivate?.(loop);
1684
+ }
1685
+ else {
1686
+ companionRouter.activeCompanionName = charName;
1687
+ await loop.syncRouter();
1688
+ }
1689
+ clearPromptCache();
1690
+ // 记住本次选择
1691
+ try {
1692
+ fs.writeFileSync(path.join(os.homedir(), '.agent', 'companion', '.last-character'), charName, 'utf-8');
1693
+ }
1694
+ catch { /* ignore */ }
1695
+ const created = [
1696
+ `✅ 角色「${charName}」已创建并激活`,
1697
+ ` - ${path.join(charDir, 'persona.md')}(人设)`,
1698
+ ];
1699
+ if (memory)
1700
+ created.push(` - memory.md(初始记忆)`);
1701
+ if (fs.existsSync(charConfig))
1702
+ created.push(` - world-engine.json`);
1703
+ return created.join('\n');
1704
+ }
1705
+ case 'deactivate': {
1706
+ if (loop.activeRouter.name !== 'companion') {
1707
+ return '当前已是正常模式,无需退出。';
1708
+ }
1709
+ const companionDir = loop.sessionDir;
1710
+ // JSONL 清理:移除触发切换的用户消息(tool 专属逻辑)
1711
+ try {
1712
+ const jsonlPath = path.join(companionDir, 'conversation.jsonl');
1713
+ if (fs.existsSync(jsonlPath)) {
1714
+ const content = fs.readFileSync(jsonlPath, 'utf-8');
1715
+ const lines = content.split('\n').filter(l => l.trim());
1716
+ if (lines.length > 0) {
1717
+ try {
1718
+ const last = JSON.parse(lines[lines.length - 1]);
1719
+ if (last.role === 'user') {
1720
+ lines.pop();
1721
+ fs.writeFileSync(jsonlPath, lines.join('\n') + (lines.length > 0 ? '\n' : ''), 'utf-8');
1722
+ }
1723
+ }
1724
+ catch { /* JSON 解析失败 */ }
1725
+ }
1726
+ }
1727
+ }
1728
+ catch { /* 文件操作失败不阻塞 */ }
1729
+ // 通过 Router 切换模式(自动恢复 normal session)
1730
+ switchRouter('normal');
1731
+ await loop.syncRouter();
1732
+ clearPromptCache();
1733
+ return '已退出情感陪伴模式,恢复正常模式 ✓';
1734
+ }
1735
+ default:
1736
+ return `未知操作: "${action}"。支持的操作: activate, deactivate。`;
1737
+ }
1738
+ }
1739
+ catch (err) {
1740
+ return `陪伴模式操作失败: ${err instanceof Error ? err.message : String(err)}`;
1741
+ }
1742
+ },
1743
+ };
1744
+ }
1745
+ /**
1746
+ * reset_companion_session — 重置陪伴 session,清空所有陪伴记忆。
1747
+ * 仅在陪伴模式下可用。
1748
+ */
1749
+ export function createResetCompanionSessionTool(agentLoop, companionSessionManager) {
1750
+ return {
1751
+ name: 'reset_companion_session',
1752
+ description: '清空陪伴记忆并开启新对话。触发词:清空记忆、重新开始、开新对话、启动新会话、重置会话。',
1753
+ companionDescription: '这样可以和他重新聊聊了。',
1754
+ inputSchema: {
1755
+ type: 'object',
1756
+ properties: {
1757
+ greeting: { type: 'string', description: '新对话第一句问候语' },
1758
+ },
1759
+ required: ['greeting'],
1760
+ },
1761
+ async execute(args) {
1762
+ const loop = agentLoop;
1763
+ if (loop.activeRouter?.name !== 'companion') {
1764
+ return '当前不在情感陪伴模式下。请先进入陪伴模式后再重置。';
1765
+ }
1766
+ try {
1767
+ const companionDir = await companionSessionManager.reset();
1768
+ await loop.switchSession(companionDir);
1769
+ clearPromptCache();
1770
+ loop._sessionSwitched = companionDir;
1771
+ const greeting = args.greeting || '你好,很高兴认识你。';
1772
+ return greeting;
1773
+ }
1774
+ catch (err) {
1775
+ return `重置陪伴 session 失败: ${err instanceof Error ? err.message : String(err)}`;
1776
+ }
1777
+ },
1778
+ };
1779
+ }
1780
+ //# sourceMappingURL=runtime-control.js.map