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
@@ -0,0 +1,769 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FusionAgent = void 0;
4
+ const agent_1 = require("./agent");
5
+ const message_1 = require("../messages/message");
6
+ const types_1 = require("../messages/types");
7
+ const response_schema_1 = require("./response-schema");
8
+ const system_prompts_1 = require("./system-prompts");
9
+ const errors_1 = require("../llm/errors");
10
+ const interface_1 = require("../llm/interface");
11
+ const boundaries_1 = require("../security/boundaries");
12
+ const reflection_agent_1 = require("../reflection/reflection-agent");
13
+ // ─── System Prompt ────────────────────────────────────────────────────────
14
+ const DEFAULT_FUSION_SYSTEM_PROMPT = `You are a helpful AI assistant powered by the Fusion Agent paradigm.
15
+ You dynamically adapt your strategy to the task's complexity.
16
+
17
+ For SIMPLE tasks: direct ReAct (Reasoning + Acting) loop.
18
+ For COMPLEX tasks: Plan → Execute → Reflect.
19
+
20
+ You have access to a set of tools you can use to answer the user's question.
21
+ ${system_prompts_1.SECURITY_GUIDANCE}
22
+ ${system_prompts_1.TOOL_ERROR_RECOVERY}${response_schema_1.FUSION_EXECUTION_INSTRUCTIONS}`;
23
+ // ─── FusionAgent ──────────────────────────────────────────────────────────
24
+ /**
25
+ * Fusion Agent that dynamically combines ReAct, Plan-and-Solve, and
26
+ * Reflection paradigms based on task complexity.
27
+ *
28
+ * ## Execution flow:
29
+ * ```
30
+ * User Input
31
+ * ↓
32
+ * [1. Route] LLM judges complexity → simple / complex
33
+ * ↓
34
+ * ├─ simple ──→ [3. ReAct Execute Loop]
35
+ * │
36
+ * └─ complex → [2. Plan] LLM creates step-by-step plan
37
+ * ↓
38
+ * [Confirm?] Optional user approval
39
+ * ↓
40
+ * [3. ReAct Execute Loop] With plan tracking
41
+ * ↓
42
+ * [4. Reflect] off | post-hoc | inline | both
43
+ * ↓
44
+ * Final Answer
45
+ * ```
46
+ *
47
+ * ## Session persistence
48
+ * When `enableCheckpointing` is set, the agent auto-saves checkpoints
49
+ * that include full fusion state (complexity, plan, reflection progress)
50
+ * so the session can be resumed after a network interruption.
51
+ */
52
+ class FusionAgent extends agent_1.Agent {
53
+ // ── Configuration ───────────────────────────────────────────────────
54
+ routing;
55
+ planConfirmation;
56
+ onPlanConfirm;
57
+ maxPlanSteps;
58
+ reflectionMode;
59
+ reflectionInterval;
60
+ notebook;
61
+ maxIterations;
62
+ replanThreshold;
63
+ // ── Runtime state ───────────────────────────────────────────────────
64
+ /** Task complexity determined during routing. */
65
+ complexity = "complex";
66
+ /** Whether routing has been completed. */
67
+ routed = false;
68
+ /** The current plan steps (empty until the plan is created). */
69
+ currentPlan = [];
70
+ /** Whether a plan has been created in this run. */
71
+ hasPlan = false;
72
+ /** How many plan steps have been completed. */
73
+ completedSteps = 0;
74
+ /** Consecutive tool failures (resets on success). */
75
+ consecutiveFailures = 0;
76
+ /** How many inline reflections have been done in this run. */
77
+ inlineReflectionsDone = 0;
78
+ /** Internal flag: when true, run() skips state reset (used by resume()). */
79
+ _skipStateReset = false;
80
+ /** Routing reason for logging. */
81
+ routeReason = "";
82
+ constructor(config) {
83
+ const mergedConfig = {
84
+ ...config,
85
+ systemPrompt: config.systemPrompt ?? DEFAULT_FUSION_SYSTEM_PROMPT,
86
+ };
87
+ super(mergedConfig);
88
+ this.routing = config.routing ?? "auto";
89
+ this.planConfirmation = config.planConfirmation ?? "auto";
90
+ this.onPlanConfirm = config.onPlanConfirm;
91
+ this.maxPlanSteps = config.maxPlanSteps ?? 12;
92
+ this.reflectionMode = config.reflection ?? "off";
93
+ this.reflectionInterval = config.reflectionInterval ?? 3;
94
+ this.notebook = config.notebook;
95
+ this.maxIterations = config.maxIterations ?? 15;
96
+ this.replanThreshold = config.replanThreshold ?? 2;
97
+ // Validate: notebook required for post-hoc/both modes
98
+ if ((this.reflectionMode === "post-hoc" || this.reflectionMode === "both") &&
99
+ !this.notebook) {
100
+ throw new Error("FusionAgent: 'notebook' (ErrorNotebook) is required when reflection is 'post-hoc' or 'both'.");
101
+ }
102
+ this.rebuildSystemPrompt();
103
+ }
104
+ // ─── Main Entry Point ───────────────────────────────────────────────
105
+ async run(input) {
106
+ // Consume the skip-reset flag immediately
107
+ const skipStateReset = this._skipStateReset;
108
+ this._skipStateReset = false;
109
+ // ── Pre-flight: reject oversized input ───────────────────────────
110
+ const sizeError = this.validateInputSize(input);
111
+ if (sizeError)
112
+ return sizeError;
113
+ // ── Create fresh abort controller for this run ───────────────────
114
+ this._abortController = new AbortController();
115
+ // ── Async initialization ─────────────────────────────────────────
116
+ await this.init();
117
+ // ── Reload dynamic resources ─────────────────────────────────────
118
+ await this.reloadDynamicResources();
119
+ // ── Recover orphaned sub-agent results ───────────────────────────
120
+ this.recoverOrphanedSubAgentResults();
121
+ // ── Create user message ──────────────────────────────────────────
122
+ const userMessage = message_1.Message.user(input);
123
+ this.contextManager.addMessage(userMessage.toDict());
124
+ // ── Reset runtime state for this run ─────────────────────────────
125
+ if (!skipStateReset) {
126
+ this.complexity = "complex";
127
+ this.routed = false;
128
+ this.routeReason = "";
129
+ this.currentPlan = [];
130
+ this.hasPlan = false;
131
+ this.completedSteps = 0;
132
+ this.consecutiveFailures = 0;
133
+ this.inlineReflectionsDone = 0;
134
+ }
135
+ // Save initial checkpoint
136
+ if (this.checkpointingEnabled) {
137
+ this.saveCheckpoint("active");
138
+ }
139
+ // ── Phase 1: Route ───────────────────────────────────────────────
140
+ if (this.routing === "auto") {
141
+ const routeResult = await this.route(input);
142
+ this.complexity = routeResult.complexity;
143
+ this.routeReason = routeResult.reason;
144
+ this.routed = true;
145
+ this.logger.info("Fusion", `Route: ${this.complexity} — ${this.routeReason}`);
146
+ }
147
+ else if (this.routing === "force-react") {
148
+ this.complexity = "simple";
149
+ this.routed = true;
150
+ this.logger.info("Fusion", "Route: simple (forced)");
151
+ }
152
+ else {
153
+ // force-plan
154
+ this.complexity = "complex";
155
+ this.routed = true;
156
+ this.logger.info("Fusion", "Route: complex (forced)");
157
+ }
158
+ // Save checkpoint after routing
159
+ if (this.checkpointingEnabled) {
160
+ this.saveCheckpoint("active");
161
+ }
162
+ // ── Phase 2: Plan (complex tasks only) ────────────────────────────
163
+ if (this.complexity === "complex") {
164
+ const planResult = await this.createPlan(input);
165
+ if (typeof planResult === "string") {
166
+ // planResult is a string → plan was aborted / returned as answer
167
+ for (const h of this.hooks)
168
+ h.onFinish?.(planResult);
169
+ return planResult;
170
+ }
171
+ // planResult is string[] → plan was confirmed, continue
172
+ this.currentPlan = planResult;
173
+ this.hasPlan = true;
174
+ for (const h of this.hooks)
175
+ h.onPlanCreated?.(this.currentPlan);
176
+ // Save checkpoint after plan creation
177
+ if (this.checkpointingEnabled) {
178
+ this.saveCheckpoint("active");
179
+ }
180
+ }
181
+ // ── Phase 3: ReAct Execute Loop ───────────────────────────────────
182
+ const answer = await this.executeReActLoop();
183
+ // ── Phase 4: Reflection ───────────────────────────────────────────
184
+ await this.runReflection(input, answer);
185
+ for (const h of this.hooks)
186
+ h.onFinish?.(answer);
187
+ return answer;
188
+ }
189
+ // ─── Phase 1: Route (Complexity Classification) ─────────────────────
190
+ /**
191
+ * Ask the LLM to classify the task complexity.
192
+ *
193
+ * Sends a lightweight prompt separate from the main conversation so
194
+ * the routing doesn't pollute the execution context.
195
+ */
196
+ async route(input) {
197
+ const messages = [
198
+ { role: types_1.Role.System, content: response_schema_1.FUSION_ROUTE_INSTRUCTIONS },
199
+ { role: types_1.Role.User, content: input },
200
+ ];
201
+ try {
202
+ const response = await this.llm.chat(messages, [], this._abortController?.signal);
203
+ const parsed = (0, response_schema_1.parseFusionRouteResponse)(response.content);
204
+ // Record token usage
205
+ if (response.usage) {
206
+ this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
207
+ }
208
+ return parsed;
209
+ }
210
+ catch (err) {
211
+ if (err instanceof errors_1.LLMNetworkError) {
212
+ this.logger.warn("Fusion", `Route LLM call failed: ${err.message}. Defaulting to complex.`);
213
+ }
214
+ // Default to complex for safety
215
+ return {
216
+ complexity: "complex",
217
+ reason: "Route classification failed; defaulting to complex to be safe.",
218
+ };
219
+ }
220
+ }
221
+ // ─── Phase 2: Plan (Plan Generation) ────────────────────────────────
222
+ /**
223
+ * Generate a plan for a complex task.
224
+ *
225
+ * Sends the user input to the LLM with plan-generation instructions.
226
+ * If plan confirmation is configured, asks the user before proceeding.
227
+ *
228
+ * @returns The plan steps (string[]), or the plan text if confirmation
229
+ * was denied (returned as the final answer string).
230
+ */
231
+ async createPlan(input) {
232
+ // Rebuild system prompt with plan-generation context
233
+ const planPrompt = this.buildSystemPrompt() +
234
+ "\n\nYou are in the PLANNING phase. Analyze the user's request and create a detailed, " +
235
+ "step-by-step plan. Output ONLY: {\"thought\": \"...\", \"plan\": [\"Step 1: ...\", ...]}\n" +
236
+ `Maximum ${this.maxPlanSteps} steps. Each step must be concrete and actionable.`;
237
+ this.contextManager.setSystemMessage(planPrompt);
238
+ const contextMessages = this.contextManager.getContextMessages();
239
+ // Token budget check
240
+ const estimatedInput = this.contextManager.getCurrentTokens();
241
+ const budgetError = this.checkTokenBudget(estimatedInput);
242
+ if (budgetError)
243
+ return budgetError;
244
+ for (const h of this.hooks)
245
+ h.onLLMStart?.(contextMessages, this.toolRegistry.getTools());
246
+ let response;
247
+ try {
248
+ response = await this.llm.chat(contextMessages, this.toolRegistry.getTools(), this._abortController?.signal);
249
+ }
250
+ catch (err) {
251
+ // Cancellation by user (AbortController)
252
+ if (this.isCancelled) {
253
+ this.saveCheckpoint("cancelled");
254
+ const sid = this.sessionManager?.getSessionId() ?? "unknown";
255
+ const cancelMsg = `Execution cancelled by user. Session "${sid}" preserved — ` +
256
+ `resume with agent.resume("${sid}", "<your prompt>").`;
257
+ for (const h of this.hooks)
258
+ h.onFinish?.(cancelMsg);
259
+ return cancelMsg;
260
+ }
261
+ if (err instanceof errors_1.LLMNetworkError) {
262
+ for (const h of this.hooks)
263
+ h.onLLMError?.(err);
264
+ return this.handleNetworkError(err, 0, "continue creating a plan");
265
+ }
266
+ throw err;
267
+ }
268
+ for (const h of this.hooks)
269
+ h.onLLMEnd?.(response);
270
+ if (response.usage) {
271
+ this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
272
+ }
273
+ const parsed = (0, response_schema_1.parseFusionResponse)(response.content);
274
+ // Store the assistant message in context
275
+ const assistantMessage = message_1.Message.assistant(response.content, response.tool_calls);
276
+ this.contextManager.addMessage(assistantMessage.toDict());
277
+ if (parsed.plan && parsed.plan.length > 0) {
278
+ const plan = parsed.plan.slice(0, this.maxPlanSteps);
279
+ this.logger.info("Plan", `Created ${plan.length}-step plan:`);
280
+ for (let i = 0; i < plan.length; i++) {
281
+ this.logger.info("Plan", ` ${i + 1}. ${plan[i]}`);
282
+ }
283
+ // ── Plan confirmation ──────────────────────────────────────────
284
+ const shouldConfirm = this.shouldConfirmPlan(plan);
285
+ if (shouldConfirm && this.onPlanConfirm) {
286
+ const confirmed = await this.onPlanConfirm(plan, this.routeReason);
287
+ if (!confirmed) {
288
+ // User rejected the plan — return it as the answer
289
+ const planText = `I've created the following plan for your task. ` +
290
+ `Please review and let me know if you'd like any changes:\n\n` +
291
+ plan.map((s, i) => `${i + 1}. ${s}`).join("\n") +
292
+ `\n\nYou can resume execution by saying "proceed" or "execute the plan".`;
293
+ return planText;
294
+ }
295
+ }
296
+ else if (shouldConfirm && !this.onPlanConfirm) {
297
+ // Confirmation needed but no callback — return plan as answer
298
+ const planText = `I've created the following plan for your task:\n\n` +
299
+ plan.map((s, i) => `${i + 1}. ${s}`).join("\n") +
300
+ `\n\n(Plan confirmation is required but no confirmation callback is configured. ` +
301
+ `Send your approval to continue, or set planConfirmation to "never" to auto-execute.)`;
302
+ return planText;
303
+ }
304
+ if (parsed.thought) {
305
+ this.logger.info("Thought", parsed.thought);
306
+ for (const h of this.hooks)
307
+ h.onThought?.(parsed.thought);
308
+ }
309
+ return plan;
310
+ }
311
+ // LLM didn't generate a plan — fall back to ReAct
312
+ this.logger.info("Fusion", "No plan generated by LLM — falling back to direct ReAct.");
313
+ this.complexity = "simple";
314
+ return [];
315
+ }
316
+ /**
317
+ * Determine whether plan confirmation is needed based on the
318
+ * planConfirmation setting and plan content.
319
+ */
320
+ shouldConfirmPlan(_plan) {
321
+ switch (this.planConfirmation) {
322
+ case "always":
323
+ return true;
324
+ case "never":
325
+ return false;
326
+ case "auto":
327
+ // Auto: check if plan contains risky keywords
328
+ // (deploy, delete, drop, migration, etc.)
329
+ const riskyKeywords = [
330
+ "deploy", "delete", "drop", "migrate", "truncate",
331
+ "destroy", "purge", "reset", "format",
332
+ ];
333
+ const planText = _plan.join(" ").toLowerCase();
334
+ return riskyKeywords.some((kw) => planText.includes(kw));
335
+ default:
336
+ return false;
337
+ }
338
+ }
339
+ // ─── Phase 3: ReAct Execute Loop ────────────────────────────────────
340
+ /**
341
+ * Main execution loop with ReAct reasoning, plan tracking, and optional
342
+ * inline reflection.
343
+ */
344
+ async executeReActLoop() {
345
+ // Rebuild system prompt for execution mode
346
+ this.rebuildContextWithPlan();
347
+ let consecutiveEmptyIterations = 0;
348
+ const EMPTY_ITERATION_LIMIT = 5;
349
+ let consecutiveTruncations = 0;
350
+ const MAX_TRUNCATION_CONTINUES = 3;
351
+ for (let iteration = 0; iteration < this.maxIterations; iteration++) {
352
+ // ── Check cancellation ──────────────────────────────────────────
353
+ if (this.isCancelled) {
354
+ this.saveCheckpoint("cancelled");
355
+ const sid = this.sessionManager?.getSessionId() ?? "unknown";
356
+ const cancelMsg = `Execution cancelled by user. Session "${sid}" preserved — ` +
357
+ `resume with agent.resume("${sid}", "<your prompt>").`;
358
+ return cancelMsg;
359
+ }
360
+ // ── Poll sub-agent results ──────────────────────────────────────
361
+ const subResults = await this.pollSubAgentResults();
362
+ for (const r of subResults) {
363
+ const source = `subagent:${r.name}`;
364
+ const msg = new message_1.Message(types_1.Role.User, (0, boundaries_1.wrapAndScan)(source, r.output), {
365
+ name: source,
366
+ });
367
+ this.contextManager.addMessage(msg.toDict());
368
+ }
369
+ await this.checkAndCompress();
370
+ // ── Rebuild system prompt (plan progress + replan hint) ─────────
371
+ this.rebuildContextWithPlan();
372
+ // ── Token budget ────────────────────────────────────────────────
373
+ const contextMessages = this.contextManager.getContextMessages();
374
+ const budgetError = this.checkTokenBudget(this.tokenBudget ? this.contextManager.getCurrentTokens() : 0);
375
+ if (budgetError)
376
+ return budgetError;
377
+ // ── LLM call ────────────────────────────────────────────────────
378
+ for (const h of this.hooks)
379
+ h.onLLMStart?.(contextMessages, this.toolRegistry.getTools());
380
+ let response;
381
+ try {
382
+ response = await this.llm.chat(contextMessages, this.toolRegistry.getTools(), this._abortController?.signal);
383
+ }
384
+ catch (err) {
385
+ // Cancellation by user (AbortController) — exit the loop cleanly
386
+ if (this.isCancelled) {
387
+ this.saveCheckpoint("cancelled");
388
+ const sid = this.sessionManager?.getSessionId() ?? "unknown";
389
+ const cancelMsg = `Execution cancelled by user. Session "${sid}" preserved — ` +
390
+ `resume with agent.resume("${sid}", "<your prompt>").`;
391
+ for (const h of this.hooks)
392
+ h.onFinish?.(cancelMsg);
393
+ return cancelMsg;
394
+ }
395
+ if (err instanceof errors_1.LLMNetworkError) {
396
+ for (const h of this.hooks)
397
+ h.onLLMError?.(err);
398
+ return this.handleNetworkError(err, iteration + 1, "continue with what you were doing");
399
+ }
400
+ throw err;
401
+ }
402
+ for (const h of this.hooks)
403
+ h.onLLMEnd?.(response);
404
+ if (response.usage) {
405
+ this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
406
+ }
407
+ const parsed = (0, response_schema_1.parseFusionResponse)(response.content);
408
+ // Capture LLM analysis of active tool error traces
409
+ if (parsed.thought) {
410
+ this.captureErrorAnalysis(parsed.thought);
411
+ }
412
+ // ── Store assistant message ─────────────────────────────────────
413
+ const assistantMessage = message_1.Message.assistant(response.content, response.tool_calls);
414
+ // ── Handle max_tokens truncation ────────────────────────────────
415
+ const isTruncated = response.responseError?.code === interface_1.LLMResponseErrorCode.MAX_TOKENS;
416
+ if (isTruncated) {
417
+ consecutiveTruncations++;
418
+ if (consecutiveTruncations > MAX_TRUNCATION_CONTINUES) {
419
+ const fallback = parsed.answer
420
+ ? parsed.answer +
421
+ "\n\n[Note: Response may be incomplete due to repeated output limits.]"
422
+ : "I apologize, but I'm unable to complete this response due to output length constraints. " +
423
+ "Please try breaking your request into smaller steps.";
424
+ return fallback;
425
+ }
426
+ this.contextManager.addMessage(assistantMessage.toDict());
427
+ const continueMsg = message_1.Message.user("Your previous response was cut off (max output tokens reached). " +
428
+ "Continue exactly where you left off — do NOT repeat any content already written. " +
429
+ "If you were calling tools, re-invoke them with complete arguments.");
430
+ this.contextManager.addMessage(continueMsg.toDict());
431
+ }
432
+ else {
433
+ this.contextManager.addMessage(assistantMessage.toDict());
434
+ consecutiveTruncations = 0;
435
+ }
436
+ // ── Tool calls ──────────────────────────────────────────────────
437
+ if (response.tool_calls && response.tool_calls.length > 0) {
438
+ consecutiveEmptyIterations = 0;
439
+ if (parsed.thought) {
440
+ this.logger.info("Thought", parsed.thought);
441
+ for (const h of this.hooks)
442
+ h.onThought?.(parsed.thought);
443
+ }
444
+ // Non-truncation quality issues
445
+ if (response.responseError &&
446
+ response.responseError.code !== interface_1.LLMResponseErrorCode.MAX_TOKENS &&
447
+ response.responseError.code !== interface_1.LLMResponseErrorCode.OK) {
448
+ const warnMsg = message_1.Message.system(`LLM response quality issue: ${response.responseError.message}`);
449
+ this.contextManager.addMessage(warnMsg.toDict());
450
+ }
451
+ const mcpWarnedServers = new Set();
452
+ const { hadFailure } = await this.executeToolCallsBatch(response.tool_calls, mcpWarnedServers);
453
+ // Update failure counter and step progress
454
+ if (hadFailure) {
455
+ this.consecutiveFailures++;
456
+ this.logger.info("Fusion", `Consecutive failures: ${this.consecutiveFailures}` +
457
+ (this.replanThreshold > 0 &&
458
+ this.consecutiveFailures >= this.replanThreshold
459
+ ? ` — threshold reached`
460
+ : ""));
461
+ // Inline reflection on first failure if enabled
462
+ if ((this.reflectionMode === "inline" ||
463
+ this.reflectionMode === "both") &&
464
+ this.inlineReflectionsDone === 0) {
465
+ await this.reflectInline(iteration);
466
+ }
467
+ }
468
+ else {
469
+ this.consecutiveFailures = 0;
470
+ // Advance completed steps
471
+ if (parsed.currentStep && this.hasPlan) {
472
+ this.completedSteps = Math.max(this.completedSteps, Math.min(parsed.currentStep - 1, this.currentPlan.length));
473
+ }
474
+ else if (this.hasPlan) {
475
+ this.completedSteps = Math.min(this.completedSteps + 1, this.currentPlan.length);
476
+ }
477
+ if (this.hasPlan) {
478
+ this.logger.info("Progress", `Step ${this.completedSteps}/${this.currentPlan.length} completed`);
479
+ }
480
+ }
481
+ // Save checkpoint
482
+ if (this.checkpointingEnabled) {
483
+ this.saveCheckpoint("active");
484
+ }
485
+ // Inline reflection at regular intervals
486
+ if ((this.reflectionMode === "inline" ||
487
+ this.reflectionMode === "both") &&
488
+ (iteration + 1) % this.reflectionInterval === 0) {
489
+ await this.reflectInline(iteration);
490
+ }
491
+ continue;
492
+ }
493
+ // ── Final answer ────────────────────────────────────────────────
494
+ if (parsed.answer) {
495
+ if (isTruncated) {
496
+ consecutiveEmptyIterations = 0;
497
+ if (parsed.thought) {
498
+ this.logger.info("Thought", parsed.thought);
499
+ for (const h of this.hooks)
500
+ h.onThought?.(parsed.thought);
501
+ }
502
+ if (iteration === this.maxIterations - 1) {
503
+ return (parsed.answer +
504
+ "\n\n[Note: Response may be incomplete due to output length constraints.]");
505
+ }
506
+ this.logger.info("Fusion", "Answer truncated — continuing in next iteration.");
507
+ continue;
508
+ }
509
+ if (parsed.thought) {
510
+ this.logger.info("Thought", parsed.thought);
511
+ for (const h of this.hooks)
512
+ h.onThought?.(parsed.thought);
513
+ }
514
+ this.logger.info("Fusion", "Task complete — returning final answer.");
515
+ if (this.checkpointingEnabled) {
516
+ this.saveCheckpoint("completed");
517
+ }
518
+ return parsed.answer;
519
+ }
520
+ // ── Plan creation (if LLM generates plan mid-execution) ─────────
521
+ if (!this.hasPlan &&
522
+ parsed.plan &&
523
+ Array.isArray(parsed.plan) &&
524
+ parsed.plan.length > 0) {
525
+ consecutiveEmptyIterations = 0;
526
+ this.currentPlan = parsed.plan.slice(0, this.maxPlanSteps);
527
+ this.hasPlan = true;
528
+ this.logger.info("Plan", `Created ${this.currentPlan.length}-step plan mid-execution:`);
529
+ for (let i = 0; i < this.currentPlan.length; i++) {
530
+ this.logger.info("Plan", ` ${i + 1}. ${this.currentPlan[i]}`);
531
+ }
532
+ for (const h of this.hooks)
533
+ h.onPlanCreated?.(this.currentPlan);
534
+ if (parsed.thought) {
535
+ this.logger.info("Thought", parsed.thought);
536
+ for (const h of this.hooks)
537
+ h.onThought?.(parsed.thought);
538
+ }
539
+ continue;
540
+ }
541
+ // ── Plan revision ───────────────────────────────────────────────
542
+ if (parsed.revised_plan &&
543
+ Array.isArray(parsed.revised_plan) &&
544
+ parsed.revised_plan.length > 0) {
545
+ consecutiveEmptyIterations = 0;
546
+ this.currentPlan = parsed.revised_plan.slice(0, this.maxPlanSteps);
547
+ this.completedSteps = 0;
548
+ this.consecutiveFailures = 0;
549
+ this.logger.info("Plan", `Revised — ${this.currentPlan.length} steps remaining:`);
550
+ for (let i = 0; i < this.currentPlan.length; i++) {
551
+ this.logger.info("Plan", ` ${i + 1}. ${this.currentPlan[i]}`);
552
+ }
553
+ for (const h of this.hooks)
554
+ h.onPlanRevised?.(this.currentPlan);
555
+ if (parsed.thought) {
556
+ this.logger.info("Thought", parsed.thought);
557
+ for (const h of this.hooks)
558
+ h.onThought?.(parsed.thought);
559
+ }
560
+ continue;
561
+ }
562
+ // ── Thought-only iteration ──────────────────────────────────────
563
+ if (parsed.thought) {
564
+ consecutiveEmptyIterations++;
565
+ this.logger.info("Thought", parsed.thought);
566
+ for (const h of this.hooks)
567
+ h.onThought?.(parsed.thought);
568
+ if (consecutiveEmptyIterations >= EMPTY_ITERATION_LIMIT) {
569
+ const stuckMsg = "I apologize, but I'm having difficulty making progress on your request. " +
570
+ "Please try rephrasing or breaking it down into smaller, more specific steps.";
571
+ const stuckAssistantMessage = message_1.Message.assistant(stuckMsg);
572
+ this.contextManager.addMessage(stuckAssistantMessage.toDict());
573
+ return stuckMsg;
574
+ }
575
+ continue;
576
+ }
577
+ // ── Empty response ──────────────────────────────────────────────
578
+ consecutiveEmptyIterations++;
579
+ if (consecutiveEmptyIterations >= EMPTY_ITERATION_LIMIT) {
580
+ const stuckMsg = "I apologize, but I'm having difficulty making progress on your request. " +
581
+ "Please try rephrasing or breaking it down into smaller, more specific steps.";
582
+ const stuckAssistantMessage = message_1.Message.assistant(stuckMsg);
583
+ this.contextManager.addMessage(stuckAssistantMessage.toDict());
584
+ return stuckMsg;
585
+ }
586
+ }
587
+ // ── Max iterations reached ────────────────────────────────────────
588
+ const timeoutMsg = `I apologize, but I was unable to complete the task within ${this.maxIterations} iterations. ` +
589
+ `Please try breaking your request into smaller steps.`;
590
+ const timeoutAssistantMessage = message_1.Message.assistant(timeoutMsg);
591
+ this.contextManager.addMessage(timeoutAssistantMessage.toDict());
592
+ return timeoutMsg;
593
+ }
594
+ // ─── Phase 4: Reflection ────────────────────────────────────────────
595
+ /**
596
+ * Run the configured reflection strategy.
597
+ */
598
+ async runReflection(input, answer) {
599
+ if (this.reflectionMode === "off")
600
+ return;
601
+ // ── Post-hoc reflection ──────────────────────────────────────────
602
+ if (this.reflectionMode === "post-hoc" ||
603
+ this.reflectionMode === "both") {
604
+ await this.reflectPostHoc(input, answer);
605
+ }
606
+ // Inline reflection is done during the loop — nothing extra here
607
+ }
608
+ /**
609
+ * Inline reflection: pause execution and ask the LLM to self-check
610
+ * its progress. Results are logged and injected into context.
611
+ */
612
+ async reflectInline(iteration) {
613
+ this.inlineReflectionsDone++;
614
+ this.logger.info("Reflection", `Inline reflection #${this.inlineReflectionsDone} at iteration ${iteration + 1}`);
615
+ const reflectionMsg = message_1.Message.user(response_schema_1.INLINE_REFLECTION_PROMPT);
616
+ this.contextManager.addMessage(reflectionMsg.toDict());
617
+ // Do NOT call the LLM here — the reflection prompt is injected as a
618
+ // user message and will be processed in the next iteration of the
619
+ // main loop. This keeps the reflection lightweight and naturally
620
+ // integrated into the flow.
621
+ // Save checkpoint after inline reflection
622
+ if (this.checkpointingEnabled) {
623
+ this.saveCheckpoint("active");
624
+ }
625
+ }
626
+ /**
627
+ * Post-hoc reflection: after execution completes, run the
628
+ * ReflectionAgent to review the full session.
629
+ */
630
+ async reflectPostHoc(input, answer) {
631
+ if (!this.notebook)
632
+ return;
633
+ this.logger.info("Reflection", "Starting post-hoc reflection...");
634
+ try {
635
+ const reflector = new reflection_agent_1.ReflectionAgent({
636
+ llm: this.llm,
637
+ notebook: this.notebook,
638
+ });
639
+ const contextMessages = this.contextManager.getContextMessages();
640
+ const entries = await reflector.reflect({
641
+ userQuery: input,
642
+ finalAnswer: answer,
643
+ conversation: contextMessages,
644
+ sessionId: this.sessionManager?.getSessionId() ?? "unknown",
645
+ });
646
+ if (entries.length > 0) {
647
+ this.logger.info("Reflection", `Post-hoc complete — ${entries.length} finding(s) written to notebook.`);
648
+ for (const entry of entries) {
649
+ this.logger.info("Reflection", ` [${entry.category}] ${entry.description}`);
650
+ }
651
+ }
652
+ else {
653
+ this.logger.info("Reflection", "Post-hoc complete — no issues found.");
654
+ }
655
+ }
656
+ catch (err) {
657
+ this.logger.warn("Reflection", `Post-hoc reflection failed: ${err instanceof Error ? err.message : String(err)}`);
658
+ }
659
+ }
660
+ // ─── System Prompt Management ───────────────────────────────────────
661
+ /**
662
+ * Rebuild the system prompt to include plan progress and replan hints.
663
+ *
664
+ * Called each iteration so the LLM always sees the up-to-date state.
665
+ */
666
+ rebuildContextWithPlan() {
667
+ let prompt = this.buildSystemPrompt();
668
+ // Plan context — injected when a plan exists
669
+ if (this.hasPlan && this.currentPlan.length > 0) {
670
+ prompt += `\n\n=== Current Plan (${this.completedSteps}/${this.currentPlan.length} completed) ===\n`;
671
+ for (let i = 0; i < this.currentPlan.length; i++) {
672
+ const marker = i < this.completedSteps
673
+ ? "✅"
674
+ : i === this.completedSteps
675
+ ? "➡️"
676
+ : " ";
677
+ prompt += ` ${marker} ${i + 1}. ${this.currentPlan[i]}\n`;
678
+ }
679
+ prompt +=
680
+ `\nWork through the plan step by step. ` +
681
+ `When you complete a step, move to the next one. ` +
682
+ `If you need to revise remaining steps, output a "revised_plan".`;
683
+ }
684
+ // Replan hint — injected when consecutive failures are detected
685
+ const replanHint = this.computeReplanHint();
686
+ if (replanHint) {
687
+ prompt += `\n\n${replanHint}`;
688
+ }
689
+ this.contextManager.setSystemMessage(prompt);
690
+ }
691
+ /**
692
+ * Compute a replan hint based on consecutive failure count.
693
+ */
694
+ computeReplanHint() {
695
+ if (this.replanThreshold <= 0)
696
+ return undefined;
697
+ if (!this.hasPlan)
698
+ return undefined;
699
+ if (this.consecutiveFailures >= this.replanThreshold) {
700
+ return (`⚠️ REPLAN SUGGESTED: Multiple consecutive tool failures detected (${this.consecutiveFailures}).\n` +
701
+ `The current approach may be fundamentally wrong.\n` +
702
+ `Please consider whether your plan needs revision. If so, output a "revised_plan" ` +
703
+ `with a new strategy for the remaining steps. Only the REMAINING steps — ` +
704
+ `do NOT re-list already completed steps.`);
705
+ }
706
+ return undefined;
707
+ }
708
+ // ─── Session Persistence ────────────────────────────────────────────
709
+ /**
710
+ * Agent type identifier for session metadata.
711
+ */
712
+ getAgentType() {
713
+ return "fusion";
714
+ }
715
+ /**
716
+ * Include fusion-specific state in session checkpoints.
717
+ */
718
+ buildBaseSessionState(status) {
719
+ const base = super.buildBaseSessionState(status);
720
+ return {
721
+ ...base,
722
+ planState: undefined, // Clear PlanSolve state — fusion uses its own
723
+ fusionState: {
724
+ complexity: this.complexity,
725
+ routed: this.routed,
726
+ currentPlan: this.currentPlan,
727
+ hasPlan: this.hasPlan,
728
+ completedSteps: this.completedSteps,
729
+ consecutiveFailures: this.consecutiveFailures,
730
+ reflectionEnabled: this.reflectionMode !== "off",
731
+ inlineReflectionsDone: this.inlineReflectionsDone,
732
+ },
733
+ };
734
+ }
735
+ /**
736
+ * Restore fusion state from a saved session.
737
+ */
738
+ loadAndRestoreSession(sessionId) {
739
+ const state = super.loadAndRestoreSession(sessionId);
740
+ if (state.fusionState) {
741
+ const fs = state.fusionState;
742
+ this.complexity = fs.complexity;
743
+ this.routed = fs.routed;
744
+ this.currentPlan = fs.currentPlan;
745
+ this.hasPlan = fs.hasPlan;
746
+ this.completedSteps = fs.completedSteps;
747
+ this.consecutiveFailures = fs.consecutiveFailures;
748
+ this.inlineReflectionsDone = fs.inlineReflectionsDone;
749
+ }
750
+ return state;
751
+ }
752
+ // ─── Resume ─────────────────────────────────────────────────────────
753
+ /**
754
+ * Resume a previously interrupted session.
755
+ *
756
+ * Restores messages, system prompt, plan state, and fusion metadata
757
+ * so the agent can continue from where it left off.
758
+ *
759
+ * @param sessionId The session ID to resume.
760
+ * @param input New user input to continue the conversation.
761
+ */
762
+ async resume(sessionId, input) {
763
+ this.loadAndRestoreSession(sessionId);
764
+ this._skipStateReset = true;
765
+ return this.run(input);
766
+ }
767
+ }
768
+ exports.FusionAgent = FusionAgent;
769
+ //# sourceMappingURL=fusion-agent.js.map