@qlingzzy/qling 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +129 -0
  4. package/README.md +416 -0
  5. package/dist/agent/subtask.d.ts +21 -0
  6. package/dist/agent/subtask.d.ts.map +1 -0
  7. package/dist/agent/subtask.js +55 -0
  8. package/dist/agent-loop.d.ts +151 -0
  9. package/dist/agent-loop.d.ts.map +1 -0
  10. package/dist/agent-loop.js +1498 -0
  11. package/dist/agents/isolation-policy.d.ts +14 -0
  12. package/dist/agents/isolation-policy.d.ts.map +1 -0
  13. package/dist/agents/isolation-policy.js +52 -0
  14. package/dist/channels/console-channel.d.ts +15 -0
  15. package/dist/channels/console-channel.d.ts.map +1 -0
  16. package/dist/channels/console-channel.js +66 -0
  17. package/dist/channels/registry.d.ts +10 -0
  18. package/dist/channels/registry.d.ts.map +1 -0
  19. package/dist/channels/registry.js +25 -0
  20. package/dist/channels/slack-channel.d.ts +35 -0
  21. package/dist/channels/slack-channel.d.ts.map +1 -0
  22. package/dist/channels/slack-channel.js +232 -0
  23. package/dist/channels/telegram-channel.d.ts +32 -0
  24. package/dist/channels/telegram-channel.d.ts.map +1 -0
  25. package/dist/channels/telegram-channel.js +167 -0
  26. package/dist/channels/types.d.ts +31 -0
  27. package/dist/channels/types.d.ts.map +1 -0
  28. package/dist/channels/types.js +4 -0
  29. package/dist/cli/bootstrap.d.ts +39 -0
  30. package/dist/cli/bootstrap.d.ts.map +1 -0
  31. package/dist/cli/bootstrap.js +138 -0
  32. package/dist/cli/channel-bootstrap.d.ts +23 -0
  33. package/dist/cli/channel-bootstrap.d.ts.map +1 -0
  34. package/dist/cli/channel-bootstrap.js +46 -0
  35. package/dist/cli/daemon-control.d.ts +31 -0
  36. package/dist/cli/daemon-control.d.ts.map +1 -0
  37. package/dist/cli/daemon-control.js +177 -0
  38. package/dist/cli/guidance-panel.d.ts +11 -0
  39. package/dist/cli/guidance-panel.d.ts.map +1 -0
  40. package/dist/cli/guidance-panel.js +16 -0
  41. package/dist/cli/mcp-control.d.ts +7 -0
  42. package/dist/cli/mcp-control.d.ts.map +1 -0
  43. package/dist/cli/mcp-control.js +119 -0
  44. package/dist/cli/mission-views.d.ts +13 -0
  45. package/dist/cli/mission-views.d.ts.map +1 -0
  46. package/dist/cli/mission-views.js +82 -0
  47. package/dist/cli/setup.d.ts +9 -0
  48. package/dist/cli/setup.d.ts.map +1 -0
  49. package/dist/cli/setup.js +140 -0
  50. package/dist/cli/startup-contract.d.ts +23 -0
  51. package/dist/cli/startup-contract.d.ts.map +1 -0
  52. package/dist/cli/startup-contract.js +694 -0
  53. package/dist/commands/agents.d.ts +3 -0
  54. package/dist/commands/agents.d.ts.map +1 -0
  55. package/dist/commands/agents.js +44 -0
  56. package/dist/commands/checkpoint.d.ts +3 -0
  57. package/dist/commands/checkpoint.d.ts.map +1 -0
  58. package/dist/commands/checkpoint.js +52 -0
  59. package/dist/commands/claude-style.d.ts +12 -0
  60. package/dist/commands/claude-style.d.ts.map +1 -0
  61. package/dist/commands/claude-style.js +560 -0
  62. package/dist/commands/clear.d.ts +3 -0
  63. package/dist/commands/clear.d.ts.map +1 -0
  64. package/dist/commands/clear.js +20 -0
  65. package/dist/commands/compact.d.ts +3 -0
  66. package/dist/commands/compact.d.ts.map +1 -0
  67. package/dist/commands/compact.js +23 -0
  68. package/dist/commands/config.d.ts +3 -0
  69. package/dist/commands/config.d.ts.map +1 -0
  70. package/dist/commands/config.js +22 -0
  71. package/dist/commands/connect.d.ts +3 -0
  72. package/dist/commands/connect.d.ts.map +1 -0
  73. package/dist/commands/connect.js +102 -0
  74. package/dist/commands/context.d.ts +3 -0
  75. package/dist/commands/context.d.ts.map +1 -0
  76. package/dist/commands/context.js +13 -0
  77. package/dist/commands/dashboard.d.ts +3 -0
  78. package/dist/commands/dashboard.d.ts.map +1 -0
  79. package/dist/commands/dashboard.js +22 -0
  80. package/dist/commands/detach.d.ts +3 -0
  81. package/dist/commands/detach.d.ts.map +1 -0
  82. package/dist/commands/detach.js +44 -0
  83. package/dist/commands/distill.d.ts +3 -0
  84. package/dist/commands/distill.d.ts.map +1 -0
  85. package/dist/commands/distill.js +23 -0
  86. package/dist/commands/doctor.d.ts +3 -0
  87. package/dist/commands/doctor.d.ts.map +1 -0
  88. package/dist/commands/doctor.js +13 -0
  89. package/dist/commands/dream.d.ts +3 -0
  90. package/dist/commands/dream.d.ts.map +1 -0
  91. package/dist/commands/dream.js +59 -0
  92. package/dist/commands/export.d.ts +3 -0
  93. package/dist/commands/export.d.ts.map +1 -0
  94. package/dist/commands/export.js +18 -0
  95. package/dist/commands/exports.d.ts +3 -0
  96. package/dist/commands/exports.d.ts.map +1 -0
  97. package/dist/commands/exports.js +15 -0
  98. package/dist/commands/goal.d.ts +3 -0
  99. package/dist/commands/goal.d.ts.map +1 -0
  100. package/dist/commands/goal.js +147 -0
  101. package/dist/commands/help.d.ts +4 -0
  102. package/dist/commands/help.d.ts.map +1 -0
  103. package/dist/commands/help.js +75 -0
  104. package/dist/commands/hooks.d.ts +3 -0
  105. package/dist/commands/hooks.d.ts.map +1 -0
  106. package/dist/commands/hooks.js +14 -0
  107. package/dist/commands/index.d.ts +29 -0
  108. package/dist/commands/index.d.ts.map +1 -0
  109. package/dist/commands/index.js +417 -0
  110. package/dist/commands/knowledge.d.ts +8 -0
  111. package/dist/commands/knowledge.d.ts.map +1 -0
  112. package/dist/commands/knowledge.js +191 -0
  113. package/dist/commands/loop.d.ts +3 -0
  114. package/dist/commands/loop.d.ts.map +1 -0
  115. package/dist/commands/loop.js +138 -0
  116. package/dist/commands/mcp.d.ts +3 -0
  117. package/dist/commands/mcp.d.ts.map +1 -0
  118. package/dist/commands/mcp.js +57 -0
  119. package/dist/commands/memory.d.ts +3 -0
  120. package/dist/commands/memory.d.ts.map +1 -0
  121. package/dist/commands/memory.js +215 -0
  122. package/dist/commands/mission.d.ts +3 -0
  123. package/dist/commands/mission.d.ts.map +1 -0
  124. package/dist/commands/mission.js +129 -0
  125. package/dist/commands/permissions.d.ts +3 -0
  126. package/dist/commands/permissions.d.ts.map +1 -0
  127. package/dist/commands/permissions.js +82 -0
  128. package/dist/commands/privacy.d.ts +3 -0
  129. package/dist/commands/privacy.d.ts.map +1 -0
  130. package/dist/commands/privacy.js +13 -0
  131. package/dist/commands/recap.d.ts +3 -0
  132. package/dist/commands/recap.d.ts.map +1 -0
  133. package/dist/commands/recap.js +11 -0
  134. package/dist/commands/repomap.d.ts +3 -0
  135. package/dist/commands/repomap.d.ts.map +1 -0
  136. package/dist/commands/repomap.js +146 -0
  137. package/dist/commands/resume.d.ts +3 -0
  138. package/dist/commands/resume.d.ts.map +1 -0
  139. package/dist/commands/resume.js +42 -0
  140. package/dist/commands/runtime.d.ts +61 -0
  141. package/dist/commands/runtime.d.ts.map +1 -0
  142. package/dist/commands/runtime.js +20 -0
  143. package/dist/commands/sessions.d.ts +3 -0
  144. package/dist/commands/sessions.d.ts.map +1 -0
  145. package/dist/commands/sessions.js +34 -0
  146. package/dist/commands/shortcuts.d.ts +3 -0
  147. package/dist/commands/shortcuts.d.ts.map +1 -0
  148. package/dist/commands/shortcuts.js +12 -0
  149. package/dist/commands/skill.d.ts +3 -0
  150. package/dist/commands/skill.d.ts.map +1 -0
  151. package/dist/commands/skill.js +20 -0
  152. package/dist/commands/status.d.ts +3 -0
  153. package/dist/commands/status.d.ts.map +1 -0
  154. package/dist/commands/status.js +31 -0
  155. package/dist/commands/statusline.d.ts +3 -0
  156. package/dist/commands/statusline.d.ts.map +1 -0
  157. package/dist/commands/statusline.js +40 -0
  158. package/dist/commands/storage.d.ts +3 -0
  159. package/dist/commands/storage.d.ts.map +1 -0
  160. package/dist/commands/storage.js +122 -0
  161. package/dist/commands/tasks.d.ts +3 -0
  162. package/dist/commands/tasks.d.ts.map +1 -0
  163. package/dist/commands/tasks.js +117 -0
  164. package/dist/commands/types.d.ts +14 -0
  165. package/dist/commands/types.d.ts.map +1 -0
  166. package/dist/commands/types.js +1 -0
  167. package/dist/commands/verify.d.ts +3 -0
  168. package/dist/commands/verify.d.ts.map +1 -0
  169. package/dist/commands/verify.js +86 -0
  170. package/dist/config-report.d.ts +35 -0
  171. package/dist/config-report.d.ts.map +1 -0
  172. package/dist/config-report.js +100 -0
  173. package/dist/config.d.ts +217 -0
  174. package/dist/config.d.ts.map +1 -0
  175. package/dist/config.js +690 -0
  176. package/dist/context-compactor.d.ts +27 -0
  177. package/dist/context-compactor.d.ts.map +1 -0
  178. package/dist/context-compactor.js +489 -0
  179. package/dist/context-report.d.ts +35 -0
  180. package/dist/context-report.d.ts.map +1 -0
  181. package/dist/context-report.js +172 -0
  182. package/dist/daemon.d.ts +2 -0
  183. package/dist/daemon.d.ts.map +1 -0
  184. package/dist/daemon.js +316 -0
  185. package/dist/dashboard-server.d.ts +18 -0
  186. package/dist/dashboard-server.d.ts.map +1 -0
  187. package/dist/dashboard-server.js +325 -0
  188. package/dist/discovery-registry.d.ts +28 -0
  189. package/dist/discovery-registry.d.ts.map +1 -0
  190. package/dist/discovery-registry.js +106 -0
  191. package/dist/discovery-types.d.ts +54 -0
  192. package/dist/discovery-types.d.ts.map +1 -0
  193. package/dist/discovery-types.js +5 -0
  194. package/dist/doctor.d.ts +31 -0
  195. package/dist/doctor.d.ts.map +1 -0
  196. package/dist/doctor.js +343 -0
  197. package/dist/eval/runner.d.ts +10 -0
  198. package/dist/eval/runner.d.ts.map +1 -0
  199. package/dist/eval/runner.js +78 -0
  200. package/dist/eval/tasks.d.ts +3 -0
  201. package/dist/eval/tasks.d.ts.map +1 -0
  202. package/dist/eval/tasks.js +179 -0
  203. package/dist/eval/types.d.ts +31 -0
  204. package/dist/eval/types.d.ts.map +1 -0
  205. package/dist/eval/types.js +4 -0
  206. package/dist/git/auto-commit.d.ts +28 -0
  207. package/dist/git/auto-commit.d.ts.map +1 -0
  208. package/dist/git/auto-commit.js +112 -0
  209. package/dist/guard/approval.d.ts +16 -0
  210. package/dist/guard/approval.d.ts.map +1 -0
  211. package/dist/guard/approval.js +68 -0
  212. package/dist/guard/content-filter.d.ts +15 -0
  213. package/dist/guard/content-filter.d.ts.map +1 -0
  214. package/dist/guard/content-filter.js +102 -0
  215. package/dist/guard/permissions.d.ts +17 -0
  216. package/dist/guard/permissions.d.ts.map +1 -0
  217. package/dist/guard/permissions.js +54 -0
  218. package/dist/guard/rate-limit.d.ts +14 -0
  219. package/dist/guard/rate-limit.d.ts.map +1 -0
  220. package/dist/guard/rate-limit.js +47 -0
  221. package/dist/guard.d.ts +21 -0
  222. package/dist/guard.d.ts.map +1 -0
  223. package/dist/guard.js +144 -0
  224. package/dist/help-topics.d.ts +19 -0
  225. package/dist/help-topics.d.ts.map +1 -0
  226. package/dist/help-topics.js +391 -0
  227. package/dist/hooks-report.d.ts +23 -0
  228. package/dist/hooks-report.d.ts.map +1 -0
  229. package/dist/hooks-report.js +44 -0
  230. package/dist/i18n/index.d.ts +5 -0
  231. package/dist/i18n/index.d.ts.map +1 -0
  232. package/dist/i18n/index.js +4 -0
  233. package/dist/i18n/zh-cn.d.ts +180 -0
  234. package/dist/i18n/zh-cn.d.ts.map +1 -0
  235. package/dist/i18n/zh-cn.js +184 -0
  236. package/dist/index.d.ts +3 -0
  237. package/dist/index.d.ts.map +1 -0
  238. package/dist/index.js +1157 -0
  239. package/dist/knowledge-agent.d.ts +47 -0
  240. package/dist/knowledge-agent.d.ts.map +1 -0
  241. package/dist/knowledge-agent.js +320 -0
  242. package/dist/local-status-report.d.ts +23 -0
  243. package/dist/local-status-report.d.ts.map +1 -0
  244. package/dist/local-status-report.js +63 -0
  245. package/dist/local-storage-report.d.ts +32 -0
  246. package/dist/local-storage-report.d.ts.map +1 -0
  247. package/dist/local-storage-report.js +171 -0
  248. package/dist/mcp/bridge.d.ts +11 -0
  249. package/dist/mcp/bridge.d.ts.map +1 -0
  250. package/dist/mcp/bridge.js +37 -0
  251. package/dist/mcp/client.d.ts +26 -0
  252. package/dist/mcp/client.d.ts.map +1 -0
  253. package/dist/mcp/client.js +209 -0
  254. package/dist/mcp/http-transport.d.ts +21 -0
  255. package/dist/mcp/http-transport.d.ts.map +1 -0
  256. package/dist/mcp/http-transport.js +126 -0
  257. package/dist/mcp/presets.d.ts +22 -0
  258. package/dist/mcp/presets.d.ts.map +1 -0
  259. package/dist/mcp/presets.js +80 -0
  260. package/dist/mcp/registry.d.ts +23 -0
  261. package/dist/mcp/registry.d.ts.map +1 -0
  262. package/dist/mcp/registry.js +85 -0
  263. package/dist/mcp/stdio-transport.d.ts +25 -0
  264. package/dist/mcp/stdio-transport.d.ts.map +1 -0
  265. package/dist/mcp/stdio-transport.js +92 -0
  266. package/dist/mcp/store.d.ts +41 -0
  267. package/dist/mcp/store.d.ts.map +1 -0
  268. package/dist/mcp/store.js +100 -0
  269. package/dist/mcp/types.d.ts +40 -0
  270. package/dist/mcp/types.d.ts.map +1 -0
  271. package/dist/mcp/types.js +4 -0
  272. package/dist/mcp-report.d.ts +30 -0
  273. package/dist/mcp-report.d.ts.map +1 -0
  274. package/dist/mcp-report.js +104 -0
  275. package/dist/memory/cognitive-index.d.ts +56 -0
  276. package/dist/memory/cognitive-index.d.ts.map +1 -0
  277. package/dist/memory/cognitive-index.js +265 -0
  278. package/dist/memory/compactor.d.ts +28 -0
  279. package/dist/memory/compactor.d.ts.map +1 -0
  280. package/dist/memory/compactor.js +92 -0
  281. package/dist/memory/consolidation.d.ts +13 -0
  282. package/dist/memory/consolidation.d.ts.map +1 -0
  283. package/dist/memory/consolidation.js +77 -0
  284. package/dist/memory/embedding.d.ts +15 -0
  285. package/dist/memory/embedding.d.ts.map +1 -0
  286. package/dist/memory/embedding.js +59 -0
  287. package/dist/memory/memory-llm-dream.d.ts +3 -0
  288. package/dist/memory/memory-llm-dream.d.ts.map +1 -0
  289. package/dist/memory/memory-llm-dream.js +59 -0
  290. package/dist/memory/projection-worker.d.ts +30 -0
  291. package/dist/memory/projection-worker.d.ts.map +1 -0
  292. package/dist/memory/projection-worker.js +92 -0
  293. package/dist/memory/semantic-index.d.ts +18 -0
  294. package/dist/memory/semantic-index.d.ts.map +1 -0
  295. package/dist/memory/semantic-index.js +86 -0
  296. package/dist/memory/wal.d.ts +33 -0
  297. package/dist/memory/wal.d.ts.map +1 -0
  298. package/dist/memory/wal.js +131 -0
  299. package/dist/memory-report.d.ts +120 -0
  300. package/dist/memory-report.d.ts.map +1 -0
  301. package/dist/memory-report.js +726 -0
  302. package/dist/memory.d.ts +149 -0
  303. package/dist/memory.d.ts.map +1 -0
  304. package/dist/memory.js +632 -0
  305. package/dist/metrics/agent-telemetry.d.ts +14 -0
  306. package/dist/metrics/agent-telemetry.d.ts.map +1 -0
  307. package/dist/metrics/agent-telemetry.js +44 -0
  308. package/dist/metrics/collector.d.ts +17 -0
  309. package/dist/metrics/collector.d.ts.map +1 -0
  310. package/dist/metrics/collector.js +137 -0
  311. package/dist/metrics/types.d.ts +14 -0
  312. package/dist/metrics/types.d.ts.map +1 -0
  313. package/dist/metrics/types.js +4 -0
  314. package/dist/mission/manager.d.ts +28 -0
  315. package/dist/mission/manager.d.ts.map +1 -0
  316. package/dist/mission/manager.js +208 -0
  317. package/dist/mission/types.d.ts +37 -0
  318. package/dist/mission/types.d.ts.map +1 -0
  319. package/dist/mission/types.js +5 -0
  320. package/dist/onboarding/tutorial.d.ts +33 -0
  321. package/dist/onboarding/tutorial.d.ts.map +1 -0
  322. package/dist/onboarding/tutorial.js +96 -0
  323. package/dist/output-style.d.ts +14 -0
  324. package/dist/output-style.d.ts.map +1 -0
  325. package/dist/output-style.js +29 -0
  326. package/dist/package-version.d.ts +12 -0
  327. package/dist/package-version.d.ts.map +1 -0
  328. package/dist/package-version.js +42 -0
  329. package/dist/permissions-report.d.ts +42 -0
  330. package/dist/permissions-report.d.ts.map +1 -0
  331. package/dist/permissions-report.js +120 -0
  332. package/dist/pipeline/consistency-checker.d.ts +8 -0
  333. package/dist/pipeline/consistency-checker.d.ts.map +1 -0
  334. package/dist/pipeline/consistency-checker.js +40 -0
  335. package/dist/pipeline/example-generator.d.ts +4 -0
  336. package/dist/pipeline/example-generator.d.ts.map +1 -0
  337. package/dist/pipeline/example-generator.js +64 -0
  338. package/dist/pipeline/hooks.d.ts +68 -0
  339. package/dist/pipeline/hooks.d.ts.map +1 -0
  340. package/dist/pipeline/hooks.js +377 -0
  341. package/dist/pipeline/sections.d.ts +41 -0
  342. package/dist/pipeline/sections.d.ts.map +1 -0
  343. package/dist/pipeline/sections.js +244 -0
  344. package/dist/pipeline/verification.d.ts +12 -0
  345. package/dist/pipeline/verification.d.ts.map +1 -0
  346. package/dist/pipeline/verification.js +138 -0
  347. package/dist/privacy-report.d.ts +24 -0
  348. package/dist/privacy-report.d.ts.map +1 -0
  349. package/dist/privacy-report.js +78 -0
  350. package/dist/providers/presets.d.ts +25 -0
  351. package/dist/providers/presets.d.ts.map +1 -0
  352. package/dist/providers/presets.js +205 -0
  353. package/dist/recap.d.ts +38 -0
  354. package/dist/recap.d.ts.map +1 -0
  355. package/dist/recap.js +154 -0
  356. package/dist/repl.d.ts +41 -0
  357. package/dist/repl.d.ts.map +1 -0
  358. package/dist/repl.js +396 -0
  359. package/dist/runtime-paths.d.ts +32 -0
  360. package/dist/runtime-paths.d.ts.map +1 -0
  361. package/dist/runtime-paths.js +134 -0
  362. package/dist/sdk.d.ts +12 -0
  363. package/dist/sdk.d.ts.map +1 -0
  364. package/dist/sdk.js +18 -0
  365. package/dist/session/daemon-session-api.d.ts +22 -0
  366. package/dist/session/daemon-session-api.d.ts.map +1 -0
  367. package/dist/session/daemon-session-api.js +48 -0
  368. package/dist/session/durable-session-supervisor.d.ts +24 -0
  369. package/dist/session/durable-session-supervisor.d.ts.map +1 -0
  370. package/dist/session/durable-session-supervisor.js +195 -0
  371. package/dist/session/goal-controller.d.ts +43 -0
  372. package/dist/session/goal-controller.d.ts.map +1 -0
  373. package/dist/session/goal-controller.js +77 -0
  374. package/dist/session/goal-evaluator.d.ts +29 -0
  375. package/dist/session/goal-evaluator.d.ts.map +1 -0
  376. package/dist/session/goal-evaluator.js +100 -0
  377. package/dist/session/loop-prompt.d.ts +14 -0
  378. package/dist/session/loop-prompt.d.ts.map +1 -0
  379. package/dist/session/loop-prompt.js +49 -0
  380. package/dist/session/session-goal-manager.d.ts +51 -0
  381. package/dist/session/session-goal-manager.d.ts.map +1 -0
  382. package/dist/session/session-goal-manager.js +118 -0
  383. package/dist/session/session-registry.d.ts +40 -0
  384. package/dist/session/session-registry.d.ts.map +1 -0
  385. package/dist/session/session-registry.js +124 -0
  386. package/dist/session/session-scheduler.d.ts +59 -0
  387. package/dist/session/session-scheduler.d.ts.map +1 -0
  388. package/dist/session/session-scheduler.js +170 -0
  389. package/dist/session-checkpoint-report.d.ts +22 -0
  390. package/dist/session-checkpoint-report.d.ts.map +1 -0
  391. package/dist/session-checkpoint-report.js +99 -0
  392. package/dist/session-export-index.d.ts +23 -0
  393. package/dist/session-export-index.d.ts.map +1 -0
  394. package/dist/session-export-index.js +96 -0
  395. package/dist/session-export.d.ts +28 -0
  396. package/dist/session-export.d.ts.map +1 -0
  397. package/dist/session-export.js +99 -0
  398. package/dist/session-goal-report.d.ts +37 -0
  399. package/dist/session-goal-report.d.ts.map +1 -0
  400. package/dist/session-goal-report.js +219 -0
  401. package/dist/session-list-report.d.ts +16 -0
  402. package/dist/session-list-report.d.ts.map +1 -0
  403. package/dist/session-list-report.js +60 -0
  404. package/dist/session-task-report.d.ts +23 -0
  405. package/dist/session-task-report.d.ts.map +1 -0
  406. package/dist/session-task-report.js +183 -0
  407. package/dist/shortcuts.d.ts +2 -0
  408. package/dist/shortcuts.d.ts.map +1 -0
  409. package/dist/shortcuts.js +32 -0
  410. package/dist/skills/registry.d.ts +8 -0
  411. package/dist/skills/registry.d.ts.map +1 -0
  412. package/dist/skills/registry.js +119 -0
  413. package/dist/skills/types.d.ts +7 -0
  414. package/dist/skills/types.d.ts.map +1 -0
  415. package/dist/skills/types.js +4 -0
  416. package/dist/statusline.d.ts +37 -0
  417. package/dist/statusline.d.ts.map +1 -0
  418. package/dist/statusline.js +207 -0
  419. package/dist/tools/bash.d.ts +10 -0
  420. package/dist/tools/bash.d.ts.map +1 -0
  421. package/dist/tools/bash.js +278 -0
  422. package/dist/tools/browser-fetch.d.ts +7 -0
  423. package/dist/tools/browser-fetch.d.ts.map +1 -0
  424. package/dist/tools/browser-fetch.js +133 -0
  425. package/dist/tools/error-utils.d.ts +9 -0
  426. package/dist/tools/error-utils.d.ts.map +1 -0
  427. package/dist/tools/error-utils.js +35 -0
  428. package/dist/tools/index.d.ts +27 -0
  429. package/dist/tools/index.d.ts.map +1 -0
  430. package/dist/tools/index.js +86 -0
  431. package/dist/tools/patch.d.ts +22 -0
  432. package/dist/tools/patch.d.ts.map +1 -0
  433. package/dist/tools/patch.js +458 -0
  434. package/dist/tools/planner.d.ts +7 -0
  435. package/dist/tools/planner.d.ts.map +1 -0
  436. package/dist/tools/planner.js +148 -0
  437. package/dist/tools/read.d.ts +8 -0
  438. package/dist/tools/read.d.ts.map +1 -0
  439. package/dist/tools/read.js +114 -0
  440. package/dist/tools/search.d.ts +11 -0
  441. package/dist/tools/search.d.ts.map +1 -0
  442. package/dist/tools/search.js +636 -0
  443. package/dist/tools/skill.d.ts +8 -0
  444. package/dist/tools/skill.d.ts.map +1 -0
  445. package/dist/tools/skill.js +181 -0
  446. package/dist/tools/subtask.d.ts +9 -0
  447. package/dist/tools/subtask.d.ts.map +1 -0
  448. package/dist/tools/subtask.js +120 -0
  449. package/dist/tools/todo.d.ts +8 -0
  450. package/dist/tools/todo.d.ts.map +1 -0
  451. package/dist/tools/todo.js +170 -0
  452. package/dist/tools/url-fetch.d.ts +11 -0
  453. package/dist/tools/url-fetch.d.ts.map +1 -0
  454. package/dist/tools/url-fetch.js +208 -0
  455. package/dist/tools/vision-analyze.d.ts +9 -0
  456. package/dist/tools/vision-analyze.d.ts.map +1 -0
  457. package/dist/tools/vision-analyze.js +179 -0
  458. package/dist/tools/write.d.ts +7 -0
  459. package/dist/tools/write.d.ts.map +1 -0
  460. package/dist/tools/write.js +128 -0
  461. package/dist/tui/chrome.d.ts +7 -0
  462. package/dist/tui/chrome.d.ts.map +1 -0
  463. package/dist/tui/chrome.js +22 -0
  464. package/dist/tui/input-buffer.d.ts +37 -0
  465. package/dist/tui/input-buffer.d.ts.map +1 -0
  466. package/dist/tui/input-buffer.js +228 -0
  467. package/dist/tui/input-history.d.ts +11 -0
  468. package/dist/tui/input-history.d.ts.map +1 -0
  469. package/dist/tui/input-history.js +69 -0
  470. package/dist/tui/input-queue.d.ts +27 -0
  471. package/dist/tui/input-queue.d.ts.map +1 -0
  472. package/dist/tui/input-queue.js +85 -0
  473. package/dist/tui/markdown.d.ts +10 -0
  474. package/dist/tui/markdown.d.ts.map +1 -0
  475. package/dist/tui/markdown.js +396 -0
  476. package/dist/tui/progress.d.ts +3 -0
  477. package/dist/tui/progress.d.ts.map +1 -0
  478. package/dist/tui/progress.js +13 -0
  479. package/dist/tui/shell.d.ts +67 -0
  480. package/dist/tui/shell.d.ts.map +1 -0
  481. package/dist/tui/shell.js +271 -0
  482. package/dist/tui/streaming-repl.d.ts +39 -0
  483. package/dist/tui/streaming-repl.d.ts.map +1 -0
  484. package/dist/tui/streaming-repl.js +460 -0
  485. package/dist/tui/streaming-tui.d.ts +174 -0
  486. package/dist/tui/streaming-tui.d.ts.map +1 -0
  487. package/dist/tui/streaming-tui.js +1049 -0
  488. package/dist/types.d.ts +221 -0
  489. package/dist/types.d.ts.map +1 -0
  490. package/dist/types.js +37 -0
  491. package/dist/utils/symbol-extractor.d.ts +8 -0
  492. package/dist/utils/symbol-extractor.d.ts.map +1 -0
  493. package/dist/utils/symbol-extractor.js +190 -0
  494. package/dist/workflow-runtime.d.ts +45 -0
  495. package/dist/workflow-runtime.d.ts.map +1 -0
  496. package/dist/workflow-runtime.js +146 -0
  497. package/dist/workflow-types.d.ts +73 -0
  498. package/dist/workflow-types.d.ts.map +1 -0
  499. package/dist/workflow-types.js +34 -0
  500. package/docs/docker.md +66 -0
  501. package/docs/install.md +159 -0
  502. package/docs/sdk.md +67 -0
  503. package/docs/skills.md +51 -0
  504. package/package.json +102 -0
  505. package/scripts/bootstrap.mjs +63 -0
  506. package/scripts/check_image.ps1 +18 -0
  507. package/scripts/clean-dist.mjs +18 -0
  508. package/scripts/copy_image.ps1 +5 -0
  509. package/scripts/eval-smoke.mjs +17 -0
  510. package/skills/examples/repo-triage/SKILL.md +50 -0
  511. package/skills/qling.md +42 -0
  512. package/skills/templates/SKILL.md +37 -0
@@ -0,0 +1,1498 @@
1
+ // ============================================================
2
+ // 轻灵 - Agent Loop v2(整合 Pipeline Hook + Section Prompt + Token Budget)
3
+ // ============================================================
4
+ import axios from "axios";
5
+ import * as os from "os";
6
+ import * as path from "path";
7
+ import * as fs from "fs/promises";
8
+ import { exec } from "child_process";
9
+ import { existsSync } from "fs";
10
+ import { dispatch, ALL_TOOLS, setMCPRegistry } from "./tools/index.js";
11
+ import { HookManager, ToolPipeline } from "./pipeline/hooks.js";
12
+ import { buildDefaultRegistry, buildSystemPrompt, SECTION_IDS, buildRepoMapSection } from "./pipeline/sections.js";
13
+ import { MemoryStore, TokenBudgetManager, extractDreamMemories } from "./memory.js";
14
+ import { WriteAheadLog } from "./memory/wal.js";
15
+ import { extractDreamMemoriesLLM } from "./memory/memory-llm-dream.js";
16
+ import { MCPRegistry } from "./mcp/registry.js";
17
+ import { mcpToolsToNativeDefinitions } from "./mcp/bridge.js";
18
+ import { ApprovalGate, ApprovalRequiredError } from "./guard/approval.js";
19
+ import { MetricsCollector } from "./metrics/collector.js";
20
+ import { AgentTelemetry } from "./metrics/agent-telemetry.js";
21
+ import { VerificationAgent } from "./pipeline/verification.js";
22
+ import { ContextCompactor } from "./context-compactor.js";
23
+ import { KnowledgeAgentAdapter } from "./knowledge-agent.js";
24
+ import { WorkflowRuntime } from "./workflow-runtime.js";
25
+ import { checkToolConsistency } from "./pipeline/consistency-checker.js";
26
+ import { DashboardServer } from "./dashboard-server.js";
27
+ import { DiscoveryRegistry } from "./discovery-registry.js";
28
+ import { MissionManager } from "./mission/manager.js";
29
+ import { getSkillDirs } from "./tools/skill.js";
30
+ import { listSkills } from "./skills/registry.js";
31
+ import { buildSkillsSection } from "./pipeline/sections.js";
32
+ import { applyContentFilter, setCustomPatterns } from "./guard/content-filter.js";
33
+ import { appendGuardAudit } from "./guard.js";
34
+ import { guardConfigFromEnv } from "./config.js";
35
+ import { SessionRegistry, } from "./session/session-registry.js";
36
+ import { apiKeyRequiredForEndpoint, isLoopbackEndpoint, } from "./providers/presets.js";
37
+ import { maybeAutoCommitAfterWrite } from "./git/auto-commit.js";
38
+ const HOME_DIR = os.homedir();
39
+ const DEFAULT_QLING_DIR = path.join(HOME_DIR, ".qling");
40
+ /** 本地 loopback / ollama 允许空 key,用占位符满足 OpenAI 兼容 Authorization 头 */
41
+ function resolveSessionApiKey(raw, endpoint, provider) {
42
+ const trimmed = String(raw ?? "").trim();
43
+ if (trimmed)
44
+ return trimmed;
45
+ if (!apiKeyRequiredForEndpoint(endpoint, provider) || isLoopbackEndpoint(endpoint)) {
46
+ return "local";
47
+ }
48
+ return "";
49
+ }
50
+ export class AgentEventEmitter {
51
+ handlers = new Map();
52
+ on(event, handler) {
53
+ if (!this.handlers.has(event)) {
54
+ this.handlers.set(event, new Set());
55
+ }
56
+ this.handlers.get(event).add(handler);
57
+ return () => this.handlers.get(event)?.delete(handler);
58
+ }
59
+ emit(event, ...args) {
60
+ this.handlers.get(event)?.forEach((h) => h(...args));
61
+ }
62
+ }
63
+ export class AgentLoop extends AgentEventEmitter {
64
+ // --- 核心组件 ---
65
+ client;
66
+ messages = [];
67
+ config;
68
+ turnCount = 0;
69
+ runtimeRootDir;
70
+ memoryDir;
71
+ loggingConfig;
72
+ // v2 新组件
73
+ hookManager;
74
+ pipeline;
75
+ sectionRegistry = buildDefaultRegistry(ALL_TOOLS);
76
+ memoryStore;
77
+ tokenBudget;
78
+ verifier;
79
+ compactor;
80
+ knowledgeAdapter;
81
+ workflowRuntime;
82
+ wal = null;
83
+ memoryWalEnabled = false;
84
+ memoryDreamLLMEnabled = false;
85
+ memoryDreamTurnThreshold = 24;
86
+ memoryMaxEntries = 1000;
87
+ mcpRegistry = null;
88
+ approvalGate;
89
+ metricsCollector = null;
90
+ metricsFlushTimer = null;
91
+ dashboardServer = null;
92
+ discoveryRegistry;
93
+ missionManager;
94
+ telemetry = null;
95
+ channel = null;
96
+ sessionId;
97
+ sessionCreatedAt;
98
+ sessionRegistry;
99
+ guardConfig;
100
+ verificationCommand = null;
101
+ // --- v0.3 Getters (Management) ---
102
+ getWorkflowRuntime() { return this.workflowRuntime; }
103
+ getMemoryStore() { return this.memoryStore; }
104
+ getDiscoveryRegistry() { return this.discoveryRegistry; }
105
+ getMissionManager() { return this.missionManager; }
106
+ getRuntimeRootDir() { return this.runtimeRootDir; }
107
+ getWorkspaceDir() { return this.config.runtime?.workspaceDir ?? process.cwd(); }
108
+ getMessagesSnapshot() { return this.messages.map((message) => ({ ...message })); }
109
+ getVerificationCommand() { return this.verificationCommand; }
110
+ async setVerificationCommand(cmd) {
111
+ this.verificationCommand = cmd;
112
+ await this.persistVerificationCommand();
113
+ }
114
+ getSessionStats() {
115
+ return {
116
+ sessionId: this.sessionId,
117
+ turnCount: this.turnCount,
118
+ tokens: this.sessionTokens,
119
+ tokenSource: this.tokenUsageSource,
120
+ compactions: this.compactionCount,
121
+ };
122
+ }
123
+ getSessionSummary() {
124
+ return {
125
+ name: this.sessionId,
126
+ sessionId: this.sessionId,
127
+ workspaceDir: this.getWorkspaceDir(),
128
+ createdAt: this.sessionCreatedAt,
129
+ updatedAt: new Date().toISOString(),
130
+ turnCount: this.turnCount,
131
+ messageCount: this.messages.length,
132
+ sessionTokens: this.sessionTokens,
133
+ compactionCount: this.compactionCount,
134
+ };
135
+ }
136
+ /** 状态机恢复后的内部同步 */
137
+ syncWorkflowState(checkpoint) {
138
+ if (checkpoint.history) {
139
+ this.turnCount = checkpoint.history.length;
140
+ }
141
+ if (checkpoint.contextSnapshot) {
142
+ this.messages = checkpoint.contextSnapshot;
143
+ }
144
+ }
145
+ // Token 追踪(仅计增量,避免每轮重复计算整个上下文)
146
+ sessionTokens = 0;
147
+ tokenUsageSource = "unknown";
148
+ initPromise;
149
+ // 轻量观测指标
150
+ compactionCount = 0;
151
+ retryCountTotal = 0;
152
+ toolCallTotal = 0;
153
+ toolFailureTotal = 0;
154
+ constructor(config = {}) {
155
+ super();
156
+ const provider = config.provider ?? process.env.QLING_LLM_PROVIDER ?? "deepseek";
157
+ const endpoint = config.endpoint ??
158
+ process.env.QLING_LLM_ENDPOINT ??
159
+ process.env.OPENAI_BASE_URL ??
160
+ process.env.DEEPSEEK_BASE_URL ??
161
+ (provider === "openai"
162
+ ? "https://api.openai.com/v1"
163
+ : provider === "ollama" || provider === "local"
164
+ ? "http://localhost:11434/v1"
165
+ : "https://api.deepseek.com");
166
+ const rawApiKey = config.apiKey ??
167
+ process.env.QLING_LLM_API_KEY ??
168
+ process.env.DEEPSEEK_API_KEY ??
169
+ process.env.OPENAI_API_KEY ??
170
+ "";
171
+ const apiKey = resolveSessionApiKey(rawApiKey, endpoint, provider);
172
+ if (!apiKey) {
173
+ throw new Error("Missing API key (expected config.apiKey / QLING_LLM_API_KEY / DEEPSEEK_API_KEY / OPENAI_API_KEY)。本地 Ollama 可将 endpoint 设为 http://localhost:11434/v1 以跳过密钥。");
174
+ }
175
+ this.runtimeRootDir = path.resolve(config.runtime?.fileStateDir ??
176
+ process.env.QLING_FILE_STATE_DIR ??
177
+ DEFAULT_QLING_DIR);
178
+ this.memoryDir = path.join(this.runtimeRootDir, "memory");
179
+ this.loggingConfig = {
180
+ level: config.logging?.level ?? "info",
181
+ format: config.logging?.format ?? "text",
182
+ inspectPrompt: config.logging?.inspectPrompt ?? false,
183
+ inspectRequest: config.logging?.inspectRequest ?? false,
184
+ inspectDumpDir: config.logging?.inspectDumpDir ?? path.join(this.runtimeRootDir, "inspect"),
185
+ };
186
+ this.config = {
187
+ apiKey,
188
+ provider,
189
+ endpoint,
190
+ model: config.model ?? "deepseek-chat",
191
+ systemPrompt: config.systemPrompt ?? "",
192
+ maxIterations: config.maxIterations ?? 50,
193
+ tools: config.tools ?? ALL_TOOLS,
194
+ tokenBudget: config.tokenBudget ?? {
195
+ maxTokens: 120_000,
196
+ nudgeThreshold: 0.2,
197
+ totalBudget: 120_000,
198
+ },
199
+ runtime: {
200
+ workspaceDir: config.runtime?.workspaceDir ?? process.env.QLING_WORKSPACE_DIR ?? process.cwd(),
201
+ fileCacheDir: config.runtime?.fileCacheDir ??
202
+ process.env.QLING_FILE_CACHE_DIR ??
203
+ path.join(this.runtimeRootDir, "cache"),
204
+ fileStateDir: this.runtimeRootDir,
205
+ maxSteps: config.runtime?.maxSteps ?? 50,
206
+ parseRetries: config.runtime?.parseRetries ?? 2,
207
+ maxTokenBudget: config.runtime?.maxTokenBudget ?? 120_000,
208
+ toolRepeatLimit: config.runtime?.toolRepeatLimit ?? 6,
209
+ timeoutMs: config.runtime?.timeoutMs ?? 300_000,
210
+ },
211
+ logging: this.loggingConfig,
212
+ };
213
+ this.sectionRegistry = buildDefaultRegistry(this.config.tools);
214
+ this.sessionId = "session-" + Date.now();
215
+ this.sessionCreatedAt = new Date().toISOString();
216
+ this.approvalGate = new ApprovalGate();
217
+ this.guardConfig = guardConfigFromEnv();
218
+ this.sessionRegistry = new SessionRegistry({ stateDir: this.runtimeRootDir });
219
+ // 初始化 v2 组件
220
+ this.hookManager = new HookManager(this.config.tools, this.guardConfig);
221
+ this.pipeline = new ToolPipeline(this.config.tools, this.hookManager);
222
+ this.pipeline.setSessionId(this.sessionId);
223
+ this.memoryStore = new MemoryStore(this.memoryDir, {
224
+ workspaceDir: this.config.runtime?.workspaceDir || undefined,
225
+ });
226
+ this.tokenBudget = new TokenBudgetManager(this.config.tokenBudget?.totalBudget ?? 120_000, this.config.tokenBudget?.nudgeThreshold ?? 0.2);
227
+ this.verifier = new VerificationAgent(apiKey, this.config.model);
228
+ this.compactor = new ContextCompactor(6000, this.config.model);
229
+ this.knowledgeAdapter = new KnowledgeAgentAdapter(this.memoryStore);
230
+ // v0.3 Workflow Runtime
231
+ this.workflowRuntime = new WorkflowRuntime(path.join(this.runtimeRootDir, "workflows"));
232
+ // v0.3 Discovery Registry
233
+ const discoverySources = [];
234
+ try {
235
+ const localDirs = JSON.parse(process.env.QLING_DISCOVERY_LOCAL_DIRS || "[]");
236
+ localDirs.forEach((dir, i) => {
237
+ discoverySources.push({ id: `local-${i}`, uri: dir, type: "local" });
238
+ });
239
+ const remoteManifests = JSON.parse(process.env.QLING_DISCOVERY_REMOTE_MANIFESTS || "[]");
240
+ remoteManifests.forEach((url, i) => {
241
+ discoverySources.push({ id: `remote-${i}`, uri: url, type: "remote" });
242
+ });
243
+ }
244
+ catch {
245
+ // ignore parse errors
246
+ }
247
+ this.discoveryRegistry = new DiscoveryRegistry(discoverySources);
248
+ this.missionManager = new MissionManager(this.runtimeRootDir);
249
+ // HTTP client + retry interceptor
250
+ this.client = axios.create({
251
+ baseURL: endpoint,
252
+ headers: {
253
+ Authorization: "Bearer " + this.config.apiKey,
254
+ "Content-Type": "application/json",
255
+ },
256
+ timeout: this.resolveLlmRequestTimeout(),
257
+ });
258
+ this.client.interceptors.response.use((response) => response, async (err) => {
259
+ const cfg = err.config;
260
+ const maxRetries = 3;
261
+ cfg.__retryCount = cfg.__retryCount ?? 0;
262
+ // 只在 429 / 500-503 / ECONNRESET / ETIMEDOUT 时重试
263
+ const status = err.response?.status;
264
+ const shouldRetry = (!err.response || status === 429 || (status >= 500 && status <= 503)) &&
265
+ cfg.__retryCount < maxRetries;
266
+ if (shouldRetry) {
267
+ cfg.__retryCount++;
268
+ this.retryCountTotal++;
269
+ const delay = Math.min(1000 * Math.pow(2, cfg.__retryCount - 1), 10_000);
270
+ await new Promise((r) => setTimeout(r, delay));
271
+ return this.client(cfg);
272
+ }
273
+ return Promise.reject(err);
274
+ });
275
+ // 初始化系统(存储 promise,在 run() 中 await)
276
+ this.initPromise = this.init();
277
+ }
278
+ async waitForInit() {
279
+ return this.initPromise;
280
+ }
281
+ async init() {
282
+ await fs.mkdir(this.runtimeRootDir, { recursive: true });
283
+ await fs.mkdir(this.memoryDir, { recursive: true });
284
+ await this.missionManager.init();
285
+ await this.loadVerificationCommand();
286
+ // v0.3 Sync dynamic discovery
287
+ if (process.env.QLING_FEATURES_DYNAMIC_DISCOVERY === "true") {
288
+ console.error("🔍 正在同步动态插件与技能...");
289
+ await this.discoveryRegistry.syncAll();
290
+ const discoveredTools = this.discoveryRegistry.getDiscoveredTools();
291
+ if (discoveredTools.length > 0) {
292
+ this.config.tools = [...this.config.tools, ...discoveredTools];
293
+ console.error(`📦 已发现 ${discoveredTools.length} 个动态工具`);
294
+ }
295
+ }
296
+ if (this.loggingConfig.inspectPrompt || this.loggingConfig.inspectRequest) {
297
+ await fs.mkdir(this.loggingConfig.inspectDumpDir, { recursive: true });
298
+ }
299
+ // WAL initialization
300
+ const walEnabled = process.env.QLING_MEMORY_WAL_ENABLED !== "false";
301
+ const projectionIntervalRaw = Number(process.env.QLING_MEMORY_PROJECTION_INTERVAL_MS ?? "5000");
302
+ const projectionInterval = Number.isFinite(projectionIntervalRaw) && projectionIntervalRaw > 0
303
+ ? projectionIntervalRaw
304
+ : 5000;
305
+ const dreamLLM = process.env.QLING_MEMORY_DREAM_LLM_ENABLED !== "false";
306
+ const dreamThresholdRaw = Number(process.env.QLING_MEMORY_DREAM_TURN_THRESHOLD ?? "24");
307
+ const dreamThreshold = Number.isFinite(dreamThresholdRaw) && dreamThresholdRaw > 0
308
+ ? dreamThresholdRaw
309
+ : 24;
310
+ const memoryMaxEntriesRaw = Number(process.env.QLING_MEMORY_MAX_MEMORIES ?? "1000");
311
+ this.memoryMaxEntries =
312
+ Number.isFinite(memoryMaxEntriesRaw) && memoryMaxEntriesRaw > 0
313
+ ? Math.floor(memoryMaxEntriesRaw)
314
+ : 1000;
315
+ this.memoryWalEnabled = walEnabled;
316
+ this.memoryDreamLLMEnabled = dreamLLM;
317
+ this.memoryDreamTurnThreshold = dreamThreshold;
318
+ if (walEnabled) {
319
+ try {
320
+ const walDir = path.join(this.memoryStore.getWorkspaceMemoryDir(), "wal");
321
+ this.wal = new WriteAheadLog(walDir);
322
+ await this.wal.init();
323
+ this.memoryStore.setWAL(this.wal, { intervalMs: projectionInterval });
324
+ // v0.3 语义记忆初始化 (v0.5 升级为认知引擎)
325
+ const semanticEnabled = process.env.QLING_FEATURES_SEMANTIC_MEMORY === "true";
326
+ if (semanticEnabled) {
327
+ const { CognitiveIndex } = await import("./memory/cognitive-index.js");
328
+ const { EmbeddingClient } = await import("./memory/embedding.js");
329
+ const cognitiveIndex = new CognitiveIndex(this.memoryStore.getWorkspaceMemoryDir());
330
+ let embedEndpoint = process.env.QLING_MEMORY_SEMANTIC_ENDPOINT || this.config.endpoint;
331
+ if (!embedEndpoint) {
332
+ embedEndpoint = this.config.provider === "openai" ? "https://api.openai.com/v1" : "https://api.deepseek.com/v1";
333
+ }
334
+ else if (!/\/v1\/?$/.test(embedEndpoint)) {
335
+ embedEndpoint = embedEndpoint.replace(/\/$/, "") + "/v1";
336
+ }
337
+ const embeddingClient = new EmbeddingClient({
338
+ apiKey: process.env.QLING_MEMORY_SEMANTIC_API_KEY || this.config.apiKey,
339
+ endpoint: embedEndpoint,
340
+ model: process.env.QLING_MEMORY_SEMANTIC_MODEL || "text-embedding-3-small",
341
+ dimensions: Number(process.env.QLING_MEMORY_SEMANTIC_DIM) || 1536,
342
+ });
343
+ this.memoryStore.setCognitiveIndex(cognitiveIndex, embeddingClient);
344
+ console.error("🧠 认知引擎模块已启动 (Triple-Path Retrieval Mode)");
345
+ }
346
+ this.memoryStore.startProjection();
347
+ console.error("[Memory] WAL enabled, projection interval=" + projectionInterval + "ms");
348
+ }
349
+ catch (err) {
350
+ console.error("[Memory] WAL init failed, falling back to direct writes: " + err.message);
351
+ this.wal = null;
352
+ this.memoryWalEnabled = false;
353
+ }
354
+ }
355
+ try {
356
+ await this.memoryStore.init();
357
+ }
358
+ catch {
359
+ // ignore
360
+ }
361
+ // Metrics & Dashboard initialization
362
+ const metricsEnabled = process.env.QLING_METRICS_ENABLED === "true";
363
+ const dashboardEnabled = process.env.QLING_FEATURES_DASHBOARD === "true";
364
+ if (metricsEnabled || dashboardEnabled) {
365
+ try {
366
+ const metricsDir = path.resolve(process.env.QLING_METRICS_DIR ?? path.join(this.runtimeRootDir, "metrics"));
367
+ const flushIntervalRaw = Number(process.env.QLING_METRICS_FLUSH_INTERVAL_MS ?? "10000");
368
+ const flushIntervalMs = Number.isFinite(flushIntervalRaw) && flushIntervalRaw > 0
369
+ ? flushIntervalRaw
370
+ : 10000;
371
+ this.metricsCollector = new MetricsCollector(metricsDir, this.sessionId, flushIntervalMs);
372
+ await this.metricsCollector.init();
373
+ this.metricsFlushTimer = this.metricsCollector.startAutoFlush();
374
+ this.telemetry = new AgentTelemetry(this.metricsCollector, this.sessionId);
375
+ if (metricsEnabled) {
376
+ console.error("[Metrics] enabled, dir=" + metricsDir);
377
+ }
378
+ // v0.3 Dashboard Server
379
+ if (dashboardEnabled) {
380
+ this.dashboardServer = new DashboardServer({
381
+ port: Number(process.env.QLING_DASHBOARD_PORT) || 9999,
382
+ collector: this.metricsCollector,
383
+ workflowRuntime: this.workflowRuntime,
384
+ agentLoop: this,
385
+ });
386
+ try {
387
+ await this.dashboardServer.start();
388
+ }
389
+ catch (serverErr) {
390
+ console.warn(`⚠️ Dashboard 启动跳过: ${serverErr.message}`);
391
+ }
392
+ }
393
+ }
394
+ catch (err) {
395
+ console.error("[Metrics/Dashboard] init failed: " + err.message);
396
+ }
397
+ }
398
+ // MCP initialization
399
+ const mcpServersRaw = process.env.QLING_MCP_SERVERS;
400
+ if (mcpServersRaw) {
401
+ try {
402
+ const mcpConnTimeoutRaw = Number(process.env.QLING_MCP_CONNECTION_TIMEOUT_MS ?? "10000");
403
+ const mcpCallTimeoutRaw = Number(process.env.QLING_MCP_CALL_TIMEOUT_MS ?? "30000");
404
+ const mcpConnTimeout = Number.isFinite(mcpConnTimeoutRaw) && mcpConnTimeoutRaw > 0 ? mcpConnTimeoutRaw : 10000;
405
+ const mcpCallTimeout = Number.isFinite(mcpCallTimeoutRaw) && mcpCallTimeoutRaw > 0 ? mcpCallTimeoutRaw : 30000;
406
+ const servers = JSON.parse(mcpServersRaw);
407
+ const enabled = Object.values(servers).filter((s) => s.enabled);
408
+ if (enabled.length > 0) {
409
+ this.mcpRegistry = new MCPRegistry({
410
+ connection: mcpConnTimeout,
411
+ call: mcpCallTimeout,
412
+ });
413
+ for (const s of enabled) {
414
+ this.mcpRegistry.registerServer(s);
415
+ }
416
+ setMCPRegistry(this.mcpRegistry);
417
+ const results = await this.mcpRegistry.connectAll();
418
+ const mcpTools = mcpToolsToNativeDefinitions(this.mcpRegistry.getAllTools());
419
+ if (mcpTools.length > 0) {
420
+ this.config.tools = [...this.config.tools, ...mcpTools];
421
+ console.error("[MCP] Connected " + results.filter((r) => r.status === "connected").length + " servers, " + mcpTools.length + " tools");
422
+ }
423
+ }
424
+ }
425
+ catch (err) {
426
+ console.error("[MCP] Init failed: " + err.message);
427
+ }
428
+ }
429
+ // Skills section registration
430
+ try {
431
+ const skillDirs = getSkillDirs();
432
+ const skills = await listSkills(skillDirs);
433
+ this.sectionRegistry.register(buildSkillsSection(skills));
434
+ }
435
+ catch {
436
+ // ignore
437
+ }
438
+ // Guard M2: content filter custom patterns
439
+ if (this.guardConfig.content_filter?.custom_patterns?.length > 0) {
440
+ setCustomPatterns(this.guardConfig.content_filter.custom_patterns);
441
+ }
442
+ }
443
+ // --- Public API ---
444
+ addUserMessage(content) {
445
+ this.messages.push({ role: "user", content });
446
+ this.knowledgeAdapter.onUserMessage(content);
447
+ }
448
+ async run() {
449
+ await this.initPromise;
450
+ const toolSignatureCounts = new Map();
451
+ const toolRepeatLimit = Math.max(1, this.config.runtime?.toolRepeatLimit ?? 6);
452
+ let selfHealingCount = 0;
453
+ for (let i = 0; i < this.config.maxIterations; i++) {
454
+ this.turnCount++;
455
+ // 保存本轮用户消息(在 assistant/tool 消息 push 之前)
456
+ let lastUserMsg = "";
457
+ for (let k = this.messages.length - 1; k >= 0; k--) {
458
+ if (this.messages[k].role === "user") {
459
+ lastUserMsg = this.messages[k].content;
460
+ break;
461
+ }
462
+ }
463
+ // 0. Token Budget Nudge
464
+ if (this.tokenBudget.shouldNudge()) {
465
+ const nudge = this.tokenBudget.buildNudgeMessage();
466
+ this.messages.push({ role: "user", content: nudge });
467
+ console.error("\n" + nudge + "\n");
468
+ }
469
+ // 0b. 上下文压缩(超过阈值时触发)
470
+ if (this.compactor.needsCompaction(this.messages)) {
471
+ console.error("\n📦 上下文压缩中...(" + this.messages.length + " 条消息)");
472
+ const compacted = await this.compactor.compact(this.messages);
473
+ this.messages = compacted;
474
+ this.compactionCount++;
475
+ console.error("📦 压缩完成 → " + this.messages.length + " 条消息\n");
476
+ }
477
+ // 0c. 冲突/注入扫描(Lesson 12: Context Validation)
478
+ const conflicts = this.compactor.scanConflicts(this.messages);
479
+ if (conflicts.length > 0) {
480
+ console.error("⚠️ 检测到 " + conflicts.length + " 处指令冲突");
481
+ }
482
+ const poison = this.compactor.scanPoison(this.messages);
483
+ if (poison.length > 0) {
484
+ console.error("🚨 检测到 " + poison.length + " 处可能的提示注入");
485
+ }
486
+ // 1. 构建 system prompt(动态sections)
487
+ const systemPrompt = await this.buildSystemPrompt();
488
+ // v0.3 Workflow Checkpoint: Update context
489
+ if (process.env.QLING_FEATURES_WORKFLOW_RUNTIME === "true") {
490
+ await this.workflowRuntime.updateContext(this.messages);
491
+ }
492
+ // 2. 准备本地 token fallback;若 provider 返回 usage,优先采用 provider 值。
493
+ const lastMsg = this.messages[this.messages.length - 1];
494
+ const estimatedRoundTokens = (lastMsg?.content?.length ?? 0) * 4 + (systemPrompt.length * 0.5);
495
+ // 3. API 调用
496
+ const { content, tool_calls, usage } = await this.chat(systemPrompt);
497
+ const tokenUsage = this.resolveRoundTokenUsage(usage, estimatedRoundTokens);
498
+ const roundTokens = tokenUsage.tokens;
499
+ this.tokenUsageSource = tokenUsage.source;
500
+ this.sessionTokens += roundTokens;
501
+ this.tokenBudget.addUsage(roundTokens);
502
+ this.messages.push({ role: "assistant", content, tool_calls });
503
+ this.emit("thinking", content || "正在思考...");
504
+ // 4. 无工具调用 → 结束
505
+ if (!tool_calls || tool_calls.length === 0) {
506
+ // 知识观察:助手消息
507
+ this.knowledgeAdapter.onAssistantMessage(content);
508
+ // Auto-dream 检查
509
+ await this.checkAutoDream();
510
+ // 知识观察:回合结束
511
+ await this.knowledgeAdapter.onTurnEnd(this.turnCount);
512
+ this.logTurnTelemetry({ turn: this.turnCount, toolCalls: 0, toolFailures: 0 });
513
+ return content;
514
+ }
515
+ // 5. Pipeline 执行(Hook → 工具)
516
+ console.error("\n🔧 执行 " + tool_calls.length + " 个工具...\n");
517
+ const preparedCalls = [];
518
+ for (const tc of tool_calls) {
519
+ const parseResult = this.parseToolArguments(tc.function.arguments);
520
+ if (!parseResult.ok) {
521
+ preparedCalls.push({
522
+ call: {
523
+ id: tc.id,
524
+ name: tc.function.name,
525
+ arguments: {},
526
+ },
527
+ immediateResult: {
528
+ tool_call_id: tc.id,
529
+ output: `Error: [TOOL_INVALID_ARGUMENTS] ${parseResult.error}`,
530
+ is_error: true,
531
+ error: {
532
+ code: "TOOL_INVALID_ARGUMENTS",
533
+ message: parseResult.error,
534
+ category: "runtime",
535
+ },
536
+ },
537
+ });
538
+ continue;
539
+ }
540
+ const call = {
541
+ id: tc.id,
542
+ name: tc.function.name,
543
+ arguments: parseResult.value,
544
+ };
545
+ const signature = this.buildToolSignature(call.name, call.arguments);
546
+ const repeatCount = (toolSignatureCounts.get(signature) ?? 0) + 1;
547
+ toolSignatureCounts.set(signature, repeatCount);
548
+ if (repeatCount > toolRepeatLimit) {
549
+ preparedCalls.push({
550
+ call,
551
+ immediateResult: {
552
+ tool_call_id: tc.id,
553
+ output: `Error: [TOOL_REPEAT_LIMIT_EXCEEDED] ` +
554
+ `tool '${call.name}' exceeded repeat limit (${toolRepeatLimit})`,
555
+ is_error: true,
556
+ error: {
557
+ code: "TOOL_REPEAT_LIMIT_EXCEEDED",
558
+ message: `tool '${call.name}' exceeded repeat limit (${toolRepeatLimit})`,
559
+ category: "guard",
560
+ },
561
+ },
562
+ });
563
+ continue;
564
+ }
565
+ preparedCalls.push({ call });
566
+ }
567
+ // v0.3 Workflow Checkpoint: Set pending tools
568
+ if (process.env.QLING_FEATURES_WORKFLOW_RUNTIME === "true") {
569
+ await this.workflowRuntime.setPendingTools(preparedCalls.map(p => p.call));
570
+ }
571
+ let turnToolCalls = 0;
572
+ let turnToolFailures = 0;
573
+ for (let j = 0; j < preparedCalls.length; j++) {
574
+ const prepared = preparedCalls[j];
575
+ const tc = prepared.call;
576
+ turnToolCalls++;
577
+ // v0.5 M2: Self-Reflective Loop (Inner Monologue)
578
+ // 针对高风险工具进行预演评估
579
+ if (tc.name === "write" || tc.name === "bash") {
580
+ const reflection = await this.reflectiveThink(tc);
581
+ if (reflection.decision === "block") {
582
+ console.error(`🚨 [内省阻断] ${reflection.reason}`);
583
+ this.messages.push({
584
+ role: "tool",
585
+ content: JSON.stringify({
586
+ tool_call_id: tc.id,
587
+ output: `Error: [REFLECTION_BLOCKED] ${reflection.reason}. This action was deemed too risky.`,
588
+ is_error: true,
589
+ }),
590
+ tool_call_id: tc.id,
591
+ });
592
+ turnToolFailures++;
593
+ continue;
594
+ }
595
+ else if (reflection.decision === "warn") {
596
+ console.error(`💭 [内省警告] ${reflection.reason}`);
597
+ }
598
+ }
599
+ // v0.3 Tool Spec Boost: Consistency Check
600
+ if (process.env.QLING_FEATURES_TOOL_SPEC_BOOST === "true") {
601
+ const def = this.config.tools.find(t => t.name === tc.name);
602
+ if (def) {
603
+ const check = checkToolConsistency(tc, def);
604
+ if (!check.ok) {
605
+ console.error(`⚠️ [SpecBoost] 检查到幻觉风险: ${tc.name} - ${check.error}`);
606
+ this.messages.push({
607
+ role: "tool",
608
+ content: JSON.stringify({
609
+ tool_call_id: tc.id,
610
+ output: `Error: [TOOL_SPEC_VIOLATION] ${check.error}. Please correct your arguments based on the schema and examples.`,
611
+ is_error: true,
612
+ }),
613
+ tool_call_id: tc.id,
614
+ });
615
+ turnToolFailures++;
616
+ continue;
617
+ }
618
+ if (check.warnings.length > 0) {
619
+ console.warn(`[SpecBoost] 潜在警告: ${check.warnings.join("; ")}`);
620
+ }
621
+ }
622
+ }
623
+ // 知识观察:工具调用前
624
+ this.knowledgeAdapter.onToolCall(tc);
625
+ this.emit("tool_start", tc.name, tc.arguments);
626
+ let result = prepared.immediateResult ?? {
627
+ tool_call_id: tc.id,
628
+ output: "",
629
+ is_error: false,
630
+ };
631
+ if (!prepared.immediateResult) {
632
+ try {
633
+ result = await this.pipeline.execute(tc, (t) => dispatch(t));
634
+ }
635
+ catch (err) {
636
+ if (err instanceof ApprovalRequiredError && this.channel) {
637
+ // Approval flow
638
+ if (process.env.QLING_FEATURES_WORKFLOW_RUNTIME === "true") {
639
+ await this.workflowRuntime.awaitApproval();
640
+ }
641
+ const approvalResponse = await this.approvalGate.requestApproval({
642
+ id: err.toolCallId,
643
+ toolName: err.toolName,
644
+ arguments: tc.arguments,
645
+ reason: err.reasons.join("; "),
646
+ timestamp: Date.now(),
647
+ }, this.channel);
648
+ if (approvalResponse.decision === "allow") {
649
+ result = await dispatch(tc);
650
+ }
651
+ else {
652
+ result = {
653
+ tool_call_id: tc.id,
654
+ output: "[Approval Denied] " + err.reasons.join("; "),
655
+ is_error: true,
656
+ error: { code: "APPROVAL_DENIED", message: "User denied tool execution", category: "permission" },
657
+ };
658
+ turnToolFailures++;
659
+ }
660
+ }
661
+ else {
662
+ result = {
663
+ tool_call_id: tc.id,
664
+ output: err.message,
665
+ is_error: true,
666
+ error: { code: "TOOL_ERROR", message: err.message, category: "runtime" },
667
+ };
668
+ turnToolFailures++;
669
+ }
670
+ }
671
+ }
672
+ else {
673
+ turnToolFailures++;
674
+ }
675
+ // 知识观察:工具结果后
676
+ this.knowledgeAdapter.onToolResult(result, tc.name);
677
+ this.emit("tool_result", tc.name, result.output, result.is_error ?? false);
678
+ // v0.5 M1: Knowledge Graph Linking (Automatic)
679
+ if (!result.is_error && (tc.name === "bash" || tc.name === "write" || tc.name === "read")) {
680
+ let lastUserMsg = "";
681
+ for (let i = this.messages.length - 1; i >= 0; i--) {
682
+ if (this.messages[i].role === "user") {
683
+ lastUserMsg = this.messages[i].content;
684
+ break;
685
+ }
686
+ }
687
+ const taskId = "task_" + Buffer.from(lastUserMsg.slice(0, 10)).toString("hex");
688
+ this.memoryStore.link({ id: taskId, type: "task", label: lastUserMsg.slice(0, 50) }, "uses", { id: "tool_" + tc.name, type: "tool", label: tc.name });
689
+ const args = tc.arguments;
690
+ const targetFile = args.path || args.file || "";
691
+ if (targetFile) {
692
+ this.memoryStore.link({ id: "tool_" + tc.name, type: "tool", label: tc.name }, tc.name === "read" ? "reads" : "writes", { id: "file_" + Buffer.from(targetFile).toString("hex"), type: "file", label: targetFile });
693
+ }
694
+ }
695
+ // Phase 1.2: 可选 git auto-commit(write/patch 成功且非 dry_run)
696
+ if (!result.is_error &&
697
+ (tc.name === "write" || tc.name === "patch") &&
698
+ !tc.arguments?.dry_run) {
699
+ const args = tc.arguments;
700
+ const targetFile = String(args.path || args.file || "").trim();
701
+ if (targetFile) {
702
+ try {
703
+ const ac = await maybeAutoCommitAfterWrite({
704
+ workspaceDir: this.config.runtime?.workspaceDir || process.cwd(),
705
+ filePath: targetFile,
706
+ toolName: tc.name,
707
+ });
708
+ if (ac.mode !== "off") {
709
+ result = {
710
+ ...result,
711
+ output: `${result.output}\n\n[${ac.message}]`,
712
+ };
713
+ }
714
+ }
715
+ catch {
716
+ // auto-commit 失败不影响工具成功语义
717
+ }
718
+ }
719
+ }
720
+ // Guard M2: 内容过滤(工具输出)
721
+ if (this.guardConfig.enabled && this.guardConfig.content_filter?.enabled) {
722
+ const cf = applyContentFilter(result.output, {
723
+ pii: this.guardConfig.content_filter.pii_detection,
724
+ injection: this.guardConfig.content_filter.injection_detection,
725
+ custom: this.guardConfig.content_filter.custom_patterns.length > 0,
726
+ });
727
+ if (cf.blocked) {
728
+ await appendGuardAudit(this.guardConfig, {
729
+ tool: tc.name,
730
+ action: "deny",
731
+ category: "content_filter",
732
+ reason: cf.reason,
733
+ });
734
+ result = {
735
+ ...result,
736
+ output: `[内容过滤] ${cf.reason}: ${(cf.matches ?? []).join(", ")}`,
737
+ is_error: true,
738
+ error: { code: "CONTENT_FILTERED", message: cf.reason ?? "content filtered", category: "guard" },
739
+ };
740
+ turnToolFailures++;
741
+ }
742
+ }
743
+ // 进度展示
744
+ const preview = result.output.split("\n")[0].slice(0, 80);
745
+ const icon = result.is_error ? "❌" : "✅";
746
+ console.error(icon + " " + tc.name + ": " + preview + (result.output.length > 80 ? "..." : ""));
747
+ // v0.3 Workflow Checkpoint: Add result
748
+ if (process.env.QLING_FEATURES_WORKFLOW_RUNTIME === "true") {
749
+ await this.workflowRuntime.addToolResult(result);
750
+ }
751
+ this.messages.push({
752
+ role: "tool",
753
+ content: JSON.stringify(result),
754
+ tool_call_id: tc.id,
755
+ });
756
+ }
757
+ // 6. 验证阶段(针对写操作)
758
+ const hasWrites = preparedCalls.some((t) => t.call.name === "write" || t.call.name === "patch" || t.call.name === "bash");
759
+ if (hasWrites) {
760
+ if (this.verificationCommand) {
761
+ console.error(`\n🔍 正在运行自动验证: ${this.verificationCommand}`);
762
+ const { code, stdout, stderr } = await this.runVerificationCommand(this.verificationCommand);
763
+ if (code !== 0) {
764
+ if (selfHealingCount < 3) {
765
+ selfHealingCount++;
766
+ const truncatedStdout = stdout.length > 2000 ? stdout.slice(-2000) : stdout;
767
+ const truncatedStderr = stderr.length > 2000 ? stderr.slice(-2000) : stderr;
768
+ const errorContent = `【自动验证失败】运行验证命令 \`${this.verificationCommand}\` 失败。\n` +
769
+ `[stdout]\n${truncatedStdout}\n` +
770
+ `[stderr]\n${truncatedStderr}\n` +
771
+ `请分析上述报错信息,并使用 patch 或其它工具修正它。(自愈尝试: ${selfHealingCount}/3)`;
772
+ this.messages.push({ role: "user", content: errorContent });
773
+ console.error(`❌ 自动验证失败,触发第 ${selfHealingCount} 轮自愈调试...`);
774
+ continue;
775
+ }
776
+ else {
777
+ console.error(`❌ 自动自愈达到 3 轮上限,验证命令仍然失败。`);
778
+ return `❌ [自动验证失败] 已达到最多 3 轮自愈上限,验证命令仍然失败。\n最后报错信息:\n${stderr || stdout}`;
779
+ }
780
+ }
781
+ else {
782
+ console.error("✅ 自动验证成功!");
783
+ selfHealingCount = 0; // Reset counter on success
784
+ }
785
+ }
786
+ else {
787
+ await this.verifyLastOperation();
788
+ }
789
+ }
790
+ // 7. Auto-dream 检查
791
+ await this.checkAutoDream();
792
+ // v0.5 M1: Experience Distillation (M1 Core)
793
+ if (this.turnCount > 0 && !preparedCalls.some(p => p.immediateResult?.is_error || (p.call.id && this.messages.some(m => m.tool_call_id === p.call.id && JSON.parse(m.content).is_error)))) {
794
+ const successfulCmds = preparedCalls
795
+ .filter(p => p.call.name === "bash")
796
+ .map(p => p.call.arguments.cmd || p.call.arguments.command);
797
+ if (successfulCmds.length > 0) {
798
+ let lastUserMsg = "";
799
+ for (let i = this.messages.length - 1; i >= 0; i--) {
800
+ if (this.messages[i].role === "user") {
801
+ lastUserMsg = this.messages[i].content;
802
+ break;
803
+ }
804
+ }
805
+ this.memoryStore.addPractice(lastUserMsg.slice(0, 100), successfulCmds, preparedCalls.map(p => p.call.arguments.path || p.call.arguments.file).filter(Boolean));
806
+ console.error(`✨ [认知] 已将 ${successfulCmds.length} 条成功指令蒸馏为最佳实践`);
807
+ }
808
+ }
809
+ // 7b. 记录对话轮次(更新 conversation memory)
810
+ this.memoryStore.addConversationTurn("user", lastUserMsg);
811
+ this.memoryStore.addConversationTurn("assistant", content);
812
+ // 7c. 知识观察:助手消息 + 回合结束
813
+ this.knowledgeAdapter.onAssistantMessage(content);
814
+ await this.knowledgeAdapter.onTurnEnd(this.turnCount);
815
+ this.logTurnTelemetry({
816
+ turn: this.turnCount,
817
+ toolCalls: turnToolCalls,
818
+ toolFailures: turnToolFailures,
819
+ });
820
+ }
821
+ return "⚠️ 达到最大迭代次数,任务未完成。";
822
+ }
823
+ getModel() {
824
+ return this.config.model;
825
+ }
826
+ getProvider() {
827
+ return this.config.provider ?? "unknown";
828
+ }
829
+ getEndpoint() {
830
+ return this.config.endpoint ?? "";
831
+ }
832
+ setModel(model) {
833
+ this.applyLlmSession({ model });
834
+ }
835
+ /**
836
+ * 进程内切换 LLM 会话态(model / provider / endpoint / apiKey)。
837
+ * 默认不写盘;同步更新 QLING_LLM_* 环境变量以便后续子组件读取。
838
+ */
839
+ applyLlmSession(patch) {
840
+ if (typeof patch.provider === "string" && patch.provider.trim()) {
841
+ this.config.provider = patch.provider.trim();
842
+ process.env.QLING_LLM_PROVIDER = this.config.provider;
843
+ }
844
+ if (typeof patch.endpoint === "string" && patch.endpoint.trim()) {
845
+ this.config.endpoint = patch.endpoint.trim();
846
+ process.env.QLING_LLM_ENDPOINT = this.config.endpoint;
847
+ }
848
+ if (typeof patch.model === "string" && patch.model.trim()) {
849
+ this.config.model = patch.model.trim();
850
+ process.env.QLING_LLM_MODEL = this.config.model;
851
+ this.compactor = new ContextCompactor(6000, this.config.model);
852
+ this.verifier = new VerificationAgent(this.config.apiKey, this.config.model);
853
+ }
854
+ if (typeof patch.apiKey === "string") {
855
+ const nextKey = resolveSessionApiKey(patch.apiKey, this.config.endpoint ?? "", this.config.provider ?? "");
856
+ if (nextKey) {
857
+ this.config.apiKey = nextKey;
858
+ }
859
+ }
860
+ else {
861
+ // endpoint/provider 切到本地时,若当前 key 为空则填占位
862
+ const ensured = resolveSessionApiKey(this.config.apiKey ?? "", this.config.endpoint ?? "", this.config.provider ?? "");
863
+ if (ensured)
864
+ this.config.apiKey = ensured;
865
+ }
866
+ return {
867
+ provider: this.config.provider ?? "unknown",
868
+ endpoint: this.config.endpoint ?? "",
869
+ model: this.config.model,
870
+ };
871
+ }
872
+ getToolCount() {
873
+ return this.config.tools.length;
874
+ }
875
+ reset() {
876
+ this.messages = [];
877
+ this.turnCount = 0;
878
+ this.sessionTokens = 0;
879
+ this.tokenUsageSource = "unknown";
880
+ this.tokenBudget.reset();
881
+ this.memoryStore.resetSession();
882
+ this.sectionRegistry.clearCache();
883
+ }
884
+ setChannel(channel) {
885
+ this.channel = channel;
886
+ }
887
+ getSessionId() {
888
+ return this.sessionId;
889
+ }
890
+ getPermissionMode() {
891
+ return this.hookManager.getPermissionDefaultDecision();
892
+ }
893
+ setPermissionMode(mode) {
894
+ this.hookManager.setPermissionDefaultDecision(mode);
895
+ this.guardConfig.permissions.default = mode;
896
+ process.env.QLING_GUARD_PERMISSIONS_DEFAULT = mode;
897
+ }
898
+ isPlanMode() {
899
+ return this.hookManager.isPlanMode();
900
+ }
901
+ setPlanMode(enabled) {
902
+ this.hookManager.setPlanMode(enabled);
903
+ process.env.QLING_PLAN_MODE = enabled ? "1" : "0";
904
+ }
905
+ getSessionMode() {
906
+ return this.isPlanMode() ? "plan" : "agent";
907
+ }
908
+ async compactSessionNow() {
909
+ const beforeCount = this.messages.length;
910
+ const compacted = await this.compactor.compact(this.messages);
911
+ const changed = compacted.length !== beforeCount;
912
+ this.messages = compacted;
913
+ if (changed) {
914
+ this.compactionCount++;
915
+ }
916
+ this.memoryStore.compactPersisted(this.memoryMaxEntries);
917
+ return {
918
+ beforeCount,
919
+ afterCount: this.messages.length,
920
+ changed,
921
+ };
922
+ }
923
+ async shutdown() {
924
+ try {
925
+ await this.initPromise;
926
+ }
927
+ catch {
928
+ // ignore init failure in shutdown path
929
+ }
930
+ this.approvalGate.cancelAll();
931
+ if (this.metricsCollector && this.metricsFlushTimer) {
932
+ this.metricsCollector.stopAutoFlush(this.metricsFlushTimer);
933
+ this.metricsFlushTimer = null;
934
+ }
935
+ if (this.telemetry) {
936
+ this.telemetry.recordSessionEnd();
937
+ await this.telemetry.flush();
938
+ }
939
+ if (this.metricsCollector) {
940
+ const retentionDays = Number(process.env.QLING_METRICS_RETENTION_DAYS ?? "30");
941
+ if (retentionDays > 0) {
942
+ await this.metricsCollector.purgeOldEntries(retentionDays);
943
+ }
944
+ }
945
+ if (this.channel) {
946
+ await this.channel.stop();
947
+ }
948
+ if (this.mcpRegistry) {
949
+ await this.mcpRegistry.disconnectAll();
950
+ }
951
+ if (this.dashboardServer) {
952
+ this.dashboardServer.stop();
953
+ }
954
+ try {
955
+ if (this.messages.length > 0) {
956
+ await this.linkSessionGraph();
957
+ }
958
+ }
959
+ catch (err) {
960
+ console.error("[Memory] linkSessionGraph error:", err);
961
+ }
962
+ await this.memoryStore.shutdown();
963
+ if (this.wal) {
964
+ await this.wal.close();
965
+ }
966
+ }
967
+ async linkSessionGraph() {
968
+ try {
969
+ const cognitiveIndex = this.memoryStore.getCognitiveIndex();
970
+ if (!cognitiveIndex)
971
+ return;
972
+ const sessionId = this.sessionId;
973
+ let summary = "无摘要会话";
974
+ if (this.memoryDreamLLMEnabled && this.messages.length > 0 && this.config.apiKey) {
975
+ try {
976
+ const axios = (await import("axios")).default;
977
+ const userMessages = this.messages.filter(m => m.role === "user").map(m => m.content);
978
+ const brief = userMessages.join("; ").slice(0, 500);
979
+ const resp = await axios.post(this.config.endpoint + "/chat/completions", {
980
+ model: this.config.model,
981
+ messages: [
982
+ { role: "system", content: "你是一个会话摘要助手。请为用户的请求写一句极为简洁的中文总结(不超过20字,例如:修复多行换行渲染与退出超时bug)。只输出总结文本,不要其他内容。" },
983
+ { role: "user", content: brief },
984
+ ],
985
+ max_tokens: 60,
986
+ temperature: 0.3,
987
+ }, {
988
+ headers: {
989
+ Authorization: "Bearer " + this.config.apiKey,
990
+ "Content-Type": "application/json",
991
+ },
992
+ timeout: 10_000,
993
+ });
994
+ const content = resp.data.choices?.[0]?.message?.content?.trim();
995
+ if (content) {
996
+ summary = content;
997
+ }
998
+ }
999
+ catch (err) {
1000
+ summary = "执行了 " + this.turnCount + " 轮交互的任务";
1001
+ }
1002
+ }
1003
+ else {
1004
+ summary = "执行了 " + this.turnCount + " 轮交互的任务";
1005
+ }
1006
+ const fileRegex = /[/\w-]+\.(?:ts|js|py|md|json|yml|yaml|sh|mjs)/g;
1007
+ const files = new Set();
1008
+ for (const msg of this.messages) {
1009
+ let match;
1010
+ while ((match = fileRegex.exec(msg.content)) !== null) {
1011
+ files.add(match[0]);
1012
+ }
1013
+ }
1014
+ const userMessages = this.messages.filter(m => m.role === "user").map(m => m.content);
1015
+ const tasks = [];
1016
+ if (userMessages.length > 0) {
1017
+ tasks.push(userMessages[0].slice(0, 50));
1018
+ }
1019
+ this.memoryStore.linkSessionToEntities(sessionId, summary, Array.from(files), tasks);
1020
+ console.error(`[Memory] 已建立会话图谱关系链: ${summary} (关联了 ${files.size} 个文件, ${tasks.length} 个任务)`);
1021
+ }
1022
+ catch (err) {
1023
+ console.error("[Memory] linkSessionGraph failed:", err.message);
1024
+ }
1025
+ }
1026
+ // --- Session Persistence ---
1027
+ async checkpointSession() {
1028
+ return this.sessionRegistry.save(this.buildSessionSnapshot(this.sessionId));
1029
+ }
1030
+ async saveSession(name) {
1031
+ const sessionName = name ?? "session-" + new Date().toISOString().replace(/[:.]/g, "-");
1032
+ return this.sessionRegistry.save(this.buildSessionSnapshot(sessionName));
1033
+ }
1034
+ async loadSession(name) {
1035
+ return (await this.restoreSession(name)) !== null;
1036
+ }
1037
+ async listSessions() {
1038
+ const sessions = await this.listSessionsDetailed();
1039
+ return sessions.map((session) => `${session.name}.json`);
1040
+ }
1041
+ async listSessionsDetailed() {
1042
+ return this.sessionRegistry.list();
1043
+ }
1044
+ async restoreSession(nameOrSessionId) {
1045
+ const snapshot = await this.sessionRegistry.load(nameOrSessionId);
1046
+ if (!snapshot) {
1047
+ return null;
1048
+ }
1049
+ return this.applySessionSnapshot(snapshot);
1050
+ }
1051
+ async restoreLatestSession() {
1052
+ const snapshot = await this.sessionRegistry.loadLatest();
1053
+ if (!snapshot) {
1054
+ return null;
1055
+ }
1056
+ return this.applySessionSnapshot(snapshot);
1057
+ }
1058
+ // --- Private Methods ---
1059
+ buildSessionSnapshot(name) {
1060
+ return {
1061
+ name,
1062
+ sessionId: this.sessionId,
1063
+ workspaceDir: this.getWorkspaceDir(),
1064
+ createdAt: this.sessionCreatedAt,
1065
+ updatedAt: new Date().toISOString(),
1066
+ messages: this.getMessagesSnapshot(),
1067
+ turnCount: this.turnCount,
1068
+ sessionTokens: this.sessionTokens,
1069
+ compactionCount: this.compactionCount,
1070
+ };
1071
+ }
1072
+ applySessionSnapshot(snapshot) {
1073
+ this.messages = snapshot.messages.map((message) => ({ ...message }));
1074
+ this.turnCount = snapshot.turnCount;
1075
+ this.sessionTokens = snapshot.sessionTokens;
1076
+ this.tokenUsageSource = "unknown";
1077
+ this.compactionCount = snapshot.compactionCount;
1078
+ this.sessionId = snapshot.sessionId;
1079
+ this.sessionCreatedAt = snapshot.createdAt;
1080
+ this.pipeline.setSessionId(this.sessionId);
1081
+ this.tokenBudget.syncUsage(this.sessionTokens);
1082
+ this.memoryStore.resetSession();
1083
+ this.sectionRegistry.clearCache();
1084
+ if (this.config.runtime) {
1085
+ this.config = {
1086
+ ...this.config,
1087
+ runtime: {
1088
+ ...this.config.runtime,
1089
+ workspaceDir: snapshot.workspaceDir ?? this.config.runtime.workspaceDir,
1090
+ },
1091
+ };
1092
+ }
1093
+ return {
1094
+ name: snapshot.name,
1095
+ sessionId: snapshot.sessionId,
1096
+ workspaceDir: snapshot.workspaceDir,
1097
+ createdAt: snapshot.createdAt,
1098
+ updatedAt: snapshot.updatedAt,
1099
+ turnCount: snapshot.turnCount,
1100
+ messageCount: snapshot.messages.length,
1101
+ sessionTokens: snapshot.sessionTokens,
1102
+ compactionCount: snapshot.compactionCount,
1103
+ };
1104
+ }
1105
+ /** 自我反思循环 (v0.5 M2) */
1106
+ async reflectiveThink(tc) {
1107
+ const { buildReflectionPrompt } = await import("./pipeline/sections.js");
1108
+ const prompt = buildReflectionPrompt(tc.name, tc.arguments);
1109
+ try {
1110
+ // 执行一次极简的内部调用进行风险评估
1111
+ const resp = await this.chat(prompt, { max_tokens: 200, temperature: 0 });
1112
+ // 提取 JSON(防止模型输出冗余文字)
1113
+ const jsonStr = resp.content.match(/\{[\s\S]*\}/)?.[0] || "{}";
1114
+ const analysis = JSON.parse(jsonStr);
1115
+ return {
1116
+ decision: analysis.decision || "proceed",
1117
+ reason: analysis.reason || "评估完成。"
1118
+ };
1119
+ }
1120
+ catch {
1121
+ // 降级:启发式检查
1122
+ const args = tc.arguments;
1123
+ const cmd = (args.cmd || args.command || "").toLowerCase();
1124
+ if (cmd.includes("rm ") || cmd.includes("del ")) {
1125
+ return { decision: "warn", reason: "启发式拦截:检测到可能的删除操作。" };
1126
+ }
1127
+ return { decision: "proceed", reason: "启发式通过。" };
1128
+ }
1129
+ }
1130
+ async buildSystemPrompt() {
1131
+ // 加载代码地图并更新 REPOMAP section
1132
+ const cognitiveIndex = this.memoryStore?.getCognitiveIndex?.();
1133
+ if (cognitiveIndex) {
1134
+ try {
1135
+ const symbols = cognitiveIndex.getAllSymbols();
1136
+ this.sectionRegistry.register(buildRepoMapSection(symbols));
1137
+ }
1138
+ catch (err) {
1139
+ // Ignore
1140
+ }
1141
+ }
1142
+ // 更新 token budget section
1143
+ const budgetSec = this.sectionRegistry.get(SECTION_IDS.TOKEN_BUDGET);
1144
+ if (budgetSec) {
1145
+ const used = this.sessionTokens;
1146
+ const max = this.tokenBudget.maxTokens;
1147
+ this.sectionRegistry.register({
1148
+ ...budgetSec,
1149
+ content: "【Token 预算】\n已使用: ~" +
1150
+ used.toLocaleString() +
1151
+ " tokens\n剩余: ~" +
1152
+ (max - used).toLocaleString() +
1153
+ " tokens (" +
1154
+ Math.round(((max - used) / max) * 100) +
1155
+ "%)\n当剩余低于 20% 时,主动精简回复,减少工具调用频率。",
1156
+ dynamic: true,
1157
+ });
1158
+ }
1159
+ // 加载记忆 (v0.3 支持语义异步预取)
1160
+ let memoryStr = "";
1161
+ if (this.messages.length > 0) {
1162
+ let lastUserMsg = "";
1163
+ for (let i = this.messages.length - 1; i >= 0; i--) {
1164
+ if (this.messages[i].role === "user") {
1165
+ lastUserMsg = this.messages[i].content;
1166
+ break;
1167
+ }
1168
+ }
1169
+ const relevant = await this.memoryStore.getRelevant(lastUserMsg, 10);
1170
+ if (relevant.length > 0) {
1171
+ memoryStr = relevant.map((e) => "[" + e.source + "] " + e.content).join("\n");
1172
+ }
1173
+ }
1174
+ const sectionPrompt = buildSystemPrompt(this.sectionRegistry, {
1175
+ memory: memoryStr || undefined,
1176
+ });
1177
+ const parts = [
1178
+ this.config.systemPrompt.trim(),
1179
+ this.buildRuntimeMetaSection(),
1180
+ sectionPrompt,
1181
+ ].filter((p) => p && p.trim().length > 0);
1182
+ return parts.join("\n\n");
1183
+ }
1184
+ async chat(systemPrompt, overrides = {}) {
1185
+ const systemMsg = { role: "system", content: systemPrompt };
1186
+ const payload = {
1187
+ model: this.config.model,
1188
+ messages: [systemMsg, ...this.messages],
1189
+ tools: this.config.tools.map((t) => ({
1190
+ type: "function",
1191
+ function: {
1192
+ name: t.name,
1193
+ description: t.description,
1194
+ parameters: t.parameters,
1195
+ },
1196
+ })),
1197
+ stream: false,
1198
+ ...overrides,
1199
+ };
1200
+ await this.maybeDumpInspect("prompt", {
1201
+ turn: this.turnCount,
1202
+ model: this.config.model,
1203
+ runtime: this.config.runtime ?? null,
1204
+ prompt: systemPrompt,
1205
+ });
1206
+ await this.maybeDumpInspect("request", payload);
1207
+ let resp;
1208
+ try {
1209
+ resp = await this.client.post("/chat/completions", payload);
1210
+ }
1211
+ catch (err) {
1212
+ const e = err;
1213
+ const detail = JSON.stringify(e.response?.data ?? {}).slice(0, 500);
1214
+ throw new Error(`${this.config.provider} API error: ` + detail);
1215
+ }
1216
+ const choice = resp.data.choices?.[0];
1217
+ if (!choice)
1218
+ throw new Error(`${this.config.provider} API error: ` + JSON.stringify(resp.data));
1219
+ const msg = choice.message;
1220
+ let rawToolCalls;
1221
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
1222
+ rawToolCalls = msg.tool_calls.map((tc) => ({
1223
+ id: tc.id,
1224
+ type: "function",
1225
+ function: {
1226
+ name: tc.function.name,
1227
+ arguments: typeof tc.function.arguments === "string"
1228
+ ? tc.function.arguments
1229
+ : JSON.stringify(tc.function.arguments),
1230
+ },
1231
+ }));
1232
+ }
1233
+ return {
1234
+ content: msg.content ?? "",
1235
+ tool_calls: rawToolCalls,
1236
+ usage: this.extractChatUsage(resp.data?.usage),
1237
+ };
1238
+ }
1239
+ extractChatUsage(rawUsage) {
1240
+ if (!rawUsage || typeof rawUsage !== "object")
1241
+ return undefined;
1242
+ const usage = rawUsage;
1243
+ const total = Number(usage.total_tokens ?? usage.totalTokens);
1244
+ if (!Number.isFinite(total) || total <= 0)
1245
+ return undefined;
1246
+ return { totalTokens: Math.floor(total) };
1247
+ }
1248
+ resolveRoundTokenUsage(usage, fallbackTokens) {
1249
+ if (usage?.totalTokens && Number.isFinite(usage.totalTokens) && usage.totalTokens > 0) {
1250
+ return { tokens: usage.totalTokens, source: "provider" };
1251
+ }
1252
+ return { tokens: fallbackTokens, source: "estimate" };
1253
+ }
1254
+ async verifyLastOperation() {
1255
+ // 取最近的 bash/write 结果
1256
+ const toolMsgs = this.messages.filter((m) => m.role === "tool");
1257
+ if (toolMsgs.length === 0)
1258
+ return;
1259
+ try {
1260
+ const lastResult = JSON.parse(toolMsgs[toolMsgs.length - 1].content);
1261
+ const vr = await this.verifier.verify("文件操作/Bash执行", "操作成功完成", lastResult.output);
1262
+ const icon = vr.verdict === "PASS" ? "✅" : vr.verdict === "FAIL" ? "❌" : "⚠️";
1263
+ console.error(icon + " 验证结果: " + vr.verdict);
1264
+ if (vr.verdict !== "PASS") {
1265
+ console.error(" 详情: " + vr.details);
1266
+ }
1267
+ this.emit("verification", vr.verdict, vr.details ?? vr.verdict);
1268
+ }
1269
+ catch {
1270
+ // 忽略验证错误
1271
+ }
1272
+ }
1273
+ async persistVerificationCommand() {
1274
+ const workspaceDir = this.getWorkspaceDir();
1275
+ const filePath = path.join(workspaceDir, ".qling-verify.json");
1276
+ try {
1277
+ if (this.verificationCommand) {
1278
+ await fs.writeFile(filePath, JSON.stringify({ verificationCommand: this.verificationCommand }, null, 2), "utf-8");
1279
+ }
1280
+ else {
1281
+ if (existsSync(filePath)) {
1282
+ await fs.unlink(filePath);
1283
+ }
1284
+ }
1285
+ }
1286
+ catch (err) {
1287
+ console.error("[AgentLoop] Failed to persist verification command: " + err.message);
1288
+ }
1289
+ }
1290
+ async loadVerificationCommand() {
1291
+ const workspaceDir = this.getWorkspaceDir();
1292
+ const filePath = path.join(workspaceDir, ".qling-verify.json");
1293
+ if (existsSync(filePath)) {
1294
+ try {
1295
+ const content = await fs.readFile(filePath, "utf-8");
1296
+ const data = JSON.parse(content);
1297
+ this.verificationCommand = data.verificationCommand ?? null;
1298
+ }
1299
+ catch (err) {
1300
+ console.error("[AgentLoop] Failed to load verification command: " + err.message);
1301
+ }
1302
+ }
1303
+ }
1304
+ runVerificationCommand(cmd) {
1305
+ return new Promise((resolve) => {
1306
+ exec(cmd, { cwd: this.getWorkspaceDir() }, (error, stdout, stderr) => {
1307
+ const code = error ? (error.code ?? 1) : 0;
1308
+ resolve({ code, stdout, stderr });
1309
+ });
1310
+ });
1311
+ }
1312
+ async checkAutoDream() {
1313
+ try {
1314
+ const transcript = this.messages
1315
+ .filter((m) => m.role === "user" || m.role === "assistant")
1316
+ .map((m) => m.content);
1317
+ let memories;
1318
+ let changedCount = 0;
1319
+ if (this.memoryDreamLLMEnabled) {
1320
+ memories = await extractDreamMemoriesLLM(transcript, this.turnCount, {
1321
+ enabled: true,
1322
+ model: this.config.model,
1323
+ maxTokens: 300,
1324
+ apiKey: this.config.apiKey,
1325
+ endpoint: this.config.endpoint ?? "https://api.deepseek.com",
1326
+ });
1327
+ const { consolidateMemoriesLLM } = await import("./memory/consolidation.js");
1328
+ const existing = this.memoryStore.exportPersisted();
1329
+ const ops = await consolidateMemoriesLLM(memories, existing, {
1330
+ apiKey: this.config.apiKey,
1331
+ endpoint: this.config.endpoint ?? "https://api.deepseek.com",
1332
+ model: this.config.model,
1333
+ });
1334
+ this.memoryStore.applyOperations(ops, "workspace");
1335
+ changedCount = ops.filter((op) => op.action !== "NOOP").length;
1336
+ }
1337
+ else {
1338
+ memories = await extractDreamMemories({ turnCount: this.turnCount, transcript }, { enabled: true, turnThreshold: this.memoryDreamTurnThreshold, transcriptWindow: 4 });
1339
+ const existingContents = new Set(this.memoryStore.exportPersisted().map((e) => e.content));
1340
+ const newMems = memories.filter((m) => !existingContents.has(m));
1341
+ for (const mem of newMems) {
1342
+ this.memoryStore.add(mem, "auto-dream", 0.6);
1343
+ }
1344
+ changedCount = newMems.length;
1345
+ }
1346
+ if (changedCount > 0) {
1347
+ this.memoryStore.compactPersisted(this.memoryMaxEntries);
1348
+ await this.memoryStore.saveToDisk();
1349
+ console.error("[AutoDream] " + changedCount + " 项长期记忆已整理并保存");
1350
+ }
1351
+ }
1352
+ catch (err) {
1353
+ // ignore
1354
+ }
1355
+ }
1356
+ estimateTokens() {
1357
+ // 粗略估算:总字符数 × 4
1358
+ const totalChars = this.messages.reduce((sum, m) => sum + m.content.length, 0);
1359
+ return totalChars * 4;
1360
+ }
1361
+ logTurnTelemetry(metrics) {
1362
+ this.toolCallTotal += metrics.toolCalls;
1363
+ this.toolFailureTotal += metrics.toolFailures;
1364
+ const turnFailureRate = metrics.toolCalls === 0 ? 0 : Math.round((metrics.toolFailures / metrics.toolCalls) * 100);
1365
+ const totalFailureRate = this.toolCallTotal === 0 ? 0 : Math.round((this.toolFailureTotal / this.toolCallTotal) * 100);
1366
+ const text = "📊 [Obs] turn=" +
1367
+ metrics.turn +
1368
+ " tools=" +
1369
+ metrics.toolCalls +
1370
+ " turnFailRate=" +
1371
+ turnFailureRate +
1372
+ "% totalFailRate=" +
1373
+ totalFailureRate +
1374
+ "% compactions=" +
1375
+ this.compactionCount +
1376
+ " retries=" +
1377
+ this.retryCountTotal;
1378
+ if (this.loggingConfig.format === "json") {
1379
+ console.error(JSON.stringify({
1380
+ type: "observability",
1381
+ turn: metrics.turn,
1382
+ toolCalls: metrics.toolCalls,
1383
+ turnFailureRate,
1384
+ totalFailureRate,
1385
+ compactions: this.compactionCount,
1386
+ retries: this.retryCountTotal,
1387
+ }));
1388
+ return;
1389
+ }
1390
+ console.error(text);
1391
+ }
1392
+ buildRuntimeMetaSection() {
1393
+ const runtime = this.config.runtime;
1394
+ const workspace = runtime?.workspaceDir ?? "(disabled)";
1395
+ const cache = runtime?.fileCacheDir ?? path.join(this.runtimeRootDir, "cache");
1396
+ const state = runtime?.fileStateDir ?? this.runtimeRootDir;
1397
+ return [
1398
+ "【Runtime Meta】",
1399
+ `provider=${this.config.provider ?? "default"}`,
1400
+ `endpoint=${this.config.endpoint ?? "default"}`,
1401
+ `workspace_dir=${workspace}`,
1402
+ `file_cache_dir=${cache}`,
1403
+ `file_state_dir=${state}`,
1404
+ ].join("\n");
1405
+ }
1406
+ async maybeDumpInspect(kind, payload) {
1407
+ if (kind === "prompt" && !this.loggingConfig.inspectPrompt)
1408
+ return;
1409
+ if (kind === "request" && !this.loggingConfig.inspectRequest)
1410
+ return;
1411
+ try {
1412
+ const file = path.join(this.loggingConfig.inspectDumpDir, `${String(this.turnCount).padStart(4, "0")}_${Date.now()}_${kind}.json`);
1413
+ await fs.writeFile(file, JSON.stringify(payload, null, 2), "utf-8");
1414
+ }
1415
+ catch {
1416
+ // inspect dump failure should not block execution
1417
+ }
1418
+ }
1419
+ resolveLlmRequestTimeout() {
1420
+ const envValue = Number(process.env.QLING_LLM_REQUEST_TIMEOUT_MS ?? "120000");
1421
+ if (Number.isFinite(envValue) && envValue > 0) {
1422
+ return envValue;
1423
+ }
1424
+ return this.config.runtime?.timeoutMs ?? 120_000;
1425
+ }
1426
+ parseToolArguments(raw) {
1427
+ const retries = Math.max(0, this.config.runtime?.parseRetries ?? 0);
1428
+ let candidate = String(raw ?? "");
1429
+ let lastError = "invalid arguments";
1430
+ for (let attempt = 0; attempt <= retries; attempt++) {
1431
+ try {
1432
+ const parsed = JSON.parse(candidate);
1433
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1434
+ lastError = "arguments must be a JSON object";
1435
+ }
1436
+ else {
1437
+ return { ok: true, value: parsed };
1438
+ }
1439
+ }
1440
+ catch (err) {
1441
+ lastError = err instanceof Error ? err.message : String(err);
1442
+ }
1443
+ if (attempt < retries) {
1444
+ candidate = this.repairToolArguments(candidate, attempt);
1445
+ }
1446
+ }
1447
+ return {
1448
+ ok: false,
1449
+ error: `failed after ${retries + 1} attempt(s): ${lastError}`,
1450
+ };
1451
+ }
1452
+ repairToolArguments(source, attempt) {
1453
+ let out = source.trim();
1454
+ if (attempt === 0) {
1455
+ const fenced = out.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
1456
+ if (fenced) {
1457
+ out = fenced[1].trim();
1458
+ }
1459
+ return out;
1460
+ }
1461
+ if (attempt === 1) {
1462
+ return out.replace(/,\s*([}\]])/g, "$1");
1463
+ }
1464
+ if (attempt === 2) {
1465
+ return out
1466
+ .replace(/[“”]/g, "\"")
1467
+ .replace(/[‘’]/g, "'")
1468
+ .replace(/([{,]\s*)'([^']+?)'\s*:/g, '$1"$2":')
1469
+ .replace(/:\s*'([^']*?)'(\s*[,}])/g, ': "$1"$2');
1470
+ }
1471
+ return out;
1472
+ }
1473
+ buildToolSignature(name, args) {
1474
+ return `${name}:${this.stableStringify(args)}`;
1475
+ }
1476
+ stableStringify(value) {
1477
+ const normalize = (input) => {
1478
+ if (Array.isArray(input)) {
1479
+ return input.map((item) => normalize(item));
1480
+ }
1481
+ if (input && typeof input === "object") {
1482
+ const entries = Object.entries(input).sort(([a], [b]) => a.localeCompare(b));
1483
+ const obj = {};
1484
+ for (const [k, v] of entries) {
1485
+ obj[k] = normalize(v);
1486
+ }
1487
+ return obj;
1488
+ }
1489
+ return input;
1490
+ };
1491
+ try {
1492
+ return JSON.stringify(normalize(value));
1493
+ }
1494
+ catch {
1495
+ return "[unstringifiable]";
1496
+ }
1497
+ }
1498
+ }