kagent-ts 0.1.4 → 0.1.5

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 (341) hide show
  1. package/LICENSE +68 -21
  2. package/README.md +27 -371
  3. package/dist/compression/progressive-compressor.d.ts +66 -0
  4. package/dist/compression/progressive-compressor.d.ts.map +1 -0
  5. package/dist/compression/progressive-compressor.js +367 -0
  6. package/dist/compression/progressive-compressor.js.map +1 -0
  7. package/dist/compression/types.d.ts +1 -5
  8. package/dist/compression/types.d.ts.map +1 -1
  9. package/dist/context/context-manager.d.ts +34 -15
  10. package/dist/context/context-manager.d.ts.map +1 -1
  11. package/dist/context/context-manager.js +78 -28
  12. package/dist/context/context-manager.js.map +1 -1
  13. package/dist/context/types.d.ts +20 -4
  14. package/dist/context/types.d.ts.map +1 -1
  15. package/dist/core/agent.d.ts +354 -25
  16. package/dist/core/agent.d.ts.map +1 -1
  17. package/dist/core/agent.js +646 -64
  18. package/dist/core/agent.js.map +1 -1
  19. package/dist/core/fusion-agent.d.ts +207 -0
  20. package/dist/core/fusion-agent.d.ts.map +1 -0
  21. package/dist/core/fusion-agent.js +769 -0
  22. package/dist/core/fusion-agent.js.map +1 -0
  23. package/dist/core/hooks.d.ts +19 -7
  24. package/dist/core/hooks.d.ts.map +1 -1
  25. package/dist/core/plan-solve-agent.d.ts +1 -15
  26. package/dist/core/plan-solve-agent.d.ts.map +1 -1
  27. package/dist/core/plan-solve-agent.js +142 -117
  28. package/dist/core/plan-solve-agent.js.map +1 -1
  29. package/dist/core/react-agent.d.ts +0 -13
  30. package/dist/core/react-agent.d.ts.map +1 -1
  31. package/dist/core/react-agent.js +127 -102
  32. package/dist/core/react-agent.js.map +1 -1
  33. package/dist/core/response-schema.d.ts +65 -0
  34. package/dist/core/response-schema.d.ts.map +1 -1
  35. package/dist/core/response-schema.js +174 -1
  36. package/dist/core/response-schema.js.map +1 -1
  37. package/dist/core/system-prompts.d.ts +27 -0
  38. package/dist/core/system-prompts.d.ts.map +1 -0
  39. package/dist/core/system-prompts.js +112 -0
  40. package/dist/core/system-prompts.js.map +1 -0
  41. package/dist/eval/benchmark.d.ts +81 -0
  42. package/dist/eval/benchmark.d.ts.map +1 -0
  43. package/dist/eval/benchmark.js +292 -0
  44. package/dist/eval/benchmark.js.map +1 -0
  45. package/dist/eval/eval-runner.d.ts +79 -0
  46. package/dist/eval/eval-runner.d.ts.map +1 -0
  47. package/dist/eval/eval-runner.js +252 -0
  48. package/dist/eval/eval-runner.js.map +1 -0
  49. package/dist/eval/index.d.ts +7 -0
  50. package/dist/eval/index.d.ts.map +1 -0
  51. package/dist/eval/index.js +13 -0
  52. package/dist/eval/index.js.map +1 -0
  53. package/dist/eval/tool-call-evaluator.d.ts +72 -0
  54. package/dist/eval/tool-call-evaluator.d.ts.map +1 -0
  55. package/dist/eval/tool-call-evaluator.js +265 -0
  56. package/dist/eval/tool-call-evaluator.js.map +1 -0
  57. package/dist/eval/types.d.ts +219 -0
  58. package/dist/eval/types.d.ts.map +1 -0
  59. package/dist/eval/types.js +3 -0
  60. package/dist/eval/types.js.map +1 -0
  61. package/dist/index.d.ts +58 -14
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +116 -8
  64. package/dist/index.js.map +1 -1
  65. package/dist/llm/anthropic-provider.d.ts +141 -0
  66. package/dist/llm/anthropic-provider.d.ts.map +1 -0
  67. package/dist/llm/anthropic-provider.js +486 -0
  68. package/dist/llm/anthropic-provider.js.map +1 -0
  69. package/dist/llm/errors.d.ts +26 -0
  70. package/dist/llm/errors.d.ts.map +1 -0
  71. package/dist/llm/errors.js +19 -0
  72. package/dist/llm/errors.js.map +1 -0
  73. package/dist/llm/factory.d.ts +73 -0
  74. package/dist/llm/factory.d.ts.map +1 -0
  75. package/dist/llm/factory.js +77 -0
  76. package/dist/llm/factory.js.map +1 -0
  77. package/dist/llm/fallback-provider.d.ts +47 -0
  78. package/dist/llm/fallback-provider.d.ts.map +1 -0
  79. package/dist/llm/fallback-provider.js +91 -0
  80. package/dist/llm/fallback-provider.js.map +1 -0
  81. package/dist/llm/interface.d.ts +54 -11
  82. package/dist/llm/interface.d.ts.map +1 -1
  83. package/dist/llm/interface.js +34 -0
  84. package/dist/llm/interface.js.map +1 -1
  85. package/dist/llm/model-router.d.ts +126 -0
  86. package/dist/llm/model-router.d.ts.map +1 -0
  87. package/dist/llm/model-router.js +178 -0
  88. package/dist/llm/model-router.js.map +1 -0
  89. package/dist/llm/openai-provider.d.ts +8 -32
  90. package/dist/llm/openai-provider.d.ts.map +1 -1
  91. package/dist/llm/openai-provider.js +27 -60
  92. package/dist/llm/openai-provider.js.map +1 -1
  93. package/dist/llm/rate-limiter.d.ts +41 -0
  94. package/dist/llm/rate-limiter.d.ts.map +1 -0
  95. package/dist/llm/rate-limiter.js +93 -0
  96. package/dist/llm/rate-limiter.js.map +1 -0
  97. package/dist/llm/retry.d.ts +26 -0
  98. package/dist/llm/retry.d.ts.map +1 -0
  99. package/dist/llm/retry.js +44 -0
  100. package/dist/llm/retry.js.map +1 -0
  101. package/dist/llm/token-budget.d.ts +97 -0
  102. package/dist/llm/token-budget.d.ts.map +1 -0
  103. package/dist/llm/token-budget.js +115 -0
  104. package/dist/llm/token-budget.js.map +1 -0
  105. package/dist/logging/index.d.ts +2 -0
  106. package/dist/logging/index.d.ts.map +1 -0
  107. package/dist/logging/index.js +7 -0
  108. package/dist/logging/index.js.map +1 -0
  109. package/dist/logging/logger.d.ts +38 -0
  110. package/dist/logging/logger.d.ts.map +1 -0
  111. package/dist/logging/logger.js +34 -0
  112. package/dist/logging/logger.js.map +1 -0
  113. package/dist/mcp/mcp-client-manager.d.ts +10 -2
  114. package/dist/mcp/mcp-client-manager.d.ts.map +1 -1
  115. package/dist/mcp/mcp-client-manager.js +20 -9
  116. package/dist/mcp/mcp-client-manager.js.map +1 -1
  117. package/dist/memory/index.d.ts +3 -0
  118. package/dist/memory/index.d.ts.map +1 -0
  119. package/dist/memory/index.js +6 -0
  120. package/dist/memory/index.js.map +1 -0
  121. package/dist/memory/memory-manager.d.ts +119 -0
  122. package/dist/memory/memory-manager.d.ts.map +1 -0
  123. package/dist/memory/memory-manager.js +334 -0
  124. package/dist/memory/memory-manager.js.map +1 -0
  125. package/dist/messages/types.d.ts +2 -0
  126. package/dist/messages/types.d.ts.map +1 -1
  127. package/dist/orchestrator/index.d.ts +5 -0
  128. package/dist/orchestrator/index.d.ts.map +1 -0
  129. package/dist/orchestrator/index.js +13 -0
  130. package/dist/orchestrator/index.js.map +1 -0
  131. package/dist/orchestrator/json-extractor.d.ts +18 -0
  132. package/dist/orchestrator/json-extractor.d.ts.map +1 -0
  133. package/dist/orchestrator/json-extractor.js +111 -0
  134. package/dist/orchestrator/json-extractor.js.map +1 -0
  135. package/dist/orchestrator/orchestrator-agent.d.ts +152 -0
  136. package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -0
  137. package/dist/orchestrator/orchestrator-agent.js +675 -0
  138. package/dist/orchestrator/orchestrator-agent.js.map +1 -0
  139. package/dist/orchestrator/orchestrator-response.d.ts +40 -0
  140. package/dist/orchestrator/orchestrator-response.d.ts.map +1 -0
  141. package/dist/orchestrator/orchestrator-response.js +275 -0
  142. package/dist/orchestrator/orchestrator-response.js.map +1 -0
  143. package/dist/orchestrator/orchestrator-types.d.ts +116 -0
  144. package/dist/orchestrator/orchestrator-types.d.ts.map +1 -0
  145. package/dist/orchestrator/orchestrator-types.js +3 -0
  146. package/dist/orchestrator/orchestrator-types.js.map +1 -0
  147. package/dist/preferences/preference-manager.d.ts +8 -3
  148. package/dist/preferences/preference-manager.d.ts.map +1 -1
  149. package/dist/preferences/preference-manager.js +17 -4
  150. package/dist/preferences/preference-manager.js.map +1 -1
  151. package/dist/rag/chroma-store.d.ts +52 -0
  152. package/dist/rag/chroma-store.d.ts.map +1 -0
  153. package/dist/rag/chroma-store.js +110 -0
  154. package/dist/rag/chroma-store.js.map +1 -0
  155. package/dist/rag/document-loader.d.ts +21 -0
  156. package/dist/rag/document-loader.d.ts.map +1 -0
  157. package/dist/rag/document-loader.js +129 -0
  158. package/dist/rag/document-loader.js.map +1 -0
  159. package/dist/rag/embedding-provider.d.ts +36 -0
  160. package/dist/rag/embedding-provider.d.ts.map +1 -0
  161. package/dist/rag/embedding-provider.js +74 -0
  162. package/dist/rag/embedding-provider.js.map +1 -0
  163. package/dist/rag/index.d.ts +17 -0
  164. package/dist/rag/index.d.ts.map +1 -0
  165. package/dist/rag/index.js +27 -0
  166. package/dist/rag/index.js.map +1 -0
  167. package/dist/rag/keyword-index.d.ts +53 -0
  168. package/dist/rag/keyword-index.d.ts.map +1 -0
  169. package/dist/rag/keyword-index.js +161 -0
  170. package/dist/rag/keyword-index.js.map +1 -0
  171. package/dist/rag/llm-reranker.d.ts +36 -0
  172. package/dist/rag/llm-reranker.d.ts.map +1 -0
  173. package/dist/rag/llm-reranker.js +95 -0
  174. package/dist/rag/llm-reranker.js.map +1 -0
  175. package/dist/rag/rag-manager.d.ts +54 -0
  176. package/dist/rag/rag-manager.d.ts.map +1 -0
  177. package/dist/rag/rag-manager.js +179 -0
  178. package/dist/rag/rag-manager.js.map +1 -0
  179. package/dist/rag/rag-types.d.ts +143 -0
  180. package/dist/rag/rag-types.d.ts.map +1 -0
  181. package/dist/rag/rag-types.js +9 -0
  182. package/dist/rag/rag-types.js.map +1 -0
  183. package/dist/rag/rrf.d.ts +47 -0
  184. package/dist/rag/rrf.d.ts.map +1 -0
  185. package/dist/rag/rrf.js +70 -0
  186. package/dist/rag/rrf.js.map +1 -0
  187. package/dist/rag/search-knowledge.d.ts +24 -0
  188. package/dist/rag/search-knowledge.d.ts.map +1 -0
  189. package/dist/rag/search-knowledge.js +86 -0
  190. package/dist/rag/search-knowledge.js.map +1 -0
  191. package/dist/rag/text-splitter.d.ts +25 -0
  192. package/dist/rag/text-splitter.d.ts.map +1 -0
  193. package/dist/rag/text-splitter.js +136 -0
  194. package/dist/rag/text-splitter.js.map +1 -0
  195. package/dist/rag/vector-store.d.ts +34 -0
  196. package/dist/rag/vector-store.d.ts.map +1 -0
  197. package/dist/rag/vector-store.js +73 -0
  198. package/dist/rag/vector-store.js.map +1 -0
  199. package/dist/reflection/error-notebook.d.ts +125 -0
  200. package/dist/reflection/error-notebook.d.ts.map +1 -0
  201. package/dist/reflection/error-notebook.js +368 -0
  202. package/dist/reflection/error-notebook.js.map +1 -0
  203. package/dist/reflection/index.d.ts +8 -0
  204. package/dist/reflection/index.d.ts.map +1 -0
  205. package/dist/reflection/index.js +12 -0
  206. package/dist/reflection/index.js.map +1 -0
  207. package/dist/reflection/memory-reflector.d.ts +97 -0
  208. package/dist/reflection/memory-reflector.d.ts.map +1 -0
  209. package/dist/reflection/memory-reflector.js +215 -0
  210. package/dist/reflection/memory-reflector.js.map +1 -0
  211. package/dist/reflection/reflection-agent.d.ts +105 -0
  212. package/dist/reflection/reflection-agent.d.ts.map +1 -0
  213. package/dist/reflection/reflection-agent.js +234 -0
  214. package/dist/reflection/reflection-agent.js.map +1 -0
  215. package/dist/reflection/reflection-hook.d.ts +50 -0
  216. package/dist/reflection/reflection-hook.d.ts.map +1 -0
  217. package/dist/reflection/reflection-hook.js +108 -0
  218. package/dist/reflection/reflection-hook.js.map +1 -0
  219. package/dist/rules/project-rules.d.ts +47 -0
  220. package/dist/rules/project-rules.d.ts.map +1 -0
  221. package/dist/rules/project-rules.js +166 -0
  222. package/dist/rules/project-rules.js.map +1 -0
  223. package/dist/security/boundaries.d.ts +81 -0
  224. package/dist/security/boundaries.d.ts.map +1 -0
  225. package/dist/security/boundaries.js +158 -0
  226. package/dist/security/boundaries.js.map +1 -0
  227. package/dist/security/index.d.ts +2 -0
  228. package/dist/security/index.d.ts.map +1 -0
  229. package/dist/security/index.js +11 -0
  230. package/dist/security/index.js.map +1 -0
  231. package/dist/session/session-types.d.ts +25 -4
  232. package/dist/session/session-types.d.ts.map +1 -1
  233. package/dist/skills/file-skill-loader.d.ts +4 -6
  234. package/dist/skills/file-skill-loader.d.ts.map +1 -1
  235. package/dist/skills/file-skill-loader.js +8 -19
  236. package/dist/skills/file-skill-loader.js.map +1 -1
  237. package/dist/skills/index.d.ts +1 -1
  238. package/dist/skills/index.d.ts.map +1 -1
  239. package/dist/skills/index.js +1 -2
  240. package/dist/skills/index.js.map +1 -1
  241. package/dist/skills/skill-manager.d.ts +18 -8
  242. package/dist/skills/skill-manager.d.ts.map +1 -1
  243. package/dist/skills/skill-manager.js +58 -36
  244. package/dist/skills/skill-manager.js.map +1 -1
  245. package/dist/skills/types.d.ts +3 -8
  246. package/dist/skills/types.d.ts.map +1 -1
  247. package/dist/subagent/index.d.ts +4 -0
  248. package/dist/subagent/index.d.ts.map +1 -0
  249. package/dist/subagent/index.js +8 -0
  250. package/dist/subagent/index.js.map +1 -0
  251. package/dist/subagent/subagent-loader.d.ts +53 -0
  252. package/dist/subagent/subagent-loader.d.ts.map +1 -0
  253. package/dist/subagent/subagent-loader.js +155 -0
  254. package/dist/subagent/subagent-loader.js.map +1 -0
  255. package/dist/subagent/subagent-manager.d.ts +161 -0
  256. package/dist/subagent/subagent-manager.d.ts.map +1 -0
  257. package/dist/subagent/subagent-manager.js +468 -0
  258. package/dist/subagent/subagent-manager.js.map +1 -0
  259. package/dist/subagent/subagent-types.d.ts +77 -0
  260. package/dist/subagent/subagent-types.d.ts.map +1 -0
  261. package/dist/subagent/subagent-types.js +3 -0
  262. package/dist/subagent/subagent-types.js.map +1 -0
  263. package/dist/tools/builtin/bash.d.ts +3 -0
  264. package/dist/tools/builtin/bash.d.ts.map +1 -0
  265. package/dist/tools/builtin/bash.js +87 -0
  266. package/dist/tools/builtin/bash.js.map +1 -0
  267. package/dist/tools/builtin/edit-file.d.ts.map +1 -1
  268. package/dist/tools/builtin/edit-file.js +1 -0
  269. package/dist/tools/builtin/edit-file.js.map +1 -1
  270. package/dist/tools/builtin/index.d.ts +14 -0
  271. package/dist/tools/builtin/index.d.ts.map +1 -1
  272. package/dist/tools/builtin/index.js +45 -1
  273. package/dist/tools/builtin/index.js.map +1 -1
  274. package/dist/tools/builtin/list-errors.d.ts +7 -0
  275. package/dist/tools/builtin/list-errors.d.ts.map +1 -0
  276. package/dist/tools/builtin/list-errors.js +64 -0
  277. package/dist/tools/builtin/list-errors.js.map +1 -0
  278. package/dist/tools/builtin/list-subagents.d.ts +7 -0
  279. package/dist/tools/builtin/list-subagents.d.ts.map +1 -0
  280. package/dist/tools/builtin/list-subagents.js +21 -0
  281. package/dist/tools/builtin/list-subagents.js.map +1 -0
  282. package/dist/tools/builtin/recall.d.ts +11 -0
  283. package/dist/tools/builtin/recall.d.ts.map +1 -0
  284. package/dist/tools/builtin/recall.js +60 -0
  285. package/dist/tools/builtin/recall.js.map +1 -0
  286. package/dist/tools/builtin/remember.d.ts +12 -0
  287. package/dist/tools/builtin/remember.d.ts.map +1 -0
  288. package/dist/tools/builtin/remember.js +72 -0
  289. package/dist/tools/builtin/remember.js.map +1 -0
  290. package/dist/tools/builtin/skill.d.ts +14 -0
  291. package/dist/tools/builtin/skill.d.ts.map +1 -0
  292. package/dist/tools/builtin/skill.js +71 -0
  293. package/dist/tools/builtin/skill.js.map +1 -0
  294. package/dist/tools/builtin/spawn-subagent.d.ts +7 -0
  295. package/dist/tools/builtin/spawn-subagent.d.ts.map +1 -0
  296. package/dist/tools/builtin/spawn-subagent.js +43 -0
  297. package/dist/tools/builtin/spawn-subagent.js.map +1 -0
  298. package/dist/tools/builtin/web-fetch.d.ts +3 -0
  299. package/dist/tools/builtin/web-fetch.d.ts.map +1 -0
  300. package/dist/tools/builtin/web-fetch.js +101 -0
  301. package/dist/tools/builtin/web-fetch.js.map +1 -0
  302. package/dist/tools/builtin/write-file.d.ts.map +1 -1
  303. package/dist/tools/builtin/write-file.js +1 -0
  304. package/dist/tools/builtin/write-file.js.map +1 -1
  305. package/dist/tools/circuit-breaker.d.ts +19 -10
  306. package/dist/tools/circuit-breaker.d.ts.map +1 -1
  307. package/dist/tools/circuit-breaker.js +22 -11
  308. package/dist/tools/circuit-breaker.js.map +1 -1
  309. package/dist/tools/error-tracker.d.ts +28 -44
  310. package/dist/tools/error-tracker.d.ts.map +1 -1
  311. package/dist/tools/error-tracker.js +39 -156
  312. package/dist/tools/error-tracker.js.map +1 -1
  313. package/dist/tools/tool-filter.d.ts +70 -0
  314. package/dist/tools/tool-filter.d.ts.map +1 -0
  315. package/dist/tools/tool-filter.js +92 -0
  316. package/dist/tools/tool-filter.js.map +1 -0
  317. package/dist/tools/tool-output-truncator.d.ts +36 -0
  318. package/dist/tools/tool-output-truncator.d.ts.map +1 -0
  319. package/dist/tools/tool-output-truncator.js +117 -0
  320. package/dist/tools/tool-output-truncator.js.map +1 -0
  321. package/dist/tools/tool-registry.d.ts +25 -9
  322. package/dist/tools/tool-registry.d.ts.map +1 -1
  323. package/dist/tools/tool-registry.js +77 -28
  324. package/dist/tools/tool-registry.js.map +1 -1
  325. package/dist/tools/tool-validator.d.ts +13 -0
  326. package/dist/tools/tool-validator.d.ts.map +1 -0
  327. package/dist/tools/tool-validator.js +116 -0
  328. package/dist/tools/tool-validator.js.map +1 -0
  329. package/dist/tools/types.d.ts +86 -3
  330. package/dist/tools/types.d.ts.map +1 -1
  331. package/dist/tools/types.js +51 -2
  332. package/dist/tools/types.js.map +1 -1
  333. package/dist/trace/trace-logger.d.ts +30 -4
  334. package/dist/trace/trace-logger.d.ts.map +1 -1
  335. package/dist/trace/trace-logger.js +82 -6
  336. package/dist/trace/trace-logger.js.map +1 -1
  337. package/package.json +13 -4
  338. package/dist/compression/sliding-window.d.ts +0 -21
  339. package/dist/compression/sliding-window.d.ts.map +0 -1
  340. package/dist/compression/sliding-window.js +0 -44
  341. package/dist/compression/sliding-window.js.map +0 -1
@@ -1,12 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Agent = void 0;
4
+ const model_router_1 = require("../llm/model-router");
5
+ const message_1 = require("../messages/message");
6
+ const system_prompts_1 = require("./system-prompts");
7
+ const boundaries_1 = require("../security/boundaries");
4
8
  const context_manager_1 = require("../context/context-manager");
5
9
  const tool_registry_1 = require("../tools/tool-registry");
10
+ const tool_output_truncator_1 = require("../tools/tool-output-truncator");
11
+ const types_1 = require("../tools/types");
12
+ const tool_validator_1 = require("../tools/tool-validator");
6
13
  const skill_manager_1 = require("../skills/skill-manager");
14
+ const memory_manager_1 = require("../memory/memory-manager");
15
+ const project_rules_1 = require("../rules/project-rules");
7
16
  const session_manager_1 = require("../session/session-manager");
17
+ const token_counter_1 = require("../utils/token-counter");
8
18
  const preference_manager_1 = require("../preferences/preference-manager");
9
19
  const mcp_client_manager_1 = require("../mcp/mcp-client-manager");
20
+ const subagent_manager_1 = require("../subagent/subagent-manager");
21
+ const rag_manager_1 = require("../rag/rag-manager");
22
+ const search_knowledge_1 = require("../rag/search-knowledge");
23
+ const list_subagents_1 = require("../tools/builtin/list-subagents");
24
+ const spawn_subagent_1 = require("../tools/builtin/spawn-subagent");
25
+ const list_errors_1 = require("../tools/builtin/list-errors");
26
+ const skill_1 = require("../tools/builtin/skill");
27
+ const remember_1 = require("../tools/builtin/remember");
28
+ const recall_1 = require("../tools/builtin/recall");
29
+ const builtin_1 = require("../tools/builtin");
30
+ const logger_1 = require("../logging/logger");
31
+ const token_budget_1 = require("../llm/token-budget");
10
32
  /**
11
33
  * Abstract base Agent class.
12
34
  *
@@ -26,6 +48,10 @@ class Agent {
26
48
  contextManager;
27
49
  toolRegistry;
28
50
  skillManager;
51
+ /** Long-term memory (rules + project facts) persisted across sessions. */
52
+ memoryManager;
53
+ /** User-defined project rules loaded from disk. */
54
+ projectRules;
29
55
  /** The original core system prompt (before skill sections are appended). */
30
56
  coreSystemPrompt;
31
57
  /** User preferences — plain-text directives injected into the system prompt. */
@@ -34,10 +60,14 @@ class Agent {
34
60
  preferenceManager;
35
61
  /** Lifecycle hooks for observing agent execution. */
36
62
  hooks = [];
37
- /**
38
- * Trace ID of the most recent tool error waiting for LLM analysis.
39
- * Set after a tool fails, cleared when the LLM's analysis is recorded. */
40
- pendingErrorTraceId = null;
63
+ /** Logger for framework-internal messages. */
64
+ logger;
65
+ /** Token budget for session-level cost control (optional). */
66
+ tokenBudget;
67
+ /** Human-in-the-loop approval callback (from AgentConfig). */
68
+ onToolApproval;
69
+ /** Whether to execute independent tool calls in parallel (default: true). */
70
+ enableParallelToolExecution;
41
71
  // ─── Session & Cancellation ─────────────────────────────────────────
42
72
  /** Session manager for checkpoint persistence (optional). */
43
73
  sessionManager;
@@ -45,6 +75,8 @@ class Agent {
45
75
  checkpointingEnabled = false;
46
76
  /** Whether the current run has been cancelled by the user. */
47
77
  _cancelled = false;
78
+ /** Controller for aborting in-flight LLM requests on cancellation. */
79
+ _abortController;
48
80
  // ─── MCP (Model Context Protocol) ─────────────────────────────────────
49
81
  /** MCP server configurations (from AgentConfig). */
50
82
  mcpServerConfigs;
@@ -52,16 +84,36 @@ class Agent {
52
84
  mcpClientManager;
53
85
  /** Guards async init() from running more than once per instance. */
54
86
  _mcpInitialized = false;
87
+ // ─── RAG ────────────────────────────────────────────────────────────────
88
+ /** RAG manager (lazily initialized in init()). */
89
+ ragManager;
90
+ /** RAG configuration (from AgentConfig). */
91
+ ragConfig;
92
+ // ─── Sub-Agent ────────────────────────────────────────────────────────
93
+ /** Sub-agent manager (lazily initialized in init()). */
94
+ subAgentManager;
95
+ /** Sub-agent definitions directory (from AgentConfig). */
96
+ subAgentsDir;
97
+ /** LLM provider for sub-agents (defaults to main llm if not set). */
98
+ subAgentLLM;
99
+ /** Skills directory path (from AgentConfig). */
100
+ skillsDir;
55
101
  constructor(config) {
56
102
  this._cancelled = false;
57
103
  this.llm = config.llm;
58
- this.contextManager = config.contextManager ?? new context_manager_1.ContextManager();
104
+ this.logger = config.logger ?? new logger_1.ConsoleLogger();
105
+ this.onToolApproval = config.onToolApproval;
106
+ this.enableParallelToolExecution = config.enableParallelToolExecution ?? true;
107
+ this.contextManager = config.contextManager ?? new context_manager_1.ContextManager(undefined, this.logger);
59
108
  // Prefer toolRegistry; fall back to plain tools array
60
109
  if (config.toolRegistry) {
61
110
  this.toolRegistry = config.toolRegistry;
62
111
  }
63
112
  else {
64
- this.toolRegistry = new tool_registry_1.ToolRegistry(config.toolRetryCount, config.toolErrorTracker);
113
+ const truncator = (config.toolOutputMaxBytes && config.toolOutputMaxBytes > 0)
114
+ ? new tool_output_truncator_1.ToolOutputTruncator(config.toolOutputMaxBytes)
115
+ : undefined;
116
+ this.toolRegistry = new tool_registry_1.ToolRegistry(config.toolRetryCount, config.toolErrorTracker, truncator);
65
117
  if (config.tools && config.tools.length > 0) {
66
118
  this.toolRegistry.registerMany(config.tools);
67
119
  }
@@ -71,12 +123,16 @@ class Agent {
71
123
  this.skillManager = config.skillManager;
72
124
  }
73
125
  else {
74
- this.skillManager = new skill_manager_1.SkillManager();
126
+ this.skillManager = new skill_manager_1.SkillManager(this.logger);
75
127
  }
76
128
  // Register file-based skills if a directory is configured
77
129
  if (config.skillsDir) {
78
130
  this.skillManager.registerFromDirectory(config.skillsDir);
79
131
  }
132
+ // Memory — long-term facts, rules, and project context
133
+ this.memoryManager = new memory_manager_1.MemoryManager(config.memoryDir);
134
+ // Project rules — user-authored, always injected
135
+ this.projectRules = new project_rules_1.ProjectRules(config.rulesPath);
80
136
  // Store core system prompt and set it
81
137
  this.coreSystemPrompt = config.systemPrompt ?? "";
82
138
  if (this.coreSystemPrompt) {
@@ -91,6 +147,23 @@ class Agent {
91
147
  }
92
148
  this.checkpointingEnabled = config.enableCheckpointing ?? false;
93
149
  this.mcpServerConfigs = config.mcpServers;
150
+ this.subAgentsDir = config.subAgentsDir;
151
+ this.skillsDir = config.skillsDir;
152
+ this.ragConfig = config.rag;
153
+ // Resolve sub-agent LLM:
154
+ // 1. Explicit `subAgentLLM` → use it directly
155
+ // 2. `llm` is a ModelRouter → use router.forSubAgent()
156
+ // 3. Fallback → sub-agents share the main `llm`
157
+ if (config.subAgentLLM) {
158
+ this.subAgentLLM = config.subAgentLLM;
159
+ }
160
+ else if (config.llm instanceof model_router_1.ModelRouter) {
161
+ this.subAgentLLM = config.llm.forSubAgent();
162
+ }
163
+ // Token budget — session-level cost control
164
+ if (config.tokenBudgetConfig) {
165
+ this.tokenBudget = new token_budget_1.TokenBudget(config.tokenBudgetConfig);
166
+ }
94
167
  // ── Hooks ──────────────────────────────────────────────────────────────
95
168
  const rawHooks = config.hooks ?? [];
96
169
  this.hooks = Array.isArray(rawHooks) ? rawHooks : [rawHooks];
@@ -158,42 +231,372 @@ class Agent {
158
231
  this.hooks.push(hook);
159
232
  }
160
233
  /**
161
- * Rebuild the system message from the core prompt + user preferences + active skills.
162
- * Called automatically when skills are activated/deactivated or preferences change.
234
+ * Rebuild the system message.
235
+ *
236
+ * Sections are assembled in priority order:
237
+ * 1. Core prompt (agent identity + instructions)
238
+ * 2. Project rules (user-authored, always injected)
239
+ * 3. Preferences (user-set language / verbosity / style)
240
+ * 4. Error recovery rules (tool failure recovery guidance)
241
+ * 5. Long-term memories (index of persisted facts + rules)
242
+ * 6. Available skills (inactive skills the LLM can activate)
243
+ * 7. Active skill content (full instructions for activated skills)
244
+ *
245
+ * Empty sections are silently skipped.
246
+ */
247
+ /**
248
+ * Build the full system prompt from all sections.
249
+ *
250
+ * Subclasses that need to append extra content (e.g. plan progress)
251
+ * should call this and concatenate, instead of duplicating the assembly.
163
252
  */
253
+ buildSystemPrompt() {
254
+ const sections = [
255
+ this.coreSystemPrompt,
256
+ system_prompts_1.SECURITY_GUIDANCE,
257
+ this.hasSubAgents() ? system_prompts_1.SUB_AGENT_DELEGATION : "",
258
+ this.projectRules.buildPrompt(),
259
+ preference_manager_1.PreferenceManager.toPrompt(this.preferences),
260
+ this.memoryManager.buildPromptHint(),
261
+ this.skillManager.buildAvailableSkillsHint(),
262
+ this.skillManager.buildSkillsPrompt(),
263
+ ].filter(Boolean);
264
+ return sections.join("");
265
+ }
164
266
  rebuildSystemPrompt() {
165
- const prefsPrompt = preference_manager_1.PreferenceManager.toPrompt(this.preferences);
166
- const skillsHint = this.skillManager.buildAvailableSkillsHint();
167
- const skillsContent = this.skillManager.buildSkillsPrompt();
168
- const fullPrompt = this.coreSystemPrompt + prefsPrompt + skillsHint + skillsContent;
169
- this.contextManager.setSystemMessage(fullPrompt);
267
+ this.contextManager.setSystemMessage(this.buildSystemPrompt());
170
268
  }
171
269
  /**
172
- * Pre-iteration maintenance:
173
- * 1. Compress context window if needed.
174
- * 2. Auto-reload preferences from disk if the file was manually edited.
270
+ * Pre-iteration maintenance: compress context window if needed.
175
271
  */
176
- checkAndCompress() {
177
- if (this.contextManager.shouldCompress()) {
178
- const { removedCount } = this.contextManager.compress();
179
- console.log(`[Context] Compression triggered: removed ${removedCount} messages.`);
180
- }
181
- // Auto-reload preferences if the file was manually edited
272
+ async checkAndCompress() {
273
+ await this.contextManager.checkAndCompress(this.llm);
274
+ }
275
+ /**
276
+ * Reload preferences from disk if the file was manually edited.
277
+ * Called once at the start of each run so edits between runs take
278
+ * effect without restarting the agent process — but they won't
279
+ * change mid-run behavior.
280
+ */
281
+ reloadPreferencesIfChanged() {
182
282
  if (this.preferenceManager?.hasFileChanged()) {
183
283
  this.preferences = this.preferenceManager.reload();
184
284
  this.rebuildSystemPrompt();
185
- console.log("[Preferences] Reloaded from disk (file changed).");
285
+ return true;
286
+ }
287
+ return false;
288
+ }
289
+ /**
290
+ * Re-scan the skills directory for new SKILL.md files added between runs.
291
+ * New skills are registered and become available to the LLM immediately.
292
+ */
293
+ reloadSkillsFromDirectory() {
294
+ if (!this.skillsDir)
295
+ return false;
296
+ const added = this.skillManager.reloadFromDirectory(this.skillsDir);
297
+ if (added.length > 0) {
298
+ this.rebuildSystemPrompt();
299
+ return true;
300
+ }
301
+ return false;
302
+ }
303
+ /**
304
+ * Re-read the MEMORY.md index from disk if it was manually edited
305
+ * between runs. The index is a lightweight list of memory names +
306
+ * descriptions — full content is loaded on demand via the recall tool.
307
+ */
308
+ reloadMemoryIfChanged() {
309
+ if (this.memoryManager.reloadIfChanged()) {
310
+ this.rebuildSystemPrompt();
311
+ return true;
312
+ }
313
+ return false;
314
+ }
315
+ /**
316
+ * Incrementally connect to MCP servers that were added to the config
317
+ * since the last run. Already-connected servers are left untouched.
318
+ */
319
+ async reconnectMCPIfNeeded() {
320
+ if (!this.mcpClientManager || !this.mcpServerConfigs)
321
+ return;
322
+ const newServers = {};
323
+ for (const [name, config] of Object.entries(this.mcpServerConfigs)) {
324
+ if (!this.mcpClientManager.hasServer(name)) {
325
+ newServers[name] = config;
326
+ }
327
+ }
328
+ if (Object.keys(newServers).length === 0)
329
+ return;
330
+ const errors = await this.mcpClientManager.connectAll(newServers);
331
+ if (errors.length > 0) {
332
+ this.logger.warn("MCP", `${errors.length} new server(s) failed to connect: ` +
333
+ errors.map((e) => e.serverName).join(", "));
334
+ }
335
+ }
336
+ /**
337
+ * Reload all dynamic resources at the start of a run.
338
+ * Picks up changes made between conversation turns.
339
+ */
340
+ async reloadDynamicResources() {
341
+ this.reloadPreferencesIfChanged(); // rebuilds internally if changed
342
+ const rulesChanged = this.projectRules.reloadIfChanged();
343
+ this.reloadSkillsFromDirectory(); // rebuilds internally if changed
344
+ this.reloadMemoryIfChanged(); // rebuilds internally if changed
345
+ if (rulesChanged) {
346
+ this.rebuildSystemPrompt();
347
+ }
348
+ await this.reconnectMCPIfNeeded();
349
+ }
350
+ /**
351
+ * After a resume, recover results from sub-agents that were cancelled
352
+ * mid-run. Completed results are injected into context so the LLM can
353
+ * see them; still-running sub-agents get a notice.
354
+ */
355
+ recoverOrphanedSubAgentResults() {
356
+ if (!this.subAgentManager)
357
+ return;
358
+ const orphaned = this.subAgentManager.collectOrphanedResults();
359
+ for (const r of orphaned) {
360
+ const msg = message_1.Message.user(`[Sub-agent "${r.name}" (${r.subAgentId}) — recovered after interruption]\n\n${r.output}`);
361
+ this.contextManager.addMessage(msg.toDict());
362
+ }
363
+ // If some cancelled sub-agents are still running, let the LLM know
364
+ const stillRunning = this.subAgentManager.getActiveCount();
365
+ if (stillRunning > 0) {
366
+ const msg = message_1.Message.user(`[System] ${stillRunning} sub-agent(s) from the previous session are still running. ` +
367
+ `Their results will appear when ready. Do not re-spawn them.`);
368
+ this.contextManager.addMessage(msg.toDict());
369
+ }
370
+ }
371
+ /**
372
+ * Validate that user input won't overwhelm the context window.
373
+ *
374
+ * Returns a user-facing error string if the input is too large,
375
+ * or `null` if it passes the check.
376
+ */
377
+ validateInputSize(input) {
378
+ const { maxTokens } = this.contextManager.getState();
379
+ const inputTokens = (0, token_counter_1.countTokens)(input);
380
+ // Reserve ~20% for system prompt + tools + response overhead
381
+ const maxSafeInput = Math.floor(maxTokens * 0.8);
382
+ if (inputTokens > maxSafeInput) {
383
+ return (`Your input is too large (~${inputTokens} tokens). ` +
384
+ `The maximum safe input size is ~${maxSafeInput} tokens ` +
385
+ `(context window: ${maxTokens} tokens, with 20% reserved for system overhead). ` +
386
+ `Please split your request into smaller parts.`);
186
387
  }
388
+ return null;
389
+ }
390
+ /**
391
+ * Check whether a tool that requires approval should be executed.
392
+ *
393
+ * @returns `true` if approved, `false` if denied (or no callback configured).
394
+ */
395
+ async checkToolApproval(toolName, args) {
396
+ if (!this.onToolApproval) {
397
+ this.logger.warn("Approval", `Tool "${toolName}" requires approval but no onToolApproval configured — denied.`);
398
+ return false;
399
+ }
400
+ try {
401
+ return await this.onToolApproval(toolName, args);
402
+ }
403
+ catch {
404
+ this.logger.warn("Approval", `Approval callback threw for "${toolName}" — denied.`);
405
+ return false;
406
+ }
407
+ }
408
+ /**
409
+ * Execute a batch of tool calls from a single LLM response.
410
+ *
411
+ * When `enableParallelToolExecution` is true and all tools in the batch are
412
+ * parallel-safe (`sequential` is not set), tools execute concurrently via
413
+ * `Promise.allSettled`. Otherwise, they execute one at a time (serial).
414
+ *
415
+ * Handles the full lifecycle for each tool call:
416
+ * 1. Parse JSON arguments (malformed args → error result, no execution)
417
+ * 2. HITL approval check for tools marked `requireApproval`
418
+ * 3. Hook notifications (`onToolStart`, `onToolEnd`, `onToolError`)
419
+ * 4. Execution via `ToolRegistry.execute()`
420
+ * 5. Context injection (all results added after execution completes)
421
+ * 6. Post-execution: sub-agent spawn tracking, MCP failure warnings
422
+ *
423
+ * @param toolCalls The tool calls from the LLM response.
424
+ * @param mcpWarnedServers Set tracking which MCP servers have already
425
+ * been warned about in this batch.
426
+ * @returns Whether any tool in the batch failed.
427
+ */
428
+ async executeToolCallsBatch(toolCalls, mcpWarnedServers) {
429
+ // ── Step 1: Parse & validate all arguments up front ──────────────
430
+ const slots = [];
431
+ for (const tc of toolCalls) {
432
+ // 1a. JSON syntax check
433
+ let args;
434
+ try {
435
+ args = JSON.parse(tc.function.arguments);
436
+ }
437
+ catch {
438
+ const result = (0, types_1.toolError)(types_1.ToolErrorCode.ARGUMENTS_PARSE_ERROR, `[RETRYABLE:ARGUMENTS_PARSE_ERROR] Failed to parse arguments for tool "${tc.function.name}". ` +
439
+ `The raw arguments were: ${tc.function.arguments || "(empty)"}\n\n` +
440
+ `Please re-invoke the tool with correctly formatted JSON arguments.`, "retryable");
441
+ for (const h of this.hooks)
442
+ h.onToolError?.(tc.function.name, result.content, tc.id);
443
+ slots.push({ toolCall: tc, args: {}, result });
444
+ continue;
445
+ }
446
+ // 1b. JSON Schema validation against the tool's parameter definition
447
+ const tool = this.toolRegistry.getTool(tc.function.name);
448
+ if (tool) {
449
+ const validationError = (0, tool_validator_1.validateToolArgs)(tc.function.name, tool.parameters, args);
450
+ if (validationError) {
451
+ for (const h of this.hooks)
452
+ h.onToolError?.(tc.function.name, validationError.content, tc.id);
453
+ slots.push({ toolCall: tc, args, result: validationError });
454
+ continue;
455
+ }
456
+ }
457
+ slots.push({ toolCall: tc, args });
458
+ }
459
+ // ── Step 2: HITL approval — check all requiring tools upfront ────
460
+ for (const slot of slots) {
461
+ if (slot.result)
462
+ continue; // already failed at parse stage
463
+ const tool = this.toolRegistry.getTool(slot.toolCall.function.name);
464
+ if (tool?.requireApproval) {
465
+ const approved = await this.checkToolApproval(slot.toolCall.function.name, slot.args);
466
+ if (!approved) {
467
+ slot.result = (0, types_1.toolError)(types_1.ToolErrorCode.APPROVAL_DENIED, `[FATAL:APPROVAL_DENIED] Tool "${slot.toolCall.function.name}" requires approval and was denied. ` +
468
+ `Do NOT retry this tool. Find a different approach.`, "fatal");
469
+ for (const h of this.hooks)
470
+ h.onToolError?.(slot.toolCall.function.name, slot.result.content, slot.toolCall.id);
471
+ }
472
+ }
473
+ }
474
+ // ── Step 3: Decide serial vs parallel ──────────────────────────────
475
+ const executable = slots.filter((s) => !s.result);
476
+ const allParallelSafe = executable.every((s) => {
477
+ const tool = this.toolRegistry.getTool(s.toolCall.function.name);
478
+ return !tool?.sequential;
479
+ });
480
+ const shouldParallelize = this.enableParallelToolExecution &&
481
+ allParallelSafe &&
482
+ executable.length > 1;
483
+ // ── Step 4: Execute ────────────────────────────────────────────────
484
+ if (shouldParallelize) {
485
+ // Fire onToolStart for all executable tools before concurrent execution
486
+ for (const slot of executable) {
487
+ for (const h of this.hooks)
488
+ h.onToolStart?.(slot.toolCall.function.name, slot.args, slot.toolCall.id);
489
+ }
490
+ // Execute all in parallel.
491
+ // Each task fires its own onToolEnd / onToolError hooks so the
492
+ // TraceLogger (and other hook consumers) see events in actual
493
+ // completion order rather than array order.
494
+ await Promise.allSettled(executable.map(async (slot) => {
495
+ const result = await this.toolRegistry.execute(slot.toolCall.function.name, slot.args);
496
+ slot.result = result;
497
+ if (result.success) {
498
+ for (const h of this.hooks)
499
+ h.onToolEnd?.(slot.toolCall.function.name, result.content, slot.toolCall.id);
500
+ }
501
+ else {
502
+ for (const h of this.hooks)
503
+ h.onToolError?.(slot.toolCall.function.name, result.content, slot.toolCall.id);
504
+ }
505
+ }));
506
+ }
507
+ else {
508
+ // Serial path (legacy behaviour or forced by sequential tools)
509
+ for (const slot of executable) {
510
+ for (const h of this.hooks)
511
+ h.onToolStart?.(slot.toolCall.function.name, slot.args, slot.toolCall.id);
512
+ slot.result = await this.toolRegistry.execute(slot.toolCall.function.name, slot.args);
513
+ if (slot.result.success) {
514
+ for (const h of this.hooks)
515
+ h.onToolEnd?.(slot.toolCall.function.name, slot.result.content, slot.toolCall.id);
516
+ }
517
+ else {
518
+ for (const h of this.hooks)
519
+ h.onToolError?.(slot.toolCall.function.name, slot.result.content, slot.toolCall.id);
520
+ }
521
+ }
522
+ }
523
+ // ── Step 5: Inject results into context & post-process ─────────────
524
+ let hadFailure = false;
525
+ for (const slot of slots) {
526
+ const result = slot.result;
527
+ const toolName = slot.toolCall.function.name;
528
+ const toolMessage = message_1.Message.tool((0, boundaries_1.wrapAndScan)(`tool:${toolName}`, result.content), slot.toolCall.id, toolName);
529
+ this.contextManager.addMessage(toolMessage.toDict());
530
+ if (!result.success) {
531
+ hadFailure = true;
532
+ }
533
+ // Sub-agent spawned — purely informational; results arrive
534
+ // asynchronously and will be injected via pollSubAgentResults().
535
+ if (result.success && slot.toolCall.function.name === "spawn_subagent") {
536
+ this.logger.info("SubAgent", `Spawned "${slot.args.name ?? "unknown"}" — ` +
537
+ `result will arrive in a later iteration.`);
538
+ }
539
+ // MCP tool failure — warn about potential connection loss.
540
+ // Only warn once per server per batch to avoid duplicate messages.
541
+ if (!result.success &&
542
+ !builtin_1.BUILTIN_TOOL_NAMES.has(slot.toolCall.function.name)) {
543
+ const serverName = slot.toolCall.function.name.split("_")[0] ?? "unknown";
544
+ if (!mcpWarnedServers.has(serverName)) {
545
+ const isConnErr = result.content.includes("connection") ||
546
+ result.content.includes("not connected") ||
547
+ result.content.includes("ECONNREFUSED") ||
548
+ result.content.includes("ENOTFOUND");
549
+ if (isConnErr) {
550
+ mcpWarnedServers.add(serverName);
551
+ this.logger.info("MCP", `Connection lost to server "${serverName}" — ` +
552
+ `further calls to this server may fail.`);
553
+ const mcpWarn = message_1.Message.system(`MCP server "${serverName}" appears to be disconnected: ${result.content.slice(0, 200)}`);
554
+ this.contextManager.addMessage(mcpWarn.toDict());
555
+ }
556
+ }
557
+ }
558
+ }
559
+ return { hadFailure };
560
+ }
561
+ /**
562
+ * Check whether the token budget allows another LLM call.
563
+ *
564
+ * @param estimatedInputTokens Approximate tokens in the upcoming request
565
+ * (system prompt + context messages).
566
+ * @returns A user-facing error string if the budget is exhausted,
567
+ * or `null` if the call can proceed.
568
+ */
569
+ checkTokenBudget(estimatedInputTokens) {
570
+ if (!this.tokenBudget)
571
+ return null;
572
+ const status = this.tokenBudget.checkBeforeCall(estimatedInputTokens);
573
+ if (status.isExhausted) {
574
+ return (`Token budget exhausted: ${status.totalTokensUsed.toLocaleString()}/${status.maxTotalTokens.toLocaleString()} tokens used ` +
575
+ `across ${status.callCount} LLM calls. ` +
576
+ `Start a new conversation with agent.newTopic() or agent.clearConversation() to reset the budget.`);
577
+ }
578
+ // One-shot warning at 80 %
579
+ if (this.tokenBudget.shouldWarn()) {
580
+ this.logger.warn("TokenBudget", `Warning: ${Math.round(status.totalTokensUsed / status.maxTotalTokens * 100)}% of budget used ` +
581
+ `(${status.totalTokensUsed.toLocaleString()}/${status.maxTotalTokens.toLocaleString()} tokens).`);
582
+ }
583
+ return null;
187
584
  }
188
585
  // ─── Cancellation ────────────────────────────────────────────────────
189
586
  /**
190
587
  * Cancel the current run.
191
588
  *
192
- * Prevents any further checkpoint saves. Call this when the user presses
193
- * SIGINT to discard the session rather than persisting it.
589
+ * Aborts any in-flight LLM request via the AbortController, sets the
590
+ * cancellation flag, and cancels all running sub-agents. The agent loop
591
+ * will save a "cancelled" checkpoint and exit at its next iteration.
592
+ * The session is preserved on disk and can be resumed later.
194
593
  */
195
594
  cancel() {
595
+ // Abort the in-flight LLM request first so the agent doesn't keep
596
+ // waiting for a response that's about to be discarded anyway.
597
+ this._abortController?.abort();
196
598
  this._cancelled = true;
599
+ this.subAgentManager?.cancelAll();
197
600
  }
198
601
  /**
199
602
  * Whether the run has been cancelled by the user.
@@ -267,17 +670,41 @@ class Agent {
267
670
  };
268
671
  }
269
672
  /**
270
- * Save a session checkpoint to disk (if session manager is configured
271
- * and the run has NOT been cancelled).
673
+ * Save a session checkpoint to disk (if session manager is configured).
272
674
  */
273
675
  saveCheckpoint(status = "active") {
274
676
  if (!this.sessionManager)
275
677
  return;
276
- if (this.isCancelled)
277
- return; // Don't save on abort
278
678
  const state = this.buildBaseSessionState(status);
279
679
  this.sessionManager.saveCheckpoint(state);
280
680
  }
681
+ /**
682
+ * Handle an LLMNetworkError: save an interrupted checkpoint if
683
+ * checkpointing is enabled, and return a user-facing message with
684
+ * resume instructions.
685
+ *
686
+ * @param err The network error that occurred.
687
+ * @param iteration The current iteration number (for the log).
688
+ * @param resumeInstruction What the user should type to resume
689
+ * (e.g. "continue with my previous request").
690
+ */
691
+ handleNetworkError(err, iteration, resumeInstruction = "continue") {
692
+ if (this.checkpointingEnabled) {
693
+ this.saveCheckpoint("interrupted");
694
+ }
695
+ const sid = this.sessionManager?.getSessionId() ?? "unknown";
696
+ this.logger.error("Network Error", `${err.cause}: ${err.message}`);
697
+ if (this.checkpointingEnabled && this.sessionManager) {
698
+ return (`[Network Error] ${err.message}\n\n` +
699
+ `Your session "${sid}" has been saved (iteration ${iteration}).\n` +
700
+ `After your network is restored, resume with:\n` +
701
+ ` agent.resume("${sid}", "${resumeInstruction}")\n\n` +
702
+ `Or start a new session by calling agent.run() again with a fresh input.`);
703
+ }
704
+ // Checkpointing not enabled — just report the error
705
+ return (`[Network Error] ${err.message}\n\n` +
706
+ `Please check your network connection and try again.`);
707
+ }
281
708
  /**
282
709
  * Restore agent state from a previously saved session.
283
710
  *
@@ -301,6 +728,9 @@ class Agent {
301
728
  throw new Error(`Session "${sessionId}" was created by a ${state.agentType} agent, ` +
302
729
  `but this agent is type "${expectedType}". Cannot resume.`);
303
730
  }
731
+ // Clear cancellation state — the user explicitly chose to resume
732
+ this._cancelled = false;
733
+ this._abortController = undefined; // will be recreated in run()
304
734
  // Sync session manager to the restored session ID so subsequent
305
735
  // checkpoints write to the correct file
306
736
  this.sessionManager.setSessionId(state.sessionId);
@@ -324,6 +754,49 @@ class Agent {
324
754
  */
325
755
  clearConversation() {
326
756
  this.contextManager.clear();
757
+ this.tokenBudget?.reset();
758
+ }
759
+ /**
760
+ * Continue the current conversation with a follow-up input.
761
+ *
762
+ * Equivalent to `run()` but conveys the semantics of a multi-turn
763
+ * conversation continuation. Messages from previous calls are preserved
764
+ * in context, so the LLM sees the full conversation history.
765
+ *
766
+ * @param input The follow-up message from the user.
767
+ * @returns The agent's response.
768
+ */
769
+ async chat(input) {
770
+ return this.run(input);
771
+ }
772
+ /**
773
+ * Start a new topic with a fresh conversation.
774
+ *
775
+ * Clears the message history (preserving the system prompt and
776
+ * configuration) and runs the input as the first message of a
777
+ * new conversation. The token budget is also reset.
778
+ *
779
+ * @param input The first message of the new topic.
780
+ * @returns The agent's response.
781
+ */
782
+ async newTopic(input) {
783
+ this.clearConversation();
784
+ return this.run(input);
785
+ }
786
+ /**
787
+ * The number of messages in the current conversation (excluding the
788
+ * system message). Use this to check whether there is an active
789
+ * conversation or to monitor context growth.
790
+ */
791
+ get conversationLength() {
792
+ return this.contextManager.getMessages().length;
793
+ }
794
+ /**
795
+ * Get the cumulative token consumption and cost for the current session.
796
+ * Returns null if no `tokenBudgetConfig` was configured.
797
+ */
798
+ getSessionCost() {
799
+ return this.tokenBudget?.getSessionCost() ?? null;
327
800
  }
328
801
  /**
329
802
  * Reset the agent to its initial state.
@@ -332,10 +805,13 @@ class Agent {
332
805
  * After calling reset(), the agent behaves as if newly constructed.
333
806
  */
334
807
  reset() {
808
+ this._abortController?.abort();
809
+ this._cancelled = false;
335
810
  this.contextManager.clear();
336
811
  this.coreSystemPrompt = "";
337
812
  this.preferences = {};
338
- this.pendingErrorTraceId = null;
813
+ this.tokenBudget?.reset();
814
+ this.subAgentManager?.cancelAll();
339
815
  if (this.sessionManager) {
340
816
  this.sessionManager.deleteSession(this.sessionManager.getSessionId());
341
817
  }
@@ -364,13 +840,77 @@ class Agent {
364
840
  if (this._mcpInitialized)
365
841
  return;
366
842
  this._mcpInitialized = true;
367
- if (!this.mcpServerConfigs || Object.keys(this.mcpServerConfigs).length === 0) {
368
- return;
843
+ // ── Error tracking tool ──────────────────────────────────────────
844
+ try {
845
+ this.toolRegistry.register((0, list_errors_1.createListErrorsTool)(this.toolRegistry));
369
846
  }
370
- this.mcpClientManager = new mcp_client_manager_1.McpClientManager(this.toolRegistry);
371
- const errors = await this.mcpClientManager.connectAll(this.mcpServerConfigs);
372
- if (errors.length > 0) {
373
- console.warn(`[MCP] ${errors.length} of ${Object.keys(this.mcpServerConfigs).length} server(s) failed to connect.`);
847
+ catch {
848
+ this.logger.debug("Init", `"list_errors" already registered — keeping existing.`);
849
+ }
850
+ // ── MCP connections ──────────────────────────────────────────────
851
+ if (this.mcpServerConfigs && Object.keys(this.mcpServerConfigs).length > 0) {
852
+ this.mcpClientManager = new mcp_client_manager_1.McpClientManager(this.toolRegistry, this.logger);
853
+ const errors = await this.mcpClientManager.connectAll(this.mcpServerConfigs);
854
+ if (errors.length > 0) {
855
+ this.logger.warn("MCP", `${errors.length} of ${Object.keys(this.mcpServerConfigs).length} server(s) failed to connect.`);
856
+ }
857
+ }
858
+ // ── Sub-agent registry ───────────────────────────────────────────
859
+ if (this.subAgentsDir) {
860
+ this.subAgentManager = new subagent_manager_1.SubAgentManager();
861
+ this.subAgentManager.setLogger(this.logger);
862
+ this.subAgentManager.bind(this.llm, this.toolRegistry, this.skillManager, this.skillsDir, undefined, undefined, this.subAgentLLM);
863
+ this.subAgentManager.registerFromDirectory(this.subAgentsDir);
864
+ // Register sub-agent tools into the tool registry
865
+ try {
866
+ this.toolRegistry.register((0, list_subagents_1.createListSubagentsTool)(this.subAgentManager));
867
+ }
868
+ catch {
869
+ this.logger.debug("Init", `"list_subagents" already registered — keeping existing.`);
870
+ }
871
+ try {
872
+ this.toolRegistry.register((0, spawn_subagent_1.createSpawnSubagentTool)(this.subAgentManager));
873
+ }
874
+ catch {
875
+ this.logger.debug("Init", `"spawn_subagent" already registered — keeping existing.`);
876
+ }
877
+ }
878
+ // ── RAG knowledge base ─────────────────────────────────────────────
879
+ if (this.ragConfig) {
880
+ this.ragManager = new rag_manager_1.RAGManager(this.ragConfig, this.logger);
881
+ await this.ragManager.index();
882
+ try {
883
+ this.toolRegistry.register((0, search_knowledge_1.createSearchKnowledgeTool)(this.ragManager));
884
+ }
885
+ catch {
886
+ this.logger.debug("Init", `"search_knowledge" already registered — keeping existing.`);
887
+ }
888
+ try {
889
+ this.toolRegistry.register((0, search_knowledge_1.createListKnowledgeDocumentsTool)(this.ragManager));
890
+ }
891
+ catch {
892
+ this.logger.debug("Init", `"list_knowledge_documents" already registered — keeping existing.`);
893
+ }
894
+ }
895
+ // ── Skill tool (LLM-driven activation) ────────────────────────────
896
+ try {
897
+ this.toolRegistry.register((0, skill_1.createSkillTool)(this.skillManager, () => this.rebuildSystemPrompt()));
898
+ }
899
+ catch {
900
+ this.logger.debug("Init", `"skill" already registered — keeping existing.`);
901
+ }
902
+ // ── Remember / Recall tools (long-term memory) ────────────────────
903
+ try {
904
+ this.toolRegistry.register((0, remember_1.createRememberTool)(this.memoryManager));
905
+ }
906
+ catch {
907
+ this.logger.debug("Init", `"remember" already registered — keeping existing.`);
908
+ }
909
+ try {
910
+ this.toolRegistry.register((0, recall_1.createRecallTool)(this.memoryManager));
911
+ }
912
+ catch {
913
+ this.logger.debug("Init", `"recall" already registered — keeping existing.`);
374
914
  }
375
915
  }
376
916
  /**
@@ -381,6 +921,48 @@ class Agent {
381
921
  */
382
922
  async shutdown() {
383
923
  await this.mcpClientManager?.disconnectAll();
924
+ if (this.subAgentManager) {
925
+ await this.subAgentManager.awaitAll();
926
+ }
927
+ }
928
+ // ─── Sub-Agent ────────────────────────────────────────────────────────
929
+ /**
930
+ * Check whether sub-agents are available.
931
+ *
932
+ * Used by `buildSystemPrompt()` to decide whether to include sub-agent
933
+ * delegation instructions. Returns true only when a SubAgentManager is
934
+ * configured AND has at least one registered definition.
935
+ */
936
+ hasSubAgents() {
937
+ return this.subAgentManager?.hasDefinitions() === true;
938
+ }
939
+ /**
940
+ * Spawn a sub-agent by definition name.
941
+ *
942
+ * The sub-agent runs asynchronously — this method returns immediately.
943
+ * Call `pollSubAgentResults()` at the start of each iteration to
944
+ * collect completed results.
945
+ *
946
+ * @param name The registered sub-agent definition name.
947
+ * @param input The task description for the sub-agent.
948
+ * @returns The unique run ID.
949
+ */
950
+ spawnSubAgent(name, input) {
951
+ if (!this.subAgentManager) {
952
+ throw new Error("No SubAgentManager configured. Set `subAgentsDir` in AgentConfig.");
953
+ }
954
+ return this.subAgentManager.spawn(name, input);
955
+ }
956
+ /**
957
+ * Poll for completed sub-agent results.
958
+ *
959
+ * Should be called at the start of each ReAct iteration to inject
960
+ * sub-agent outputs into the main agent's context.
961
+ */
962
+ async pollSubAgentResults() {
963
+ if (!this.subAgentManager)
964
+ return [];
965
+ return this.subAgentManager.pollCompleted();
384
966
  }
385
967
  // ─── Error Trace & Analysis ──────────────────────────────────────────
386
968
  /**
@@ -390,38 +972,38 @@ class Agent {
390
972
  return this.toolRegistry.getErrorTracker();
391
973
  }
392
974
  /**
393
- * Call after a tool returns an error string — checks if the error
394
- * indicates a tool failure (with retry guidance) and saves the trace ID
395
- * so the next LLM analysis thought can be captured.
975
+ * Call after a tool returns a result — checks if the result indicates
976
+ * a tool failure (retryable or fatal) and saves the trace ID so the
977
+ * next LLM analysis thought can be captured.
396
978
  *
397
- * @param toolName The name of the tool that failed.
398
- * @param result The string returned by ToolRegistry.execute().
979
+ * @param toolName The name of the tool that was executed.
980
+ * @param result The structured ToolResult returned by ToolRegistry.execute().
399
981
  */
400
- trackToolErrorForAnalysis(toolName, result) {
401
- const tracker = this.errorTracker;
402
- if (!tracker)
403
- return;
404
- // Check if the result contains retry guidance (indicating a failure)
405
- if (result.includes("[Retry Guidance]") || result.includes("has been automatically disabled")) {
406
- const activeTraceId = tracker.getActiveTraceId(toolName);
407
- if (activeTraceId) {
408
- this.pendingErrorTraceId = activeTraceId;
409
- }
410
- }
411
- }
412
982
  /**
413
- * Records the LLM's analysis thought against the pending tool error trace.
414
- * Should be called each iteration after parsing the LLM's response.
983
+ * Capture the LLM's reasoning as error analysis for any active tool error traces.
984
+ *
985
+ * Call this after parsing the LLM's `thought` from each response. If any tools
986
+ * have an open failure chain (active trace), the thought is recorded as the
987
+ * LLM's analysis of what went wrong and how to proceed.
988
+ *
989
+ * This feeds the error→analysis pipeline:
990
+ * 1. Tool fails → recordFailure() creates an active trace
991
+ * 2. LLM sees error → its next thought IS the analysis
992
+ * 3. Analysis → recordAnalysis() attaches the LLM's reasoning to the trace
993
+ *
994
+ * For cross-session learning, use ErrorNotebook (错题本) via ReflectionHook.
415
995
  *
416
- * @param thought The LLM's thought/analysis content.
996
+ * @param thought The LLM's reasoning (from parsed.thought).
417
997
  */
418
- captureAnalysisFromThought(thought) {
419
- if (this.pendingErrorTraceId && thought) {
420
- const tracker = this.errorTracker;
421
- if (tracker) {
422
- tracker.recordAnalysis(this.pendingErrorTraceId, thought);
423
- }
424
- this.pendingErrorTraceId = null;
998
+ captureErrorAnalysis(thought) {
999
+ if (!thought)
1000
+ return;
1001
+ const tracker = this.toolRegistry.getErrorTracker();
1002
+ if (!tracker)
1003
+ return;
1004
+ const activeTraces = tracker.getActiveTraces();
1005
+ for (const { traceId } of activeTraces) {
1006
+ tracker.recordAnalysis(traceId, thought);
425
1007
  }
426
1008
  }
427
1009
  /**