attocode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +164 -0
  4. package/dist/src/adapters.d.ts +83 -0
  5. package/dist/src/adapters.d.ts.map +1 -0
  6. package/dist/src/adapters.js +221 -0
  7. package/dist/src/adapters.js.map +1 -0
  8. package/dist/src/agent-tools/index.d.ts +7 -0
  9. package/dist/src/agent-tools/index.d.ts.map +1 -0
  10. package/dist/src/agent-tools/index.js +8 -0
  11. package/dist/src/agent-tools/index.js.map +1 -0
  12. package/dist/src/agent-tools/lsp-file-tools.d.ts +33 -0
  13. package/dist/src/agent-tools/lsp-file-tools.d.ts.map +1 -0
  14. package/dist/src/agent-tools/lsp-file-tools.js +200 -0
  15. package/dist/src/agent-tools/lsp-file-tools.js.map +1 -0
  16. package/dist/src/agent.d.ts +667 -0
  17. package/dist/src/agent.d.ts.map +1 -0
  18. package/dist/src/agent.js +2824 -0
  19. package/dist/src/agent.js.map +1 -0
  20. package/dist/src/cli.d.ts +36 -0
  21. package/dist/src/cli.d.ts.map +1 -0
  22. package/dist/src/cli.js +176 -0
  23. package/dist/src/cli.js.map +1 -0
  24. package/dist/src/commands/handler.d.ts +22 -0
  25. package/dist/src/commands/handler.d.ts.map +1 -0
  26. package/dist/src/commands/handler.js +1320 -0
  27. package/dist/src/commands/handler.js.map +1 -0
  28. package/dist/src/commands/init.d.ts +7 -0
  29. package/dist/src/commands/init.d.ts.map +1 -0
  30. package/dist/src/commands/init.js +153 -0
  31. package/dist/src/commands/init.js.map +1 -0
  32. package/dist/src/commands/types.d.ts +70 -0
  33. package/dist/src/commands/types.d.ts.map +1 -0
  34. package/dist/src/commands/types.js +8 -0
  35. package/dist/src/commands/types.js.map +1 -0
  36. package/dist/src/config.d.ts +22 -0
  37. package/dist/src/config.d.ts.map +1 -0
  38. package/dist/src/config.js +25 -0
  39. package/dist/src/config.js.map +1 -0
  40. package/dist/src/core/index.d.ts +32 -0
  41. package/dist/src/core/index.d.ts.map +1 -0
  42. package/dist/src/core/index.js +35 -0
  43. package/dist/src/core/index.js.map +1 -0
  44. package/dist/src/core/process-handlers.d.ts +43 -0
  45. package/dist/src/core/process-handlers.d.ts.map +1 -0
  46. package/dist/src/core/process-handlers.js +117 -0
  47. package/dist/src/core/process-handlers.js.map +1 -0
  48. package/dist/src/core/protocol/bridge.d.ts +117 -0
  49. package/dist/src/core/protocol/bridge.d.ts.map +1 -0
  50. package/dist/src/core/protocol/bridge.js +149 -0
  51. package/dist/src/core/protocol/bridge.js.map +1 -0
  52. package/dist/src/core/protocol/index.d.ts +8 -0
  53. package/dist/src/core/protocol/index.d.ts.map +1 -0
  54. package/dist/src/core/protocol/index.js +8 -0
  55. package/dist/src/core/protocol/index.js.map +1 -0
  56. package/dist/src/core/protocol/types.d.ts +539 -0
  57. package/dist/src/core/protocol/types.d.ts.map +1 -0
  58. package/dist/src/core/protocol/types.js +149 -0
  59. package/dist/src/core/protocol/types.js.map +1 -0
  60. package/dist/src/core/queues/atomic-counter.d.ts +36 -0
  61. package/dist/src/core/queues/atomic-counter.d.ts.map +1 -0
  62. package/dist/src/core/queues/atomic-counter.js +46 -0
  63. package/dist/src/core/queues/atomic-counter.js.map +1 -0
  64. package/dist/src/core/queues/event-queue.d.ts +126 -0
  65. package/dist/src/core/queues/event-queue.d.ts.map +1 -0
  66. package/dist/src/core/queues/event-queue.js +208 -0
  67. package/dist/src/core/queues/event-queue.js.map +1 -0
  68. package/dist/src/core/queues/index.d.ts +12 -0
  69. package/dist/src/core/queues/index.d.ts.map +1 -0
  70. package/dist/src/core/queues/index.js +15 -0
  71. package/dist/src/core/queues/index.js.map +1 -0
  72. package/dist/src/core/queues/submission-queue.d.ts +116 -0
  73. package/dist/src/core/queues/submission-queue.d.ts.map +1 -0
  74. package/dist/src/core/queues/submission-queue.js +236 -0
  75. package/dist/src/core/queues/submission-queue.js.map +1 -0
  76. package/dist/src/costs/index.d.ts +22 -0
  77. package/dist/src/costs/index.d.ts.map +1 -0
  78. package/dist/src/costs/index.js +22 -0
  79. package/dist/src/costs/index.js.map +1 -0
  80. package/dist/src/costs/model-registry.d.ts +80 -0
  81. package/dist/src/costs/model-registry.d.ts.map +1 -0
  82. package/dist/src/costs/model-registry.js +237 -0
  83. package/dist/src/costs/model-registry.js.map +1 -0
  84. package/dist/src/costs/types.d.ts +50 -0
  85. package/dist/src/costs/types.d.ts.map +1 -0
  86. package/dist/src/costs/types.js +2 -0
  87. package/dist/src/costs/types.js.map +1 -0
  88. package/dist/src/defaults.d.ts +114 -0
  89. package/dist/src/defaults.d.ts.map +1 -0
  90. package/dist/src/defaults.js +457 -0
  91. package/dist/src/defaults.js.map +1 -0
  92. package/dist/src/first-run.d.ts +35 -0
  93. package/dist/src/first-run.d.ts.map +1 -0
  94. package/dist/src/first-run.js +94 -0
  95. package/dist/src/first-run.js.map +1 -0
  96. package/dist/src/hello.d.ts +2 -0
  97. package/dist/src/hello.d.ts.map +1 -0
  98. package/dist/src/hello.js +4 -0
  99. package/dist/src/hello.js.map +1 -0
  100. package/dist/src/integrations/agent-registry.d.ts +160 -0
  101. package/dist/src/integrations/agent-registry.d.ts.map +1 -0
  102. package/dist/src/integrations/agent-registry.js +446 -0
  103. package/dist/src/integrations/agent-registry.js.map +1 -0
  104. package/dist/src/integrations/auto-compaction.d.ts +177 -0
  105. package/dist/src/integrations/auto-compaction.d.ts.map +1 -0
  106. package/dist/src/integrations/auto-compaction.js +428 -0
  107. package/dist/src/integrations/auto-compaction.js.map +1 -0
  108. package/dist/src/integrations/cancellation.d.ts +162 -0
  109. package/dist/src/integrations/cancellation.d.ts.map +1 -0
  110. package/dist/src/integrations/cancellation.js +339 -0
  111. package/dist/src/integrations/cancellation.js.map +1 -0
  112. package/dist/src/integrations/codebase-context.d.ts +319 -0
  113. package/dist/src/integrations/codebase-context.d.ts.map +1 -0
  114. package/dist/src/integrations/codebase-context.js +816 -0
  115. package/dist/src/integrations/codebase-context.js.map +1 -0
  116. package/dist/src/integrations/compaction.d.ts +192 -0
  117. package/dist/src/integrations/compaction.d.ts.map +1 -0
  118. package/dist/src/integrations/compaction.js +376 -0
  119. package/dist/src/integrations/compaction.js.map +1 -0
  120. package/dist/src/integrations/context-engineering.d.ts +246 -0
  121. package/dist/src/integrations/context-engineering.d.ts.map +1 -0
  122. package/dist/src/integrations/context-engineering.js +394 -0
  123. package/dist/src/integrations/context-engineering.js.map +1 -0
  124. package/dist/src/integrations/diff-utils.d.ts +105 -0
  125. package/dist/src/integrations/diff-utils.d.ts.map +1 -0
  126. package/dist/src/integrations/diff-utils.js +497 -0
  127. package/dist/src/integrations/diff-utils.js.map +1 -0
  128. package/dist/src/integrations/economics.d.ts +192 -0
  129. package/dist/src/integrations/economics.d.ts.map +1 -0
  130. package/dist/src/integrations/economics.js +431 -0
  131. package/dist/src/integrations/economics.js.map +1 -0
  132. package/dist/src/integrations/execution-policy.d.ts +189 -0
  133. package/dist/src/integrations/execution-policy.d.ts.map +1 -0
  134. package/dist/src/integrations/execution-policy.js +352 -0
  135. package/dist/src/integrations/execution-policy.js.map +1 -0
  136. package/dist/src/integrations/file-change-tracker.d.ts +161 -0
  137. package/dist/src/integrations/file-change-tracker.d.ts.map +1 -0
  138. package/dist/src/integrations/file-change-tracker.js +520 -0
  139. package/dist/src/integrations/file-change-tracker.js.map +1 -0
  140. package/dist/src/integrations/hierarchical-config.d.ts +212 -0
  141. package/dist/src/integrations/hierarchical-config.d.ts.map +1 -0
  142. package/dist/src/integrations/hierarchical-config.js +484 -0
  143. package/dist/src/integrations/hierarchical-config.js.map +1 -0
  144. package/dist/src/integrations/hooks.d.ts +114 -0
  145. package/dist/src/integrations/hooks.d.ts.map +1 -0
  146. package/dist/src/integrations/hooks.js +326 -0
  147. package/dist/src/integrations/hooks.js.map +1 -0
  148. package/dist/src/integrations/ignore.d.ts +143 -0
  149. package/dist/src/integrations/ignore.d.ts.map +1 -0
  150. package/dist/src/integrations/ignore.js +417 -0
  151. package/dist/src/integrations/ignore.js.map +1 -0
  152. package/dist/src/integrations/image-renderer.d.ts +119 -0
  153. package/dist/src/integrations/image-renderer.d.ts.map +1 -0
  154. package/dist/src/integrations/image-renderer.js +306 -0
  155. package/dist/src/integrations/image-renderer.js.map +1 -0
  156. package/dist/src/integrations/index.d.ts +42 -0
  157. package/dist/src/integrations/index.d.ts.map +1 -0
  158. package/dist/src/integrations/index.js +73 -0
  159. package/dist/src/integrations/index.js.map +1 -0
  160. package/dist/src/integrations/lsp.d.ts +196 -0
  161. package/dist/src/integrations/lsp.d.ts.map +1 -0
  162. package/dist/src/integrations/lsp.js +582 -0
  163. package/dist/src/integrations/lsp.js.map +1 -0
  164. package/dist/src/integrations/mcp-client.d.ts +270 -0
  165. package/dist/src/integrations/mcp-client.d.ts.map +1 -0
  166. package/dist/src/integrations/mcp-client.js +698 -0
  167. package/dist/src/integrations/mcp-client.js.map +1 -0
  168. package/dist/src/integrations/mcp-tool-search.d.ts +77 -0
  169. package/dist/src/integrations/mcp-tool-search.d.ts.map +1 -0
  170. package/dist/src/integrations/mcp-tool-search.js +220 -0
  171. package/dist/src/integrations/mcp-tool-search.js.map +1 -0
  172. package/dist/src/integrations/memory.d.ts +108 -0
  173. package/dist/src/integrations/memory.d.ts.map +1 -0
  174. package/dist/src/integrations/memory.js +288 -0
  175. package/dist/src/integrations/memory.js.map +1 -0
  176. package/dist/src/integrations/multi-agent.d.ts +150 -0
  177. package/dist/src/integrations/multi-agent.d.ts.map +1 -0
  178. package/dist/src/integrations/multi-agent.js +306 -0
  179. package/dist/src/integrations/multi-agent.js.map +1 -0
  180. package/dist/src/integrations/observability.d.ts +162 -0
  181. package/dist/src/integrations/observability.d.ts.map +1 -0
  182. package/dist/src/integrations/observability.js +406 -0
  183. package/dist/src/integrations/observability.js.map +1 -0
  184. package/dist/src/integrations/openrouter-pricing.d.ts +42 -0
  185. package/dist/src/integrations/openrouter-pricing.d.ts.map +1 -0
  186. package/dist/src/integrations/openrouter-pricing.js +124 -0
  187. package/dist/src/integrations/openrouter-pricing.js.map +1 -0
  188. package/dist/src/integrations/pending-plan.d.ts +171 -0
  189. package/dist/src/integrations/pending-plan.d.ts.map +1 -0
  190. package/dist/src/integrations/pending-plan.js +244 -0
  191. package/dist/src/integrations/pending-plan.js.map +1 -0
  192. package/dist/src/integrations/persistence.d.ts +48 -0
  193. package/dist/src/integrations/persistence.d.ts.map +1 -0
  194. package/dist/src/integrations/persistence.js +196 -0
  195. package/dist/src/integrations/persistence.js.map +1 -0
  196. package/dist/src/integrations/planning.d.ts +96 -0
  197. package/dist/src/integrations/planning.d.ts.map +1 -0
  198. package/dist/src/integrations/planning.js +338 -0
  199. package/dist/src/integrations/planning.js.map +1 -0
  200. package/dist/src/integrations/pty-shell.d.ts +169 -0
  201. package/dist/src/integrations/pty-shell.d.ts.map +1 -0
  202. package/dist/src/integrations/pty-shell.js +367 -0
  203. package/dist/src/integrations/pty-shell.js.map +1 -0
  204. package/dist/src/integrations/react.d.ts +139 -0
  205. package/dist/src/integrations/react.d.ts.map +1 -0
  206. package/dist/src/integrations/react.js +273 -0
  207. package/dist/src/integrations/react.js.map +1 -0
  208. package/dist/src/integrations/resources.d.ts +177 -0
  209. package/dist/src/integrations/resources.d.ts.map +1 -0
  210. package/dist/src/integrations/resources.js +311 -0
  211. package/dist/src/integrations/resources.js.map +1 -0
  212. package/dist/src/integrations/result-synthesizer.d.ts +389 -0
  213. package/dist/src/integrations/result-synthesizer.d.ts.map +1 -0
  214. package/dist/src/integrations/result-synthesizer.js +951 -0
  215. package/dist/src/integrations/result-synthesizer.js.map +1 -0
  216. package/dist/src/integrations/routing.d.ts +117 -0
  217. package/dist/src/integrations/routing.d.ts.map +1 -0
  218. package/dist/src/integrations/routing.js +347 -0
  219. package/dist/src/integrations/routing.js.map +1 -0
  220. package/dist/src/integrations/rules.d.ts +131 -0
  221. package/dist/src/integrations/rules.d.ts.map +1 -0
  222. package/dist/src/integrations/rules.js +284 -0
  223. package/dist/src/integrations/rules.js.map +1 -0
  224. package/dist/src/integrations/safety.d.ts +142 -0
  225. package/dist/src/integrations/safety.d.ts.map +1 -0
  226. package/dist/src/integrations/safety.js +342 -0
  227. package/dist/src/integrations/safety.js.map +1 -0
  228. package/dist/src/integrations/sandbox/basic.d.ts +74 -0
  229. package/dist/src/integrations/sandbox/basic.d.ts.map +1 -0
  230. package/dist/src/integrations/sandbox/basic.js +310 -0
  231. package/dist/src/integrations/sandbox/basic.js.map +1 -0
  232. package/dist/src/integrations/sandbox/docker.d.ts +94 -0
  233. package/dist/src/integrations/sandbox/docker.d.ts.map +1 -0
  234. package/dist/src/integrations/sandbox/docker.js +293 -0
  235. package/dist/src/integrations/sandbox/docker.js.map +1 -0
  236. package/dist/src/integrations/sandbox/index.d.ts +182 -0
  237. package/dist/src/integrations/sandbox/index.d.ts.map +1 -0
  238. package/dist/src/integrations/sandbox/index.js +382 -0
  239. package/dist/src/integrations/sandbox/index.js.map +1 -0
  240. package/dist/src/integrations/sandbox/landlock.d.ts +59 -0
  241. package/dist/src/integrations/sandbox/landlock.d.ts.map +1 -0
  242. package/dist/src/integrations/sandbox/landlock.js +326 -0
  243. package/dist/src/integrations/sandbox/landlock.js.map +1 -0
  244. package/dist/src/integrations/sandbox/seatbelt.d.ts +68 -0
  245. package/dist/src/integrations/sandbox/seatbelt.d.ts.map +1 -0
  246. package/dist/src/integrations/sandbox/seatbelt.js +298 -0
  247. package/dist/src/integrations/sandbox/seatbelt.js.map +1 -0
  248. package/dist/src/integrations/semantic-cache.d.ts +178 -0
  249. package/dist/src/integrations/semantic-cache.d.ts.map +1 -0
  250. package/dist/src/integrations/semantic-cache.js +372 -0
  251. package/dist/src/integrations/semantic-cache.js.map +1 -0
  252. package/dist/src/integrations/session-store.d.ts +183 -0
  253. package/dist/src/integrations/session-store.d.ts.map +1 -0
  254. package/dist/src/integrations/session-store.js +345 -0
  255. package/dist/src/integrations/session-store.js.map +1 -0
  256. package/dist/src/integrations/shared-blackboard.d.ts +403 -0
  257. package/dist/src/integrations/shared-blackboard.d.ts.map +1 -0
  258. package/dist/src/integrations/shared-blackboard.js +710 -0
  259. package/dist/src/integrations/shared-blackboard.js.map +1 -0
  260. package/dist/src/integrations/skills.d.ts +171 -0
  261. package/dist/src/integrations/skills.d.ts.map +1 -0
  262. package/dist/src/integrations/skills.js +403 -0
  263. package/dist/src/integrations/skills.js.map +1 -0
  264. package/dist/src/integrations/smart-decomposer.d.ts +322 -0
  265. package/dist/src/integrations/smart-decomposer.d.ts.map +1 -0
  266. package/dist/src/integrations/smart-decomposer.js +856 -0
  267. package/dist/src/integrations/smart-decomposer.js.map +1 -0
  268. package/dist/src/integrations/sourcegraph.d.ts +169 -0
  269. package/dist/src/integrations/sourcegraph.d.ts.map +1 -0
  270. package/dist/src/integrations/sourcegraph.js +379 -0
  271. package/dist/src/integrations/sourcegraph.js.map +1 -0
  272. package/dist/src/integrations/sqlite-store.d.ts +518 -0
  273. package/dist/src/integrations/sqlite-store.d.ts.map +1 -0
  274. package/dist/src/integrations/sqlite-store.js +1423 -0
  275. package/dist/src/integrations/sqlite-store.js.map +1 -0
  276. package/dist/src/integrations/streaming.d.ts +102 -0
  277. package/dist/src/integrations/streaming.d.ts.map +1 -0
  278. package/dist/src/integrations/streaming.js +362 -0
  279. package/dist/src/integrations/streaming.js.map +1 -0
  280. package/dist/src/integrations/thread-manager.d.ts +199 -0
  281. package/dist/src/integrations/thread-manager.d.ts.map +1 -0
  282. package/dist/src/integrations/thread-manager.js +357 -0
  283. package/dist/src/integrations/thread-manager.js.map +1 -0
  284. package/dist/src/main.d.ts +26 -0
  285. package/dist/src/main.d.ts.map +1 -0
  286. package/dist/src/main.js +170 -0
  287. package/dist/src/main.js.map +1 -0
  288. package/dist/src/modes/index.d.ts +10 -0
  289. package/dist/src/modes/index.d.ts.map +1 -0
  290. package/dist/src/modes/index.js +10 -0
  291. package/dist/src/modes/index.js.map +1 -0
  292. package/dist/src/modes/repl.d.ts +19 -0
  293. package/dist/src/modes/repl.d.ts.map +1 -0
  294. package/dist/src/modes/repl.js +393 -0
  295. package/dist/src/modes/repl.js.map +1 -0
  296. package/dist/src/modes/tui.d.ts +29 -0
  297. package/dist/src/modes/tui.d.ts.map +1 -0
  298. package/dist/src/modes/tui.js +272 -0
  299. package/dist/src/modes/tui.js.map +1 -0
  300. package/dist/src/modes.d.ts +179 -0
  301. package/dist/src/modes.d.ts.map +1 -0
  302. package/dist/src/modes.js +385 -0
  303. package/dist/src/modes.js.map +1 -0
  304. package/dist/src/observability/tracer.d.ts +111 -0
  305. package/dist/src/observability/tracer.d.ts.map +1 -0
  306. package/dist/src/observability/tracer.js +300 -0
  307. package/dist/src/observability/tracer.js.map +1 -0
  308. package/dist/src/observability/types.d.ts +271 -0
  309. package/dist/src/observability/types.d.ts.map +1 -0
  310. package/dist/src/observability/types.js +24 -0
  311. package/dist/src/observability/types.js.map +1 -0
  312. package/dist/src/paths.d.ts +101 -0
  313. package/dist/src/paths.d.ts.map +1 -0
  314. package/dist/src/paths.js +148 -0
  315. package/dist/src/paths.js.map +1 -0
  316. package/dist/src/persistence/index.d.ts +38 -0
  317. package/dist/src/persistence/index.d.ts.map +1 -0
  318. package/dist/src/persistence/index.js +48 -0
  319. package/dist/src/persistence/index.js.map +1 -0
  320. package/dist/src/persistence/migrator.d.ts +135 -0
  321. package/dist/src/persistence/migrator.d.ts.map +1 -0
  322. package/dist/src/persistence/migrator.js +303 -0
  323. package/dist/src/persistence/migrator.js.map +1 -0
  324. package/dist/src/persistence/schema.d.ts +101 -0
  325. package/dist/src/persistence/schema.d.ts.map +1 -0
  326. package/dist/src/persistence/schema.js +395 -0
  327. package/dist/src/persistence/schema.js.map +1 -0
  328. package/dist/src/providers/adapters/anthropic.d.ts +20 -0
  329. package/dist/src/providers/adapters/anthropic.d.ts.map +1 -0
  330. package/dist/src/providers/adapters/anthropic.js +124 -0
  331. package/dist/src/providers/adapters/anthropic.js.map +1 -0
  332. package/dist/src/providers/adapters/mock.d.ts +25 -0
  333. package/dist/src/providers/adapters/mock.d.ts.map +1 -0
  334. package/dist/src/providers/adapters/mock.js +133 -0
  335. package/dist/src/providers/adapters/mock.js.map +1 -0
  336. package/dist/src/providers/adapters/openai.d.ts +21 -0
  337. package/dist/src/providers/adapters/openai.d.ts.map +1 -0
  338. package/dist/src/providers/adapters/openai.js +126 -0
  339. package/dist/src/providers/adapters/openai.js.map +1 -0
  340. package/dist/src/providers/adapters/openrouter.d.ts +49 -0
  341. package/dist/src/providers/adapters/openrouter.d.ts.map +1 -0
  342. package/dist/src/providers/adapters/openrouter.js +363 -0
  343. package/dist/src/providers/adapters/openrouter.js.map +1 -0
  344. package/dist/src/providers/provider.d.ts +54 -0
  345. package/dist/src/providers/provider.d.ts.map +1 -0
  346. package/dist/src/providers/provider.js +111 -0
  347. package/dist/src/providers/provider.js.map +1 -0
  348. package/dist/src/providers/resilient-fetch.d.ts +99 -0
  349. package/dist/src/providers/resilient-fetch.d.ts.map +1 -0
  350. package/dist/src/providers/resilient-fetch.js +208 -0
  351. package/dist/src/providers/resilient-fetch.js.map +1 -0
  352. package/dist/src/providers/types.d.ts +227 -0
  353. package/dist/src/providers/types.d.ts.map +1 -0
  354. package/dist/src/providers/types.js +24 -0
  355. package/dist/src/providers/types.js.map +1 -0
  356. package/dist/src/session-picker.d.ts +28 -0
  357. package/dist/src/session-picker.d.ts.map +1 -0
  358. package/dist/src/session-picker.js +256 -0
  359. package/dist/src/session-picker.js.map +1 -0
  360. package/dist/src/test-sqlite.d.ts +2 -0
  361. package/dist/src/test-sqlite.d.ts.map +1 -0
  362. package/dist/src/test-sqlite.js +114 -0
  363. package/dist/src/test-sqlite.js.map +1 -0
  364. package/dist/src/tools/agent.d.ts +44 -0
  365. package/dist/src/tools/agent.d.ts.map +1 -0
  366. package/dist/src/tools/agent.js +110 -0
  367. package/dist/src/tools/agent.js.map +1 -0
  368. package/dist/src/tools/bash.d.ts +52 -0
  369. package/dist/src/tools/bash.d.ts.map +1 -0
  370. package/dist/src/tools/bash.js +141 -0
  371. package/dist/src/tools/bash.js.map +1 -0
  372. package/dist/src/tools/file.d.ts +47 -0
  373. package/dist/src/tools/file.d.ts.map +1 -0
  374. package/dist/src/tools/file.js +263 -0
  375. package/dist/src/tools/file.js.map +1 -0
  376. package/dist/src/tools/permission.d.ts +43 -0
  377. package/dist/src/tools/permission.d.ts.map +1 -0
  378. package/dist/src/tools/permission.js +216 -0
  379. package/dist/src/tools/permission.js.map +1 -0
  380. package/dist/src/tools/registry.d.ts +63 -0
  381. package/dist/src/tools/registry.d.ts.map +1 -0
  382. package/dist/src/tools/registry.js +250 -0
  383. package/dist/src/tools/registry.js.map +1 -0
  384. package/dist/src/tools/standard.d.ts +57 -0
  385. package/dist/src/tools/standard.d.ts.map +1 -0
  386. package/dist/src/tools/standard.js +113 -0
  387. package/dist/src/tools/standard.js.map +1 -0
  388. package/dist/src/tools/types.d.ts +146 -0
  389. package/dist/src/tools/types.d.ts.map +1 -0
  390. package/dist/src/tools/types.js +28 -0
  391. package/dist/src/tools/types.js.map +1 -0
  392. package/dist/src/tools/undo.d.ts +71 -0
  393. package/dist/src/tools/undo.d.ts.map +1 -0
  394. package/dist/src/tools/undo.js +123 -0
  395. package/dist/src/tools/undo.js.map +1 -0
  396. package/dist/src/tracing/cache-boundary-tracker.d.ts +189 -0
  397. package/dist/src/tracing/cache-boundary-tracker.d.ts.map +1 -0
  398. package/dist/src/tracing/cache-boundary-tracker.js +411 -0
  399. package/dist/src/tracing/cache-boundary-tracker.js.map +1 -0
  400. package/dist/src/tracing/trace-collector.d.ts +274 -0
  401. package/dist/src/tracing/trace-collector.d.ts.map +1 -0
  402. package/dist/src/tracing/trace-collector.js +727 -0
  403. package/dist/src/tracing/trace-collector.js.map +1 -0
  404. package/dist/src/tracing/types.d.ts +657 -0
  405. package/dist/src/tracing/types.d.ts.map +1 -0
  406. package/dist/src/tracing/types.js +39 -0
  407. package/dist/src/tracing/types.js.map +1 -0
  408. package/dist/src/tricks/failure-evidence.d.ts +268 -0
  409. package/dist/src/tricks/failure-evidence.d.ts.map +1 -0
  410. package/dist/src/tricks/failure-evidence.js +544 -0
  411. package/dist/src/tricks/failure-evidence.js.map +1 -0
  412. package/dist/src/tricks/json-utils.d.ts +77 -0
  413. package/dist/src/tricks/json-utils.d.ts.map +1 -0
  414. package/dist/src/tricks/json-utils.js +247 -0
  415. package/dist/src/tricks/json-utils.js.map +1 -0
  416. package/dist/src/tricks/kv-cache-context.d.ts +227 -0
  417. package/dist/src/tricks/kv-cache-context.d.ts.map +1 -0
  418. package/dist/src/tricks/kv-cache-context.js +377 -0
  419. package/dist/src/tricks/kv-cache-context.js.map +1 -0
  420. package/dist/src/tricks/recitation.d.ts +208 -0
  421. package/dist/src/tricks/recitation.d.ts.map +1 -0
  422. package/dist/src/tricks/recitation.js +374 -0
  423. package/dist/src/tricks/recitation.js.map +1 -0
  424. package/dist/src/tricks/reversible-compaction.d.ts +251 -0
  425. package/dist/src/tricks/reversible-compaction.d.ts.map +1 -0
  426. package/dist/src/tricks/reversible-compaction.js +555 -0
  427. package/dist/src/tricks/reversible-compaction.js.map +1 -0
  428. package/dist/src/tricks/serialization-diversity.d.ts +197 -0
  429. package/dist/src/tricks/serialization-diversity.d.ts.map +1 -0
  430. package/dist/src/tricks/serialization-diversity.js +460 -0
  431. package/dist/src/tricks/serialization-diversity.js.map +1 -0
  432. package/dist/src/tui/app.d.ts +42 -0
  433. package/dist/src/tui/app.d.ts.map +1 -0
  434. package/dist/src/tui/app.js +1076 -0
  435. package/dist/src/tui/app.js.map +1 -0
  436. package/dist/src/tui/components/ApprovalDialog.d.ts +28 -0
  437. package/dist/src/tui/components/ApprovalDialog.d.ts.map +1 -0
  438. package/dist/src/tui/components/ApprovalDialog.js +59 -0
  439. package/dist/src/tui/components/ApprovalDialog.js.map +1 -0
  440. package/dist/src/tui/components/InputArea.d.ts +35 -0
  441. package/dist/src/tui/components/InputArea.d.ts.map +1 -0
  442. package/dist/src/tui/components/InputArea.js +144 -0
  443. package/dist/src/tui/components/InputArea.js.map +1 -0
  444. package/dist/src/tui/components/MessageItem.d.ts +28 -0
  445. package/dist/src/tui/components/MessageItem.d.ts.map +1 -0
  446. package/dist/src/tui/components/MessageItem.js +27 -0
  447. package/dist/src/tui/components/MessageItem.js.map +1 -0
  448. package/dist/src/tui/components/ScrollableBox.d.ts +41 -0
  449. package/dist/src/tui/components/ScrollableBox.d.ts.map +1 -0
  450. package/dist/src/tui/components/ScrollableBox.js +101 -0
  451. package/dist/src/tui/components/ScrollableBox.js.map +1 -0
  452. package/dist/src/tui/components/ToolCallItem.d.ts +33 -0
  453. package/dist/src/tui/components/ToolCallItem.d.ts.map +1 -0
  454. package/dist/src/tui/components/ToolCallItem.js +91 -0
  455. package/dist/src/tui/components/ToolCallItem.js.map +1 -0
  456. package/dist/src/tui/components/index.d.ts +13 -0
  457. package/dist/src/tui/components/index.d.ts.map +1 -0
  458. package/dist/src/tui/components/index.js +15 -0
  459. package/dist/src/tui/components/index.js.map +1 -0
  460. package/dist/src/tui/event-display.d.ts +19 -0
  461. package/dist/src/tui/event-display.d.ts.map +1 -0
  462. package/dist/src/tui/event-display.js +178 -0
  463. package/dist/src/tui/event-display.js.map +1 -0
  464. package/dist/src/tui/index.d.ts +105 -0
  465. package/dist/src/tui/index.d.ts.map +1 -0
  466. package/dist/src/tui/index.js +214 -0
  467. package/dist/src/tui/index.js.map +1 -0
  468. package/dist/src/tui/input/CommandPalette.d.ts +55 -0
  469. package/dist/src/tui/input/CommandPalette.d.ts.map +1 -0
  470. package/dist/src/tui/input/CommandPalette.js +135 -0
  471. package/dist/src/tui/input/CommandPalette.js.map +1 -0
  472. package/dist/src/tui/input/index.d.ts +7 -0
  473. package/dist/src/tui/input/index.d.ts.map +1 -0
  474. package/dist/src/tui/input/index.js +7 -0
  475. package/dist/src/tui/input/index.js.map +1 -0
  476. package/dist/src/tui/theme/index.d.ts +45 -0
  477. package/dist/src/tui/theme/index.d.ts.map +1 -0
  478. package/dist/src/tui/theme/index.js +215 -0
  479. package/dist/src/tui/theme/index.js.map +1 -0
  480. package/dist/src/tui/types.d.ts +214 -0
  481. package/dist/src/tui/types.d.ts.map +1 -0
  482. package/dist/src/tui/types.js +27 -0
  483. package/dist/src/tui/types.js.map +1 -0
  484. package/dist/src/types.d.ts +905 -0
  485. package/dist/src/types.d.ts.map +1 -0
  486. package/dist/src/types.js +9 -0
  487. package/dist/src/types.js.map +1 -0
  488. package/package.json +89 -0
@@ -0,0 +1,2824 @@
1
+ /**
2
+ * Lesson 25: Production Agent
3
+ *
4
+ * The capstone agent that composes all features from previous lessons
5
+ * into a production-ready, modular system.
6
+ *
7
+ * Features integrated:
8
+ * - Hooks & Plugins (Lessons 10-11)
9
+ * - Rules System (Lesson 12)
10
+ * - Memory Systems (Lesson 14)
11
+ * - Planning & Reflection (Lessons 15-16)
12
+ * - Multi-Agent Coordination (Lesson 17)
13
+ * - ReAct Pattern (Lesson 18)
14
+ * - Observability (Lesson 19)
15
+ * - Sandboxing (Lesson 20)
16
+ * - Human-in-the-Loop (Lesson 21)
17
+ * - Model Routing (Lesson 22)
18
+ * - Execution Policies (Lesson 23)
19
+ * - Thread Management (Lesson 24)
20
+ */
21
+ import { buildConfig, isFeatureEnabled, getEnabledFeatures, } from './defaults.js';
22
+ import { createModeManager, formatModeList, parseMode, } from './modes.js';
23
+ import { createLSPFileTools, } from './agent-tools/index.js';
24
+ import { HookManager, MemoryManager, PlanningManager, ObservabilityManager, SafetyManager, RoutingManager, MultiAgentManager, ReActManager, ExecutionPolicyManager, ThreadManager, RulesManager, DEFAULT_RULE_SOURCES, ExecutionEconomicsManager, STANDARD_BUDGET, AgentRegistry, filterToolsForAgent, formatAgentList, createCancellationManager, isCancellationError, createResourceManager, createLSPManager, createSemanticCacheManager, createSkillManager, formatSkillList, createContextEngineering, stableStringify, createCodebaseContext, buildContextFromChunks, createPendingPlanManager, } from './integrations/index.js';
25
+ // Lesson 26: Tracing & Evaluation integration
26
+ import { createTraceCollector } from './tracing/trace-collector.js';
27
+ // Spawn agent tool for LLM-driven subagent delegation
28
+ import { createBoundSpawnAgentTool } from './tools/agent.js';
29
+ // =============================================================================
30
+ // PRODUCTION AGENT
31
+ // =============================================================================
32
+ /**
33
+ * Production-ready agent that composes all features.
34
+ */
35
+ export class ProductionAgent {
36
+ config;
37
+ provider;
38
+ tools;
39
+ // Integration managers
40
+ hooks = null;
41
+ memory = null;
42
+ planning = null;
43
+ observability = null;
44
+ safety = null;
45
+ routing = null;
46
+ multiAgent = null;
47
+ react = null;
48
+ executionPolicy = null;
49
+ threadManager = null;
50
+ rules = null;
51
+ economics = null;
52
+ agentRegistry = null;
53
+ cancellation = null;
54
+ resourceManager = null;
55
+ lspManager = null;
56
+ semanticCache = null;
57
+ skillManager = null;
58
+ contextEngineering = null;
59
+ codebaseContext = null;
60
+ traceCollector = null;
61
+ modeManager;
62
+ pendingPlanManager;
63
+ toolResolver = null;
64
+ // Initialization tracking
65
+ initPromises = [];
66
+ initComplete = false;
67
+ // State
68
+ state = {
69
+ status: 'idle',
70
+ messages: [],
71
+ plan: undefined,
72
+ memoryContext: [],
73
+ metrics: {
74
+ totalTokens: 0,
75
+ inputTokens: 0,
76
+ outputTokens: 0,
77
+ estimatedCost: 0,
78
+ llmCalls: 0,
79
+ toolCalls: 0,
80
+ duration: 0,
81
+ },
82
+ iteration: 0,
83
+ };
84
+ constructor(userConfig) {
85
+ // Build complete config with defaults
86
+ this.config = buildConfig(userConfig);
87
+ this.provider = userConfig.provider;
88
+ // Initialize tool registry
89
+ this.tools = new Map();
90
+ for (const tool of this.config.tools) {
91
+ this.tools.set(tool.name, tool);
92
+ }
93
+ // Store tool resolver for lazy-loading unknown tools (e.g., MCP tools)
94
+ this.toolResolver = userConfig.toolResolver || null;
95
+ // Initialize mode manager (always enabled)
96
+ this.modeManager = createModeManager(this.config.tools);
97
+ // Initialize pending plan manager for plan mode write interception
98
+ this.pendingPlanManager = createPendingPlanManager();
99
+ // Initialize enabled features
100
+ this.initializeFeatures();
101
+ }
102
+ /**
103
+ * Initialize all enabled features.
104
+ */
105
+ initializeFeatures() {
106
+ // Debug output only when DEBUG env var is set
107
+ if (process.env.DEBUG) {
108
+ const features = getEnabledFeatures(this.config);
109
+ console.log(`[ProductionAgent] Initializing with features: ${features.join(', ')}`);
110
+ }
111
+ // Hooks & Plugins
112
+ if (isFeatureEnabled(this.config.hooks) && isFeatureEnabled(this.config.plugins)) {
113
+ this.hooks = new HookManager(this.config.hooks, this.config.plugins);
114
+ }
115
+ // Memory
116
+ if (isFeatureEnabled(this.config.memory)) {
117
+ this.memory = new MemoryManager(this.config.memory);
118
+ }
119
+ // Planning & Reflection
120
+ if (isFeatureEnabled(this.config.planning) && isFeatureEnabled(this.config.reflection)) {
121
+ this.planning = new PlanningManager(this.config.planning, this.config.reflection);
122
+ }
123
+ // Observability
124
+ if (isFeatureEnabled(this.config.observability)) {
125
+ this.observability = new ObservabilityManager(this.config.observability);
126
+ // Lesson 26: Full trace capture
127
+ const traceCaptureConfig = this.config.observability.traceCapture;
128
+ if (traceCaptureConfig?.enabled) {
129
+ this.traceCollector = createTraceCollector({
130
+ enabled: true,
131
+ outputDir: traceCaptureConfig.outputDir ?? '.traces',
132
+ captureMessageContent: traceCaptureConfig.captureMessageContent ?? true,
133
+ captureToolResults: traceCaptureConfig.captureToolResults ?? true,
134
+ analyzeCacheBoundaries: traceCaptureConfig.analyzeCacheBoundaries ?? true,
135
+ filePattern: traceCaptureConfig.filePattern ?? 'trace-{sessionId}-{timestamp}.jsonl',
136
+ enableConsoleOutput: false,
137
+ });
138
+ }
139
+ }
140
+ // Safety (Sandbox + Human-in-Loop)
141
+ if (isFeatureEnabled(this.config.sandbox) || isFeatureEnabled(this.config.humanInLoop)) {
142
+ this.safety = new SafetyManager(isFeatureEnabled(this.config.sandbox) ? this.config.sandbox : false, isFeatureEnabled(this.config.humanInLoop) ? this.config.humanInLoop : false);
143
+ }
144
+ // Routing
145
+ if (isFeatureEnabled(this.config.routing)) {
146
+ this.routing = new RoutingManager(this.config.routing);
147
+ }
148
+ // Multi-Agent (Lesson 17)
149
+ if (isFeatureEnabled(this.config.multiAgent)) {
150
+ const roles = (this.config.multiAgent.roles || []).map((r) => ({
151
+ name: r.name,
152
+ description: r.description,
153
+ systemPrompt: r.systemPrompt,
154
+ capabilities: r.capabilities,
155
+ authority: r.authority,
156
+ model: r.model,
157
+ }));
158
+ this.multiAgent = new MultiAgentManager(this.provider, Array.from(this.tools.values()), roles);
159
+ }
160
+ // ReAct (Lesson 18)
161
+ if (isFeatureEnabled(this.config.react)) {
162
+ this.react = new ReActManager(this.provider, Array.from(this.tools.values()), {
163
+ maxSteps: this.config.react.maxSteps,
164
+ stopOnAnswer: this.config.react.stopOnAnswer,
165
+ includeReasoning: this.config.react.includeReasoning,
166
+ });
167
+ }
168
+ // Execution Policies (Lesson 23)
169
+ if (isFeatureEnabled(this.config.executionPolicy)) {
170
+ this.executionPolicy = new ExecutionPolicyManager({
171
+ defaultPolicy: this.config.executionPolicy.defaultPolicy,
172
+ toolPolicies: this.config.executionPolicy.toolPolicies,
173
+ intentAware: this.config.executionPolicy.intentAware,
174
+ intentConfidenceThreshold: this.config.executionPolicy.intentConfidenceThreshold,
175
+ });
176
+ }
177
+ // Thread Management (Lesson 24)
178
+ if (isFeatureEnabled(this.config.threads)) {
179
+ this.threadManager = new ThreadManager();
180
+ }
181
+ // Rules System (Lesson 12)
182
+ if (isFeatureEnabled(this.config.rules)) {
183
+ const ruleSources = this.config.rules.sources || DEFAULT_RULE_SOURCES;
184
+ this.rules = new RulesManager({
185
+ enabled: true,
186
+ sources: ruleSources,
187
+ watch: this.config.rules.watch,
188
+ });
189
+ // Load rules asynchronously - tracked for ensureReady()
190
+ this.initPromises.push(this.rules.loadRules().catch(err => {
191
+ console.warn('[ProductionAgent] Failed to load rules:', err);
192
+ }));
193
+ }
194
+ // Economics System (Token Budget) - always enabled
195
+ this.economics = new ExecutionEconomicsManager({
196
+ ...STANDARD_BUDGET,
197
+ // Use maxIterations from config as absolute safety cap
198
+ maxIterations: this.config.maxIterations,
199
+ targetIterations: Math.min(20, this.config.maxIterations),
200
+ });
201
+ // Agent Registry - always enabled for subagent support
202
+ this.agentRegistry = new AgentRegistry();
203
+ // Load user agents asynchronously - tracked for ensureReady()
204
+ this.initPromises.push(this.agentRegistry.loadUserAgents().catch(err => {
205
+ console.warn('[ProductionAgent] Failed to load user agents:', err);
206
+ }));
207
+ // Register spawn_agent tool so LLM can delegate to subagents
208
+ const boundSpawnTool = createBoundSpawnAgentTool((name, task) => this.spawnAgent(name, task));
209
+ this.tools.set(boundSpawnTool.name, boundSpawnTool);
210
+ // Cancellation Support
211
+ if (isFeatureEnabled(this.config.cancellation)) {
212
+ this.cancellation = createCancellationManager();
213
+ // Forward cancellation events
214
+ this.cancellation.subscribe(event => {
215
+ if (event.type === 'cancellation.requested') {
216
+ this.emit({ type: 'cancellation.requested', reason: event.reason });
217
+ }
218
+ });
219
+ }
220
+ // Resource Monitoring
221
+ if (isFeatureEnabled(this.config.resources)) {
222
+ this.resourceManager = createResourceManager({
223
+ enabled: this.config.resources.enabled,
224
+ maxMemoryMB: this.config.resources.maxMemoryMB,
225
+ maxCpuTimeSec: this.config.resources.maxCpuTimeSec,
226
+ maxConcurrentOps: this.config.resources.maxConcurrentOps,
227
+ warnThreshold: this.config.resources.warnThreshold,
228
+ criticalThreshold: this.config.resources.criticalThreshold,
229
+ });
230
+ }
231
+ // LSP (Language Server Protocol) Support
232
+ if (isFeatureEnabled(this.config.lsp)) {
233
+ this.lspManager = createLSPManager({
234
+ enabled: this.config.lsp.enabled,
235
+ autoDetect: this.config.lsp.autoDetect,
236
+ servers: this.config.lsp.servers,
237
+ timeout: this.config.lsp.timeout,
238
+ });
239
+ // Auto-start is done lazily on first use to avoid startup delays
240
+ }
241
+ // Semantic Cache Support
242
+ if (isFeatureEnabled(this.config.semanticCache)) {
243
+ this.semanticCache = createSemanticCacheManager({
244
+ enabled: this.config.semanticCache.enabled,
245
+ threshold: this.config.semanticCache.threshold,
246
+ maxSize: this.config.semanticCache.maxSize,
247
+ ttl: this.config.semanticCache.ttl,
248
+ });
249
+ // Forward cache events
250
+ this.semanticCache.subscribe(event => {
251
+ if (event.type === 'cache.hit') {
252
+ this.emit({ type: 'cache.hit', query: event.query, similarity: event.similarity });
253
+ }
254
+ else if (event.type === 'cache.miss') {
255
+ this.emit({ type: 'cache.miss', query: event.query });
256
+ }
257
+ else if (event.type === 'cache.set') {
258
+ this.emit({ type: 'cache.set', query: event.query });
259
+ }
260
+ });
261
+ }
262
+ // Skills Support
263
+ if (isFeatureEnabled(this.config.skills)) {
264
+ this.skillManager = createSkillManager({
265
+ enabled: this.config.skills.enabled,
266
+ directories: this.config.skills.directories,
267
+ loadBuiltIn: this.config.skills.loadBuiltIn,
268
+ autoActivate: this.config.skills.autoActivate,
269
+ });
270
+ // Load skills asynchronously - tracked for ensureReady()
271
+ this.initPromises.push(this.skillManager.loadSkills()
272
+ .then(() => { }) // Convert to void
273
+ .catch(err => {
274
+ console.warn('[ProductionAgent] Failed to load skills:', err);
275
+ }));
276
+ }
277
+ // Context Engineering (Manus-inspired tricks P, Q, R, S, T)
278
+ // Always enabled - these are performance optimizations
279
+ this.contextEngineering = createContextEngineering({
280
+ enableCacheOptimization: true,
281
+ enableRecitation: true,
282
+ enableReversibleCompaction: true,
283
+ enableFailureTracking: true,
284
+ enableDiversity: false, // Off by default - can cause unexpected behavior
285
+ staticPrefix: this.config.systemPrompt,
286
+ recitationFrequency: 5,
287
+ maxFailures: 30,
288
+ maxReferences: 50,
289
+ });
290
+ // Codebase Context - intelligent code selection for context management
291
+ // Analyzes repo structure and selects relevant code within token budgets
292
+ if (this.config.codebaseContext !== false) {
293
+ const codebaseConfig = typeof this.config.codebaseContext === 'object'
294
+ ? this.config.codebaseContext
295
+ : {};
296
+ this.codebaseContext = createCodebaseContext({
297
+ root: codebaseConfig.root ?? process.cwd(),
298
+ includePatterns: codebaseConfig.includePatterns,
299
+ excludePatterns: codebaseConfig.excludePatterns,
300
+ maxFileSize: codebaseConfig.maxFileSize ?? 100 * 1024, // 100KB
301
+ tokensPerChar: 0.25,
302
+ analyzeDependencies: true,
303
+ cacheResults: true,
304
+ cacheTTL: 5 * 60 * 1000, // 5 minutes
305
+ });
306
+ }
307
+ // Forward context engineering events
308
+ this.contextEngineering.on(event => {
309
+ switch (event.type) {
310
+ case 'failure.recorded':
311
+ this.observability?.logger?.warn('Failure recorded', {
312
+ action: event.failure.action,
313
+ category: event.failure.category,
314
+ });
315
+ break;
316
+ case 'failure.pattern':
317
+ this.observability?.logger?.warn('Failure pattern detected', {
318
+ type: event.pattern.type,
319
+ description: event.pattern.description,
320
+ });
321
+ this.emit({ type: 'error', error: `Pattern: ${event.pattern.description}` });
322
+ break;
323
+ case 'recitation.injected':
324
+ this.observability?.logger?.debug('Recitation injected', {
325
+ iteration: event.iteration,
326
+ });
327
+ break;
328
+ }
329
+ });
330
+ }
331
+ /**
332
+ * Ensure all async initialization is complete before running.
333
+ * Call this at the start of run() to prevent race conditions.
334
+ */
335
+ async ensureReady() {
336
+ if (this.initComplete) {
337
+ return;
338
+ }
339
+ if (this.initPromises.length > 0) {
340
+ await Promise.all(this.initPromises);
341
+ }
342
+ this.initComplete = true;
343
+ }
344
+ /**
345
+ * Run the agent on a task.
346
+ */
347
+ async run(task) {
348
+ // Ensure all integrations are ready before running
349
+ await this.ensureReady();
350
+ const startTime = Date.now();
351
+ // Create cancellation context if enabled
352
+ const cancellationConfig = isFeatureEnabled(this.config.cancellation) ? this.config.cancellation : null;
353
+ const cancellationToken = this.cancellation?.createContext(cancellationConfig?.defaultTimeout || undefined);
354
+ // Start tracing
355
+ const traceId = this.observability?.tracer?.startTrace('agent.run') || `trace-${Date.now()}`;
356
+ this.emit({ type: 'start', task, traceId });
357
+ this.observability?.logger?.info('Agent started', { task });
358
+ // Lesson 26: Start trace capture session
359
+ const traceSessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
360
+ await this.traceCollector?.startSession(traceSessionId, task, this.config.model || 'default', {});
361
+ try {
362
+ // Check for cancellation before starting
363
+ cancellationToken?.throwIfCancellationRequested();
364
+ // Check if planning is needed
365
+ if (this.planning?.shouldPlan(task)) {
366
+ await this.createAndExecutePlan(task);
367
+ }
368
+ else {
369
+ await this.executeDirectly(task);
370
+ }
371
+ // Get final response - find the LAST assistant message (not just check if last message is assistant)
372
+ const assistantMessages = this.state.messages.filter(m => m.role === 'assistant');
373
+ const lastAssistantMessage = assistantMessages[assistantMessages.length - 1];
374
+ const response = typeof lastAssistantMessage?.content === 'string'
375
+ ? lastAssistantMessage.content
376
+ : '';
377
+ // Finalize
378
+ const duration = Date.now() - startTime;
379
+ this.state.metrics.duration = duration;
380
+ await this.observability?.tracer?.endTrace();
381
+ const result = {
382
+ success: true,
383
+ response,
384
+ metrics: this.getMetrics(),
385
+ messages: this.state.messages,
386
+ traceId,
387
+ plan: this.state.plan,
388
+ };
389
+ this.emit({ type: 'complete', result });
390
+ this.observability?.logger?.info('Agent completed', { duration, success: true });
391
+ // Lesson 26: End trace capture session
392
+ await this.traceCollector?.endSession({ success: true, output: response });
393
+ return result;
394
+ }
395
+ catch (err) {
396
+ const error = err instanceof Error ? err : new Error(String(err));
397
+ // Handle cancellation specially
398
+ if (isCancellationError(err)) {
399
+ const cleanupStart = Date.now();
400
+ this.cancellation?.disposeContext();
401
+ const cleanupDuration = Date.now() - cleanupStart;
402
+ this.emit({ type: 'cancellation.completed', cleanupDuration });
403
+ this.observability?.logger?.info('Agent cancelled', { reason: error.message, cleanupDuration });
404
+ // Lesson 26: End trace capture session on cancellation
405
+ await this.traceCollector?.endSession({ success: false, failureReason: `Cancelled: ${error.message}` });
406
+ return {
407
+ success: false,
408
+ response: '',
409
+ error: `Cancelled: ${error.message}`,
410
+ metrics: this.getMetrics(),
411
+ messages: this.state.messages,
412
+ traceId,
413
+ };
414
+ }
415
+ this.observability?.tracer?.recordError(error);
416
+ await this.observability?.tracer?.endTrace();
417
+ this.emit({ type: 'error', error: error.message });
418
+ this.observability?.logger?.error('Agent failed', { error: error.message });
419
+ // Lesson 26: End trace capture session on error
420
+ await this.traceCollector?.endSession({ success: false, failureReason: error.message });
421
+ return {
422
+ success: false,
423
+ response: '',
424
+ error: error.message,
425
+ metrics: this.getMetrics(),
426
+ messages: this.state.messages,
427
+ traceId,
428
+ };
429
+ }
430
+ finally {
431
+ // Dispose cancellation context on completion
432
+ this.cancellation?.disposeContext();
433
+ }
434
+ }
435
+ /**
436
+ * Create and execute a plan for complex tasks.
437
+ */
438
+ async createAndExecutePlan(task) {
439
+ this.observability?.logger?.info('Creating plan for complex task');
440
+ const plan = await this.planning.createPlan(task, this.provider);
441
+ this.state.plan = plan;
442
+ this.emit({ type: 'planning', plan });
443
+ // Execute each task in the plan
444
+ while (!this.planning.isPlanComplete()) {
445
+ const currentTask = this.planning.getNextTask();
446
+ if (!currentTask)
447
+ break;
448
+ this.planning.startTask(currentTask.id);
449
+ this.emit({ type: 'task.start', task: currentTask });
450
+ try {
451
+ await this.executeDirectly(currentTask.description);
452
+ this.planning.completeTask(currentTask.id);
453
+ this.emit({ type: 'task.complete', task: currentTask });
454
+ }
455
+ catch (err) {
456
+ this.planning.failTask(currentTask.id);
457
+ this.observability?.logger?.warn('Plan task failed', { taskId: currentTask.id });
458
+ // Continue with other tasks if possible
459
+ }
460
+ // Check iteration limit
461
+ if (this.state.iteration >= this.config.maxIterations) {
462
+ this.observability?.logger?.warn('Max iterations reached');
463
+ break;
464
+ }
465
+ }
466
+ }
467
+ /**
468
+ * Execute a task directly without planning.
469
+ */
470
+ async executeDirectly(task) {
471
+ // Build messages
472
+ const messages = this.buildMessages(task);
473
+ // Reset economics for new task
474
+ this.economics?.reset();
475
+ // Reflection configuration
476
+ const reflectionConfig = this.config.reflection;
477
+ const reflectionEnabled = isFeatureEnabled(reflectionConfig);
478
+ const autoReflect = reflectionEnabled && reflectionConfig.autoReflect;
479
+ const maxReflectionAttempts = reflectionEnabled
480
+ ? (reflectionConfig.maxAttempts || 3)
481
+ : 1;
482
+ const confidenceThreshold = reflectionEnabled
483
+ ? (reflectionConfig.confidenceThreshold || 0.8)
484
+ : 0.8;
485
+ let reflectionAttempt = 0;
486
+ let lastResponse = '';
487
+ // Outer loop for reflection (if enabled)
488
+ while (reflectionAttempt < maxReflectionAttempts) {
489
+ reflectionAttempt++;
490
+ // Agent loop - now uses economics-based budget checking
491
+ while (true) {
492
+ this.state.iteration++;
493
+ // =======================================================================
494
+ // CANCELLATION CHECK
495
+ // =======================================================================
496
+ if (this.cancellation?.isCancelled) {
497
+ this.cancellation.token.throwIfCancellationRequested();
498
+ }
499
+ // =======================================================================
500
+ // RESOURCE CHECK - system resource limits
501
+ // =======================================================================
502
+ if (this.resourceManager) {
503
+ const resourceCheck = this.resourceManager.check();
504
+ if (!resourceCheck.canContinue) {
505
+ this.observability?.logger?.warn('Resource limit reached', {
506
+ status: resourceCheck.status,
507
+ message: resourceCheck.message,
508
+ });
509
+ this.emit({ type: 'error', error: resourceCheck.message || 'Resource limit exceeded' });
510
+ break;
511
+ }
512
+ // Log warnings for elevated usage
513
+ if (resourceCheck.status === 'warning' || resourceCheck.status === 'critical') {
514
+ this.observability?.logger?.info(`Resource status: ${resourceCheck.status}`, {
515
+ message: resourceCheck.message,
516
+ });
517
+ }
518
+ }
519
+ // =======================================================================
520
+ // ECONOMICS CHECK (Token Budget) - replaces hard iteration limit
521
+ // =======================================================================
522
+ if (this.economics) {
523
+ const budgetCheck = this.economics.checkBudget();
524
+ if (!budgetCheck.canContinue) {
525
+ // Hard limit reached
526
+ this.observability?.logger?.warn('Budget limit reached', {
527
+ reason: budgetCheck.reason,
528
+ budgetType: budgetCheck.budgetType,
529
+ });
530
+ // Emit appropriate event
531
+ if (budgetCheck.budgetType === 'iterations') {
532
+ this.emit({ type: 'error', error: `Max iterations reached (${this.state.iteration})` });
533
+ }
534
+ else {
535
+ this.emit({ type: 'error', error: budgetCheck.reason || 'Budget exceeded' });
536
+ }
537
+ break;
538
+ }
539
+ // Check for soft limits and potential extension
540
+ if (budgetCheck.isSoftLimit && budgetCheck.suggestedAction === 'request_extension') {
541
+ this.observability?.logger?.info('Approaching budget limit', {
542
+ reason: budgetCheck.reason,
543
+ percentUsed: budgetCheck.percentUsed,
544
+ });
545
+ // Could request extension here if handler is set
546
+ }
547
+ }
548
+ else {
549
+ // Fallback to simple iteration check if economics not available
550
+ if (this.state.iteration >= this.config.maxIterations) {
551
+ this.observability?.logger?.warn('Max iterations reached');
552
+ break;
553
+ }
554
+ }
555
+ // =======================================================================
556
+ // RECITATION INJECTION (Trick Q) - Combat "lost in middle" attention
557
+ // =======================================================================
558
+ if (this.contextEngineering) {
559
+ if (process.env.DEBUG_LLM) {
560
+ if (process.env.DEBUG)
561
+ console.log(`[recitation] Before: ${messages.length} messages`);
562
+ }
563
+ const enrichedMessages = this.contextEngineering.injectRecitation(messages, {
564
+ goal: task,
565
+ plan: this.state.plan ? {
566
+ description: this.state.plan.goal || task,
567
+ tasks: this.state.plan.tasks.map(t => ({
568
+ id: t.id,
569
+ description: t.description,
570
+ status: t.status,
571
+ })),
572
+ currentTaskIndex: this.state.plan.tasks.findIndex(t => t.status === 'in_progress'),
573
+ } : undefined,
574
+ activeFiles: this.economics?.getProgress().filesModified
575
+ ? [`${this.economics.getProgress().filesModified} files modified`]
576
+ : undefined,
577
+ recentErrors: this.contextEngineering.getFailureInsights().slice(0, 2),
578
+ });
579
+ if (process.env.DEBUG_LLM) {
580
+ if (process.env.DEBUG)
581
+ console.log(`[recitation] After: ${enrichedMessages?.length ?? 'null/undefined'} messages`);
582
+ }
583
+ // Only replace if we got a DIFFERENT array back (avoid clearing same reference)
584
+ // When no injection needed, injectRecitation returns the same array reference
585
+ if (enrichedMessages && enrichedMessages !== messages && enrichedMessages.length > 0) {
586
+ messages.length = 0;
587
+ messages.push(...enrichedMessages);
588
+ }
589
+ else if (!enrichedMessages || enrichedMessages.length === 0) {
590
+ console.warn('[executeDirectly] Recitation returned empty/null messages, keeping original');
591
+ }
592
+ // If enrichedMessages === messages, we don't need to do anything (same reference)
593
+ // Update recitation frequency based on context size
594
+ const contextTokens = messages.reduce((sum, m) => sum + (m.content?.length || 0) / 4, 0);
595
+ this.contextEngineering.updateRecitationFrequency(contextTokens);
596
+ }
597
+ // =======================================================================
598
+ // FAILURE CONTEXT INJECTION (Trick S) - Learn from mistakes
599
+ // =======================================================================
600
+ if (this.contextEngineering) {
601
+ const failureContext = this.contextEngineering.getFailureContext(5);
602
+ if (failureContext) {
603
+ // Insert failure context before the last user message
604
+ // (Using reverse iteration for ES2022 compatibility)
605
+ let lastUserIdx = -1;
606
+ for (let i = messages.length - 1; i >= 0; i--) {
607
+ if (messages[i].role === 'user') {
608
+ lastUserIdx = i;
609
+ break;
610
+ }
611
+ }
612
+ if (lastUserIdx > 0) {
613
+ messages.splice(lastUserIdx, 0, {
614
+ role: 'system',
615
+ content: failureContext,
616
+ });
617
+ }
618
+ }
619
+ }
620
+ // Make LLM call
621
+ const response = await this.callLLM(messages);
622
+ // Record LLM usage for economics
623
+ if (this.economics && response.usage) {
624
+ this.economics.recordLLMUsage(response.usage.inputTokens, response.usage.outputTokens, this.config.model, response.usage.cost // Use actual cost from provider when available
625
+ );
626
+ }
627
+ // Add assistant message
628
+ const assistantMessage = {
629
+ role: 'assistant',
630
+ content: response.content,
631
+ toolCalls: response.toolCalls,
632
+ };
633
+ messages.push(assistantMessage);
634
+ this.state.messages.push(assistantMessage);
635
+ lastResponse = response.content;
636
+ // Check for tool calls
637
+ if (!response.toolCalls || response.toolCalls.length === 0) {
638
+ // No tool calls, agent is done - compact tool outputs to save context
639
+ // The model has "consumed" the tool outputs and produced a response,
640
+ // so we can replace verbose outputs with compact summaries
641
+ this.compactToolOutputs();
642
+ break;
643
+ }
644
+ // Execute tool calls
645
+ const toolResults = await this.executeToolCalls(response.toolCalls);
646
+ // Record tool calls for economics/progress tracking
647
+ for (let i = 0; i < response.toolCalls.length; i++) {
648
+ const toolCall = response.toolCalls[i];
649
+ const result = toolResults[i];
650
+ this.economics?.recordToolCall(toolCall.name, toolCall.arguments, result?.result);
651
+ }
652
+ // Add tool results to messages (with truncation and proactive budget management)
653
+ const MAX_TOOL_OUTPUT_CHARS = 8000; // ~2000 tokens max per tool output
654
+ // =======================================================================
655
+ // PROACTIVE BUDGET CHECK - compact BEFORE we overflow, not after
656
+ // =======================================================================
657
+ if (this.economics) {
658
+ const currentUsage = this.economics.getUsage();
659
+ const budget = this.economics.getBudget();
660
+ const percentUsed = (currentUsage.tokens / budget.maxTokens) * 100;
661
+ // If we're at 70%+ of budget, proactively compact to make room
662
+ if (percentUsed >= 70) {
663
+ this.observability?.logger?.info('Proactive compaction triggered', {
664
+ percentUsed: Math.round(percentUsed),
665
+ currentTokens: currentUsage.tokens,
666
+ maxTokens: budget.maxTokens,
667
+ });
668
+ this.compactToolOutputs();
669
+ }
670
+ }
671
+ for (const result of toolResults) {
672
+ let content = typeof result.result === 'string' ? result.result : stableStringify(result.result);
673
+ // Truncate long outputs to save context
674
+ if (content.length > MAX_TOOL_OUTPUT_CHARS) {
675
+ content = content.slice(0, MAX_TOOL_OUTPUT_CHARS) + `\n\n... [truncated ${content.length - MAX_TOOL_OUTPUT_CHARS} chars]`;
676
+ }
677
+ // =======================================================================
678
+ // ESTIMATE if adding this result would exceed budget
679
+ // =======================================================================
680
+ if (this.economics) {
681
+ const estimatedNewTokens = Math.ceil(content.length / 4); // ~4 chars per token
682
+ const currentContextTokens = this.estimateContextTokens(messages);
683
+ const budget = this.economics.getBudget();
684
+ // Check if adding this would push us over the hard limit
685
+ if (currentContextTokens + estimatedNewTokens > budget.maxTokens * 0.95) {
686
+ this.observability?.logger?.warn('Skipping tool result to stay within budget', {
687
+ toolCallId: result.callId,
688
+ estimatedTokens: estimatedNewTokens,
689
+ currentContext: currentContextTokens,
690
+ limit: budget.maxTokens,
691
+ });
692
+ // Add a truncated placeholder instead
693
+ const toolMessage = {
694
+ role: 'tool',
695
+ content: `[Result omitted to stay within token budget. Original size: ${content.length} chars]`,
696
+ toolCallId: result.callId,
697
+ };
698
+ messages.push(toolMessage);
699
+ this.state.messages.push(toolMessage);
700
+ continue;
701
+ }
702
+ }
703
+ const toolMessage = {
704
+ role: 'tool',
705
+ content,
706
+ toolCallId: result.callId,
707
+ };
708
+ messages.push(toolMessage);
709
+ this.state.messages.push(toolMessage);
710
+ }
711
+ }
712
+ // =======================================================================
713
+ // REFLECTION (Lesson 16)
714
+ // =======================================================================
715
+ if (autoReflect && this.planning && reflectionAttempt < maxReflectionAttempts) {
716
+ this.emit({ type: 'reflection', attempt: reflectionAttempt, satisfied: false });
717
+ const reflectionResult = await this.planning.reflect(task, lastResponse, this.provider);
718
+ this.state.metrics.reflectionAttempts = reflectionAttempt;
719
+ if (reflectionResult.satisfied && reflectionResult.confidence >= confidenceThreshold) {
720
+ // Output is satisfactory
721
+ this.emit({ type: 'reflection', attempt: reflectionAttempt, satisfied: true });
722
+ break;
723
+ }
724
+ // Not satisfied - add feedback and continue
725
+ const feedbackMessage = {
726
+ role: 'user',
727
+ content: `[Reflection feedback]\nThe previous output needs improvement:\n- Critique: ${reflectionResult.critique}\n- Suggestions: ${reflectionResult.suggestions.join(', ')}\n\nPlease improve the output.`,
728
+ };
729
+ messages.push(feedbackMessage);
730
+ this.state.messages.push(feedbackMessage);
731
+ this.observability?.logger?.info('Reflection not satisfied, retrying', {
732
+ attempt: reflectionAttempt,
733
+ confidence: reflectionResult.confidence,
734
+ critique: reflectionResult.critique,
735
+ });
736
+ }
737
+ else {
738
+ // No reflection or already satisfied
739
+ break;
740
+ }
741
+ }
742
+ // Store conversation in memory
743
+ this.memory?.storeConversation(this.state.messages);
744
+ this.updateMemoryStats();
745
+ }
746
+ /**
747
+ * Build messages for LLM call.
748
+ *
749
+ * Uses cache-aware system prompt building (Trick P) when contextEngineering
750
+ * is available, ensuring static content is ordered for optimal KV-cache reuse.
751
+ */
752
+ buildMessages(task) {
753
+ const messages = [];
754
+ // Gather all context components
755
+ const rulesContent = this.rules?.getRulesContent() ?? '';
756
+ const skillsPrompt = this.skillManager?.getActiveSkillsPrompt() ?? '';
757
+ const memoryContext = this.memory?.getContextStrings(task) ?? [];
758
+ // Budget-aware codebase context selection
759
+ let codebaseContextStr = '';
760
+ if (this.codebaseContext) {
761
+ // Calculate available budget for codebase context
762
+ // Reserve tokens for: rules (~2000), tools (~2500), memory (~1000), conversation (~5000)
763
+ const reservedTokens = 10500;
764
+ const maxContextTokens = (this.config.maxContextTokens ?? 80000) - reservedTokens;
765
+ const codebaseBudget = Math.min(maxContextTokens * 0.3, 15000); // Up to 30% or 15K tokens
766
+ try {
767
+ // Use synchronous cache if available, otherwise skip
768
+ const repoMap = this.codebaseContext.getRepoMap();
769
+ if (repoMap) {
770
+ const selection = this.selectRelevantCodeSync(task, codebaseBudget);
771
+ if (selection.chunks.length > 0) {
772
+ codebaseContextStr = buildContextFromChunks(selection.chunks, {
773
+ includeFilePaths: true,
774
+ includeSeparators: true,
775
+ maxTotalTokens: codebaseBudget,
776
+ });
777
+ }
778
+ }
779
+ }
780
+ catch {
781
+ // Codebase analysis not ready yet - skip for this call
782
+ }
783
+ }
784
+ // Build tool descriptions
785
+ let toolDescriptions = '';
786
+ if (this.tools.size > 0) {
787
+ const toolLines = [];
788
+ for (const tool of this.tools.values()) {
789
+ toolLines.push(`- ${tool.name}: ${tool.description}`);
790
+ }
791
+ toolDescriptions = toolLines.join('\n');
792
+ }
793
+ // Add MCP tool summaries
794
+ if (this.config.mcpToolSummaries && this.config.mcpToolSummaries.length > 0) {
795
+ const mcpLines = this.config.mcpToolSummaries.map(s => `- ${s.name}: ${s.description}`);
796
+ if (toolDescriptions) {
797
+ toolDescriptions += '\n\nMCP tools (call directly, they auto-load):\n' + mcpLines.join('\n');
798
+ }
799
+ else {
800
+ toolDescriptions = 'MCP tools (call directly, they auto-load):\n' + mcpLines.join('\n');
801
+ }
802
+ }
803
+ // Build system prompt using cache-aware builder if available (Trick P)
804
+ let systemPrompt;
805
+ // Combine memory and codebase context
806
+ const combinedContext = [
807
+ ...(memoryContext.length > 0 ? memoryContext : []),
808
+ ...(codebaseContextStr ? [`\n## Relevant Code\n${codebaseContextStr}`] : []),
809
+ ].join('\n');
810
+ if (this.contextEngineering) {
811
+ // Use cache-optimized prompt builder - orders sections for KV-cache reuse:
812
+ // static prefix -> rules -> tools -> memory/codebase -> dynamic
813
+ systemPrompt = this.contextEngineering.buildSystemPrompt({
814
+ rules: rulesContent + (skillsPrompt ? '\n\n' + skillsPrompt : ''),
815
+ tools: toolDescriptions,
816
+ memory: combinedContext.length > 0 ? combinedContext : undefined,
817
+ dynamic: {
818
+ mode: this.modeManager?.getMode() ?? 'default',
819
+ },
820
+ });
821
+ }
822
+ else {
823
+ // Fallback: manual concatenation (original behavior)
824
+ systemPrompt = this.config.systemPrompt;
825
+ if (rulesContent)
826
+ systemPrompt += '\n\n' + rulesContent;
827
+ if (skillsPrompt)
828
+ systemPrompt += skillsPrompt;
829
+ if (combinedContext.length > 0) {
830
+ systemPrompt += '\n\nRelevant context:\n' + combinedContext;
831
+ }
832
+ if (toolDescriptions) {
833
+ systemPrompt += '\n\nAvailable tools:\n' + toolDescriptions;
834
+ }
835
+ }
836
+ // Safety check: ensure system prompt is not empty
837
+ if (!systemPrompt || systemPrompt.trim().length === 0) {
838
+ console.warn('[buildMessages] Warning: Empty system prompt detected, using fallback');
839
+ systemPrompt = this.config.systemPrompt || 'You are a helpful AI assistant.';
840
+ }
841
+ messages.push({ role: 'system', content: systemPrompt });
842
+ // Add existing conversation
843
+ for (const msg of this.state.messages) {
844
+ if (msg.role !== 'system') {
845
+ messages.push(msg);
846
+ }
847
+ }
848
+ // Add current task
849
+ messages.push({ role: 'user', content: task });
850
+ return messages;
851
+ }
852
+ /**
853
+ * Call the LLM with routing and observability.
854
+ */
855
+ async callLLM(messages) {
856
+ const spanId = this.observability?.tracer?.startSpan('llm.call');
857
+ this.emit({ type: 'llm.start', model: this.config.model || 'default' });
858
+ // Emit context insight for verbose feedback
859
+ const estimatedTokens = messages.reduce((sum, m) => {
860
+ const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
861
+ return sum + Math.ceil(content.length / 3.5); // ~3.5 chars per token estimate
862
+ }, 0);
863
+ // Use context window size, not output token limit
864
+ const contextLimit = this.config.maxContextTokens || 100000;
865
+ this.emit({
866
+ type: 'insight.context',
867
+ currentTokens: estimatedTokens,
868
+ maxTokens: contextLimit,
869
+ messageCount: messages.length,
870
+ percentUsed: Math.round((estimatedTokens / contextLimit) * 100),
871
+ });
872
+ const startTime = Date.now();
873
+ const requestId = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
874
+ // Debug: Log message count and structure (helps diagnose API errors)
875
+ if (process.env.DEBUG_LLM) {
876
+ console.log(`[callLLM] Sending ${messages.length} messages:`);
877
+ messages.forEach((m, i) => {
878
+ console.log(` [${i}] ${m.role}: ${m.content?.slice(0, 50)}...`);
879
+ });
880
+ }
881
+ // Validate messages are not empty
882
+ if (!messages || messages.length === 0) {
883
+ throw new Error('No messages to send to LLM');
884
+ }
885
+ // Lesson 26: Record LLM request for tracing
886
+ const model = this.config.model || 'default';
887
+ const provider = this.config.provider?.name || 'unknown';
888
+ this.traceCollector?.record({
889
+ type: 'llm.request',
890
+ data: {
891
+ requestId,
892
+ model,
893
+ provider,
894
+ messages: messages.map(m => ({
895
+ role: m.role,
896
+ content: m.content,
897
+ toolCallId: m.toolCallId,
898
+ toolCalls: m.toolCalls?.map(tc => ({
899
+ id: tc.id,
900
+ name: tc.name,
901
+ arguments: tc.arguments,
902
+ })),
903
+ })),
904
+ tools: Array.from(this.tools.values()).map(t => ({
905
+ name: t.name,
906
+ description: t.description,
907
+ parametersSchema: t.parameters,
908
+ })),
909
+ parameters: {
910
+ maxTokens: this.config.maxTokens,
911
+ temperature: this.config.temperature,
912
+ },
913
+ },
914
+ });
915
+ try {
916
+ let response;
917
+ let actualModel = model;
918
+ // Use routing if enabled
919
+ if (this.routing) {
920
+ const complexity = this.routing.estimateComplexity(messages[messages.length - 1]?.content || '');
921
+ const context = {
922
+ task: messages[messages.length - 1]?.content || '',
923
+ complexity,
924
+ hasTools: this.tools.size > 0,
925
+ hasImages: false,
926
+ taskType: 'general',
927
+ estimatedTokens: messages.reduce((sum, m) => sum + m.content.length / 4, 0),
928
+ };
929
+ const result = await this.routing.executeWithFallback(messages, context);
930
+ response = result.response;
931
+ actualModel = result.model;
932
+ // Emit routing insight
933
+ this.emit({
934
+ type: 'insight.routing',
935
+ model: actualModel,
936
+ reason: actualModel !== model ? 'Routed based on complexity' : 'Default model',
937
+ complexity: complexity <= 0.3 ? 'low' : complexity <= 0.7 ? 'medium' : 'high',
938
+ });
939
+ }
940
+ else {
941
+ response = await this.provider.chat(messages, {
942
+ model: this.config.model,
943
+ tools: Array.from(this.tools.values()),
944
+ });
945
+ }
946
+ const duration = Date.now() - startTime;
947
+ // Lesson 26: Record LLM response for tracing
948
+ this.traceCollector?.record({
949
+ type: 'llm.response',
950
+ data: {
951
+ requestId,
952
+ content: response.content || '',
953
+ toolCalls: response.toolCalls?.map(tc => ({
954
+ id: tc.id,
955
+ name: tc.name,
956
+ arguments: tc.arguments,
957
+ })),
958
+ stopReason: response.stopReason === 'end_turn' ? 'end_turn'
959
+ : response.stopReason === 'tool_use' ? 'tool_use'
960
+ : response.stopReason === 'max_tokens' ? 'max_tokens'
961
+ : 'stop_sequence',
962
+ usage: {
963
+ inputTokens: response.usage?.inputTokens || 0,
964
+ outputTokens: response.usage?.outputTokens || 0,
965
+ cacheReadTokens: response.usage?.cacheReadTokens,
966
+ cacheWriteTokens: response.usage?.cacheWriteTokens,
967
+ cost: response.usage?.cost, // Actual cost from provider (e.g., OpenRouter)
968
+ },
969
+ durationMs: duration,
970
+ },
971
+ });
972
+ // Record metrics
973
+ this.observability?.metrics?.recordLLMCall(response.usage?.inputTokens || 0, response.usage?.outputTokens || 0, duration, actualModel, response.usage?.cost // Actual cost from provider (e.g., OpenRouter)
974
+ );
975
+ this.state.metrics.llmCalls++;
976
+ this.state.metrics.inputTokens += response.usage?.inputTokens || 0;
977
+ this.state.metrics.outputTokens += response.usage?.outputTokens || 0;
978
+ this.state.metrics.totalTokens = this.state.metrics.inputTokens + this.state.metrics.outputTokens;
979
+ this.emit({ type: 'llm.complete', response });
980
+ // Emit token usage insight for verbose feedback
981
+ if (response.usage) {
982
+ this.emit({
983
+ type: 'insight.tokens',
984
+ inputTokens: response.usage.inputTokens,
985
+ outputTokens: response.usage.outputTokens,
986
+ cacheReadTokens: response.usage.cacheReadTokens,
987
+ cacheWriteTokens: response.usage.cacheWriteTokens,
988
+ cost: response.usage.cost,
989
+ model: actualModel,
990
+ });
991
+ }
992
+ this.observability?.tracer?.endSpan(spanId);
993
+ return response;
994
+ }
995
+ catch (err) {
996
+ const error = err instanceof Error ? err : new Error(String(err));
997
+ this.observability?.tracer?.recordError(error);
998
+ this.observability?.tracer?.endSpan(spanId);
999
+ throw error;
1000
+ }
1001
+ }
1002
+ /**
1003
+ * Execute tool calls with safety checks and execution policy enforcement.
1004
+ */
1005
+ async executeToolCalls(toolCalls) {
1006
+ const results = [];
1007
+ for (const toolCall of toolCalls) {
1008
+ const spanId = this.observability?.tracer?.startSpan(`tool.${toolCall.name}`);
1009
+ const executionId = `exec-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1010
+ this.emit({ type: 'tool.start', tool: toolCall.name, args: toolCall.arguments });
1011
+ const startTime = Date.now();
1012
+ // Lesson 26: Record tool start for tracing
1013
+ this.traceCollector?.record({
1014
+ type: 'tool.start',
1015
+ data: {
1016
+ executionId,
1017
+ toolName: toolCall.name,
1018
+ arguments: toolCall.arguments,
1019
+ },
1020
+ });
1021
+ try {
1022
+ // =====================================================================
1023
+ // PLAN MODE WRITE INTERCEPTION
1024
+ // =====================================================================
1025
+ // In plan mode, intercept write operations and queue them as proposed changes
1026
+ if (this.modeManager.shouldInterceptTool(toolCall.name, toolCall.arguments)) {
1027
+ // Extract reason from context - use last assistant message or generate one
1028
+ const lastAssistantMsg = [...this.state.messages].reverse().find(m => m.role === 'assistant');
1029
+ const reason = typeof lastAssistantMsg?.content === 'string'
1030
+ ? lastAssistantMsg.content.slice(0, 200)
1031
+ : `Proposed change: ${toolCall.name}`;
1032
+ // Start a new plan if needed
1033
+ if (!this.pendingPlanManager.hasPendingPlan()) {
1034
+ const lastUserMsg = [...this.state.messages].reverse().find(m => m.role === 'user');
1035
+ const task = typeof lastUserMsg?.content === 'string' ? lastUserMsg.content : 'Plan';
1036
+ this.pendingPlanManager.startPlan(task);
1037
+ }
1038
+ // Queue the write operation
1039
+ const change = this.pendingPlanManager.addProposedChange(toolCall.name, toolCall.arguments, reason, toolCall.id);
1040
+ // Emit event for UI
1041
+ this.emit({
1042
+ type: 'plan.change.queued',
1043
+ tool: toolCall.name,
1044
+ changeId: change?.id,
1045
+ });
1046
+ // Return a message indicating the change was queued
1047
+ const queueMessage = `[PLAN MODE] Change queued for approval:\n` +
1048
+ `Tool: ${toolCall.name}\n` +
1049
+ `${this.formatToolArgsForPlan(toolCall.name, toolCall.arguments)}\n` +
1050
+ `Use /show-plan to see all pending changes, /approve to execute, /reject to discard.`;
1051
+ results.push({
1052
+ callId: toolCall.id,
1053
+ result: queueMessage,
1054
+ });
1055
+ this.observability?.tracer?.endSpan(spanId);
1056
+ continue; // Skip actual execution
1057
+ }
1058
+ // =====================================================================
1059
+ // EXECUTION POLICY ENFORCEMENT (Lesson 23)
1060
+ // =====================================================================
1061
+ if (this.executionPolicy) {
1062
+ const policyContext = {
1063
+ messages: this.state.messages,
1064
+ currentMessage: this.state.messages.find(m => m.role === 'user')?.content,
1065
+ previousToolCalls: toolCalls.slice(0, toolCalls.indexOf(toolCall)),
1066
+ };
1067
+ const evaluation = this.executionPolicy.evaluate(toolCall, policyContext);
1068
+ // Emit policy event
1069
+ this.emit({
1070
+ type: 'policy.evaluated',
1071
+ tool: toolCall.name,
1072
+ policy: evaluation.policy,
1073
+ reason: evaluation.reason,
1074
+ });
1075
+ // Handle forbidden policy - always block
1076
+ if (evaluation.policy === 'forbidden') {
1077
+ throw new Error(`Forbidden by policy: ${evaluation.reason}`);
1078
+ }
1079
+ // Handle prompt policy - requires approval
1080
+ if (evaluation.policy === 'prompt' && evaluation.requiresApproval) {
1081
+ // Try to get approval through safety manager's human-in-loop
1082
+ if (this.safety?.humanInLoop) {
1083
+ const approval = await this.safety.humanInLoop.requestApproval(toolCall, `Policy requires approval: ${evaluation.reason}`);
1084
+ if (!approval.approved) {
1085
+ throw new Error(`Denied by user: ${approval.reason || 'No reason provided'}`);
1086
+ }
1087
+ // Create a grant for future similar calls if approved
1088
+ this.executionPolicy.createGrant({
1089
+ toolName: toolCall.name,
1090
+ grantedBy: 'user',
1091
+ reason: 'Approved during execution',
1092
+ maxUsages: 5, // Allow 5 more similar calls
1093
+ });
1094
+ }
1095
+ else {
1096
+ // No approval handler - block by default for safety
1097
+ throw new Error(`Policy requires approval but no approval handler available: ${evaluation.reason}`);
1098
+ }
1099
+ }
1100
+ // Log intent classification if available
1101
+ if (evaluation.intent) {
1102
+ this.emit({
1103
+ type: 'intent.classified',
1104
+ tool: toolCall.name,
1105
+ intent: evaluation.intent.type,
1106
+ confidence: evaluation.intent.confidence,
1107
+ });
1108
+ }
1109
+ }
1110
+ // =====================================================================
1111
+ // SAFETY VALIDATION (Lesson 20-21)
1112
+ // =====================================================================
1113
+ if (this.safety) {
1114
+ const validation = await this.safety.validateAndApprove(toolCall, `Executing tool: ${toolCall.name}`);
1115
+ if (!validation.allowed) {
1116
+ throw new Error(`Tool call blocked: ${validation.reason}`);
1117
+ }
1118
+ }
1119
+ // Get tool definition (with lazy-loading support for MCP tools)
1120
+ let tool = this.tools.get(toolCall.name);
1121
+ const wasPreloaded = !!tool;
1122
+ if (!tool && this.toolResolver) {
1123
+ // Try to resolve and load the tool on-demand
1124
+ const resolved = this.toolResolver(toolCall.name);
1125
+ if (resolved) {
1126
+ this.addTool(resolved);
1127
+ tool = resolved;
1128
+ if (process.env.DEBUG)
1129
+ console.log(` 🔄 Auto-loaded MCP tool: ${toolCall.name}`);
1130
+ this.observability?.logger?.info('Tool auto-loaded', { tool: toolCall.name });
1131
+ }
1132
+ }
1133
+ if (!tool) {
1134
+ throw new Error(`Unknown tool: ${toolCall.name}`);
1135
+ }
1136
+ // Log whether tool was pre-loaded or auto-loaded (for MCP tools)
1137
+ if (process.env.DEBUG && toolCall.name.startsWith('mcp_') && wasPreloaded) {
1138
+ console.log(` ✓ Using pre-loaded MCP tool: ${toolCall.name}`);
1139
+ }
1140
+ // Execute tool (with sandbox if available)
1141
+ let result;
1142
+ if (this.safety?.sandbox) {
1143
+ result = await this.safety.sandbox.executeWithLimits(() => tool.execute(toolCall.arguments));
1144
+ }
1145
+ else {
1146
+ result = await tool.execute(toolCall.arguments);
1147
+ }
1148
+ const duration = Date.now() - startTime;
1149
+ // Lesson 26: Record tool completion for tracing
1150
+ this.traceCollector?.record({
1151
+ type: 'tool.end',
1152
+ data: {
1153
+ executionId,
1154
+ status: 'success',
1155
+ result,
1156
+ durationMs: duration,
1157
+ },
1158
+ });
1159
+ // Record metrics
1160
+ this.observability?.metrics?.recordToolCall(toolCall.name, duration, true);
1161
+ this.state.metrics.toolCalls++;
1162
+ this.emit({ type: 'tool.complete', tool: toolCall.name, result });
1163
+ // Emit tool insight with result summary
1164
+ const summary = this.summarizeToolResult(toolCall.name, result);
1165
+ this.emit({
1166
+ type: 'insight.tool',
1167
+ tool: toolCall.name,
1168
+ summary,
1169
+ durationMs: duration,
1170
+ success: true,
1171
+ });
1172
+ results.push({
1173
+ callId: toolCall.id,
1174
+ result,
1175
+ });
1176
+ this.observability?.tracer?.endSpan(spanId);
1177
+ }
1178
+ catch (err) {
1179
+ const error = err instanceof Error ? err : new Error(String(err));
1180
+ const duration = Date.now() - startTime;
1181
+ // Lesson 26: Record tool error for tracing
1182
+ this.traceCollector?.record({
1183
+ type: 'tool.end',
1184
+ data: {
1185
+ executionId,
1186
+ status: error.message.includes('Blocked') || error.message.includes('Policy') ? 'blocked' : 'error',
1187
+ error,
1188
+ durationMs: duration,
1189
+ },
1190
+ });
1191
+ this.observability?.metrics?.recordToolCall(toolCall.name, duration, false);
1192
+ this.observability?.tracer?.recordError(error);
1193
+ this.observability?.tracer?.endSpan(spanId);
1194
+ // FAILURE EVIDENCE RECORDING (Trick S)
1195
+ // Track failed tool calls to prevent loops and provide context
1196
+ this.contextEngineering?.recordFailure({
1197
+ action: toolCall.name,
1198
+ args: toolCall.arguments,
1199
+ error,
1200
+ intent: `Execute tool ${toolCall.name}`,
1201
+ });
1202
+ results.push({
1203
+ callId: toolCall.id,
1204
+ result: `Error: ${error.message}`,
1205
+ error: error.message,
1206
+ });
1207
+ this.emit({ type: 'tool.blocked', tool: toolCall.name, reason: error.message });
1208
+ }
1209
+ }
1210
+ return results;
1211
+ }
1212
+ /**
1213
+ * Select relevant code synchronously using cached repo analysis.
1214
+ * Returns empty result if analysis hasn't been run yet.
1215
+ */
1216
+ selectRelevantCodeSync(task, maxTokens) {
1217
+ if (!this.codebaseContext) {
1218
+ return { chunks: [], totalTokens: 0 };
1219
+ }
1220
+ const repoMap = this.codebaseContext.getRepoMap();
1221
+ if (!repoMap) {
1222
+ return { chunks: [], totalTokens: 0 };
1223
+ }
1224
+ // Get all chunks and score by relevance
1225
+ const allChunks = Array.from(repoMap.chunks.values());
1226
+ const taskLower = task.toLowerCase();
1227
+ const taskWords = taskLower.split(/\s+/).filter((w) => w.length > 2);
1228
+ // Score chunks by task relevance
1229
+ const scored = allChunks.map((chunk) => {
1230
+ let relevance = 0;
1231
+ // Check file path
1232
+ const pathLower = chunk.filePath.toLowerCase();
1233
+ for (const word of taskWords) {
1234
+ if (pathLower.includes(word))
1235
+ relevance += 0.3;
1236
+ }
1237
+ // Check symbols
1238
+ for (const symbol of chunk.symbols) {
1239
+ const symbolLower = symbol.toLowerCase();
1240
+ for (const word of taskWords) {
1241
+ if (symbolLower.includes(word) || word.includes(symbolLower)) {
1242
+ relevance += 0.2;
1243
+ }
1244
+ }
1245
+ }
1246
+ // Combine with base importance
1247
+ const combinedScore = chunk.importance * 0.4 + Math.min(relevance, 1) * 0.6;
1248
+ return { chunk, score: combinedScore };
1249
+ });
1250
+ // Sort by score and select within budget
1251
+ scored.sort((a, b) => b.score - a.score);
1252
+ const selected = [];
1253
+ let totalTokens = 0;
1254
+ for (const { chunk, score } of scored) {
1255
+ if (score < 0.1)
1256
+ continue; // Skip very low relevance
1257
+ if (totalTokens + chunk.tokenCount > maxTokens)
1258
+ continue;
1259
+ selected.push({
1260
+ filePath: chunk.filePath,
1261
+ content: chunk.content,
1262
+ tokenCount: chunk.tokenCount,
1263
+ importance: score,
1264
+ });
1265
+ totalTokens += chunk.tokenCount;
1266
+ }
1267
+ return { chunks: selected, totalTokens };
1268
+ }
1269
+ /**
1270
+ * Analyze the codebase (async). Call this once at startup for optimal performance.
1271
+ */
1272
+ async analyzeCodebase(root) {
1273
+ if (this.codebaseContext) {
1274
+ await this.codebaseContext.analyze(root);
1275
+ }
1276
+ }
1277
+ /**
1278
+ * Emit an event.
1279
+ */
1280
+ emit(event) {
1281
+ this.hooks?.emit(event);
1282
+ }
1283
+ /**
1284
+ * Create a brief summary of a tool result for insight display.
1285
+ */
1286
+ summarizeToolResult(toolName, result) {
1287
+ if (result === null || result === undefined) {
1288
+ return 'No output';
1289
+ }
1290
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
1291
+ // Tool-specific summaries
1292
+ if (toolName === 'list_files' || toolName === 'glob') {
1293
+ const lines = resultStr.split('\n').filter(l => l.trim());
1294
+ return `Found ${lines.length} file${lines.length !== 1 ? 's' : ''}`;
1295
+ }
1296
+ if (toolName === 'bash' || toolName === 'execute_command') {
1297
+ const lines = resultStr.split('\n').filter(l => l.trim());
1298
+ if (resultStr.includes('exit code: 0') || !resultStr.includes('exit code:')) {
1299
+ return lines.length > 1 ? `Success (${lines.length} lines)` : 'Success';
1300
+ }
1301
+ return `Failed - ${lines[0]?.slice(0, 50) || 'see output'}`;
1302
+ }
1303
+ if (toolName === 'read_file') {
1304
+ const lines = resultStr.split('\n').length;
1305
+ return `Read ${lines} line${lines !== 1 ? 's' : ''}`;
1306
+ }
1307
+ if (toolName === 'write_file' || toolName === 'edit_file') {
1308
+ return 'File updated';
1309
+ }
1310
+ if (toolName === 'search' || toolName === 'grep') {
1311
+ const matches = (resultStr.match(/\n/g) || []).length;
1312
+ return `${matches} match${matches !== 1 ? 'es' : ''}`;
1313
+ }
1314
+ // Generic summary
1315
+ if (resultStr.length <= 50) {
1316
+ return resultStr;
1317
+ }
1318
+ return `${resultStr.slice(0, 47)}...`;
1319
+ }
1320
+ /**
1321
+ * Format tool arguments for plan display.
1322
+ */
1323
+ formatToolArgsForPlan(toolName, args) {
1324
+ if (toolName === 'write_file') {
1325
+ const path = args.path || args.file_path;
1326
+ const content = String(args.content || '');
1327
+ const preview = content.slice(0, 100).replace(/\n/g, '\\n');
1328
+ return `File: ${path}\nContent preview: ${preview}${content.length > 100 ? '...' : ''}`;
1329
+ }
1330
+ if (toolName === 'edit_file') {
1331
+ const path = args.path || args.file_path;
1332
+ return `File: ${path}\nOld: ${String(args.old_string || args.search || '').slice(0, 50)}...\nNew: ${String(args.new_string || args.replace || '').slice(0, 50)}...`;
1333
+ }
1334
+ if (toolName === 'bash') {
1335
+ return `Command: ${String(args.command || '').slice(0, 100)}`;
1336
+ }
1337
+ if (toolName === 'delete_file') {
1338
+ return `Delete: ${args.path || args.file_path}`;
1339
+ }
1340
+ // Generic
1341
+ return `Args: ${JSON.stringify(args).slice(0, 100)}...`;
1342
+ }
1343
+ /**
1344
+ * Update memory statistics.
1345
+ * Memory stats are retrieved via memory manager, not stored in state.
1346
+ */
1347
+ updateMemoryStats() {
1348
+ // Memory stats are accessed via getMetrics() when needed
1349
+ // This method exists for hook/extension points
1350
+ this.memory?.getStats();
1351
+ }
1352
+ /**
1353
+ * Get current metrics.
1354
+ */
1355
+ getMetrics() {
1356
+ if (this.observability?.metrics) {
1357
+ return this.observability.metrics.getMetrics();
1358
+ }
1359
+ return this.state.metrics;
1360
+ }
1361
+ /**
1362
+ * Get current state.
1363
+ */
1364
+ getState() {
1365
+ return { ...this.state };
1366
+ }
1367
+ /**
1368
+ * Get the trace collector (Lesson 26).
1369
+ * Returns null if trace capture is not enabled.
1370
+ */
1371
+ getTraceCollector() {
1372
+ return this.traceCollector;
1373
+ }
1374
+ /**
1375
+ * Subscribe to events.
1376
+ */
1377
+ subscribe(listener) {
1378
+ if (this.hooks) {
1379
+ return this.hooks.subscribe(listener);
1380
+ }
1381
+ return () => { };
1382
+ }
1383
+ /**
1384
+ * Reset agent state.
1385
+ */
1386
+ reset() {
1387
+ this.state = {
1388
+ status: 'idle',
1389
+ messages: [],
1390
+ plan: undefined,
1391
+ memoryContext: [],
1392
+ metrics: {
1393
+ totalTokens: 0,
1394
+ inputTokens: 0,
1395
+ outputTokens: 0,
1396
+ estimatedCost: 0,
1397
+ llmCalls: 0,
1398
+ toolCalls: 0,
1399
+ duration: 0,
1400
+ },
1401
+ iteration: 0,
1402
+ };
1403
+ this.memory?.clear();
1404
+ this.observability?.metrics?.reset();
1405
+ this.planning?.clearPlan();
1406
+ this.observability?.logger?.info('Agent state reset');
1407
+ }
1408
+ /**
1409
+ * Load messages from a previous session.
1410
+ * @deprecated Use loadState() for full state restoration
1411
+ */
1412
+ loadMessages(messages) {
1413
+ this.state.messages = [...messages];
1414
+ // Sync to threadManager if enabled
1415
+ if (this.threadManager) {
1416
+ const thread = this.threadManager.getActiveThread();
1417
+ thread.messages = [...messages];
1418
+ }
1419
+ this.observability?.logger?.info('Messages loaded', { count: messages.length });
1420
+ }
1421
+ /**
1422
+ * Serializable state for checkpoints (excludes non-serializable fields).
1423
+ */
1424
+ getSerializableState() {
1425
+ return {
1426
+ messages: this.state.messages,
1427
+ iteration: this.state.iteration,
1428
+ metrics: { ...this.state.metrics },
1429
+ plan: this.state.plan ? { ...this.state.plan } : undefined,
1430
+ memoryContext: this.state.memoryContext ? [...this.state.memoryContext] : undefined,
1431
+ };
1432
+ }
1433
+ /**
1434
+ * Validate checkpoint data before loading.
1435
+ * Returns validation result with errors and warnings.
1436
+ */
1437
+ validateCheckpoint(data) {
1438
+ const errors = [];
1439
+ const warnings = [];
1440
+ // Check if data is an object
1441
+ if (!data || typeof data !== 'object') {
1442
+ errors.push('Checkpoint data must be an object');
1443
+ return { valid: false, errors, warnings, sanitized: null };
1444
+ }
1445
+ const checkpoint = data;
1446
+ // Validate messages array (required)
1447
+ if (!checkpoint.messages) {
1448
+ errors.push('Checkpoint missing required "messages" field');
1449
+ }
1450
+ else if (!Array.isArray(checkpoint.messages)) {
1451
+ errors.push('Checkpoint "messages" must be an array');
1452
+ }
1453
+ else {
1454
+ // Validate each message has required fields
1455
+ for (let i = 0; i < checkpoint.messages.length; i++) {
1456
+ const msg = checkpoint.messages[i];
1457
+ if (!msg || typeof msg !== 'object') {
1458
+ errors.push(`Message at index ${i} is not an object`);
1459
+ continue;
1460
+ }
1461
+ if (!msg.role || typeof msg.role !== 'string') {
1462
+ errors.push(`Message at index ${i} missing valid "role" field`);
1463
+ }
1464
+ if (msg.content !== undefined && msg.content !== null && typeof msg.content !== 'string') {
1465
+ // Content can be undefined for tool call messages
1466
+ warnings.push(`Message at index ${i} has non-string content (type: ${typeof msg.content})`);
1467
+ }
1468
+ }
1469
+ }
1470
+ // Validate iteration (optional but should be non-negative number)
1471
+ if (checkpoint.iteration !== undefined) {
1472
+ if (typeof checkpoint.iteration !== 'number' || checkpoint.iteration < 0) {
1473
+ warnings.push(`Invalid iteration value: ${checkpoint.iteration}, will use default`);
1474
+ }
1475
+ }
1476
+ // Validate metrics (optional)
1477
+ if (checkpoint.metrics !== undefined && checkpoint.metrics !== null) {
1478
+ if (typeof checkpoint.metrics !== 'object') {
1479
+ warnings.push('Metrics field is not an object, will be ignored');
1480
+ }
1481
+ }
1482
+ // Validate memoryContext (optional)
1483
+ if (checkpoint.memoryContext !== undefined && checkpoint.memoryContext !== null) {
1484
+ if (!Array.isArray(checkpoint.memoryContext)) {
1485
+ warnings.push('memoryContext is not an array, will be ignored');
1486
+ }
1487
+ }
1488
+ // If we have critical errors, fail validation
1489
+ if (errors.length > 0) {
1490
+ return { valid: false, errors, warnings, sanitized: null };
1491
+ }
1492
+ // Build sanitized checkpoint
1493
+ const messages = checkpoint.messages.filter((msg) => msg && typeof msg === 'object' && typeof msg.role === 'string');
1494
+ const sanitized = {
1495
+ messages,
1496
+ iteration: typeof checkpoint.iteration === 'number' && checkpoint.iteration >= 0
1497
+ ? checkpoint.iteration
1498
+ : Math.floor(messages.length / 2),
1499
+ metrics: typeof checkpoint.metrics === 'object' && checkpoint.metrics !== null
1500
+ ? checkpoint.metrics
1501
+ : undefined,
1502
+ plan: checkpoint.plan,
1503
+ memoryContext: Array.isArray(checkpoint.memoryContext)
1504
+ ? checkpoint.memoryContext
1505
+ : undefined,
1506
+ };
1507
+ return { valid: true, errors, warnings, sanitized };
1508
+ }
1509
+ /**
1510
+ * Load full state from a checkpoint.
1511
+ * Restores messages, iteration, metrics, plan, and memory context.
1512
+ * Validates checkpoint data before loading to prevent corrupted state.
1513
+ */
1514
+ loadState(savedState) {
1515
+ // Validate checkpoint data
1516
+ const validation = this.validateCheckpoint(savedState);
1517
+ // Log warnings
1518
+ for (const warning of validation.warnings) {
1519
+ console.warn(`[Checkpoint] Warning: ${warning}`);
1520
+ this.observability?.logger?.warn('Checkpoint validation warning', { warning });
1521
+ }
1522
+ // Fail on validation errors
1523
+ if (!validation.valid || !validation.sanitized) {
1524
+ const errorMsg = `Invalid checkpoint: ${validation.errors.join('; ')}`;
1525
+ this.observability?.logger?.error('Checkpoint validation failed', { errors: validation.errors });
1526
+ throw new Error(errorMsg);
1527
+ }
1528
+ // Use sanitized data
1529
+ const sanitized = validation.sanitized;
1530
+ // Restore messages
1531
+ this.state.messages = [...sanitized.messages];
1532
+ // Restore iteration (already validated/defaulted in sanitized)
1533
+ this.state.iteration = sanitized.iteration;
1534
+ // Restore metrics (merge with defaults)
1535
+ if (sanitized.metrics) {
1536
+ this.state.metrics = {
1537
+ totalTokens: sanitized.metrics.totalTokens ?? 0,
1538
+ inputTokens: sanitized.metrics.inputTokens ?? 0,
1539
+ outputTokens: sanitized.metrics.outputTokens ?? 0,
1540
+ estimatedCost: sanitized.metrics.estimatedCost ?? 0,
1541
+ llmCalls: sanitized.metrics.llmCalls ?? 0,
1542
+ toolCalls: sanitized.metrics.toolCalls ?? 0,
1543
+ duration: sanitized.metrics.duration ?? 0,
1544
+ reflectionAttempts: sanitized.metrics.reflectionAttempts,
1545
+ };
1546
+ }
1547
+ // Restore plan if present
1548
+ if (sanitized.plan) {
1549
+ this.state.plan = { ...sanitized.plan };
1550
+ // Sync with planning manager if enabled
1551
+ if (this.planning) {
1552
+ this.planning.loadPlan(sanitized.plan);
1553
+ }
1554
+ }
1555
+ // Restore memory context if present
1556
+ if (sanitized.memoryContext) {
1557
+ this.state.memoryContext = [...sanitized.memoryContext];
1558
+ }
1559
+ // Sync to threadManager if enabled
1560
+ if (this.threadManager) {
1561
+ const thread = this.threadManager.getActiveThread();
1562
+ thread.messages = [...sanitized.messages];
1563
+ }
1564
+ this.observability?.logger?.info('State loaded', {
1565
+ messageCount: sanitized.messages.length,
1566
+ iteration: this.state.iteration,
1567
+ hasPlan: !!sanitized.plan,
1568
+ hasMemoryContext: !!sanitized.memoryContext,
1569
+ });
1570
+ }
1571
+ /**
1572
+ * Add a tool dynamically.
1573
+ */
1574
+ addTool(tool) {
1575
+ this.tools.set(tool.name, tool);
1576
+ this.observability?.logger?.debug('Tool added', { tool: tool.name });
1577
+ }
1578
+ /**
1579
+ * Remove a tool.
1580
+ */
1581
+ removeTool(name) {
1582
+ this.tools.delete(name);
1583
+ this.observability?.logger?.debug('Tool removed', { tool: name });
1584
+ }
1585
+ /**
1586
+ * Compact tool outputs to save context.
1587
+ * Called after model produces a response - replaces verbose tool outputs
1588
+ * with compact summaries since the model has already "consumed" them.
1589
+ */
1590
+ compactToolOutputs() {
1591
+ const COMPACT_PREVIEW_LENGTH = 200; // Keep first 200 chars as preview
1592
+ let compactedCount = 0;
1593
+ let savedChars = 0;
1594
+ for (const msg of this.state.messages) {
1595
+ if (msg.role === 'tool' && msg.content && msg.content.length > COMPACT_PREVIEW_LENGTH * 2) {
1596
+ const originalLength = msg.content.length;
1597
+ const preview = msg.content.slice(0, COMPACT_PREVIEW_LENGTH).replace(/\n/g, ' ');
1598
+ msg.content = `[${preview}...] (${originalLength} chars, compacted)`;
1599
+ savedChars += originalLength - msg.content.length;
1600
+ compactedCount++;
1601
+ }
1602
+ }
1603
+ if (compactedCount > 0 && process.env.DEBUG) {
1604
+ console.log(` 📦 Compacted ${compactedCount} tool outputs (saved ~${Math.round(savedChars / 4)} tokens)`);
1605
+ }
1606
+ }
1607
+ /**
1608
+ * Estimate total tokens in a message array.
1609
+ * Uses ~4 chars per token heuristic for fast estimation.
1610
+ */
1611
+ estimateContextTokens(messages) {
1612
+ let totalChars = 0;
1613
+ for (const msg of messages) {
1614
+ if (msg.content) {
1615
+ totalChars += msg.content.length;
1616
+ }
1617
+ // Account for tool calls in assistant messages
1618
+ if (msg.toolCalls) {
1619
+ for (const tc of msg.toolCalls) {
1620
+ totalChars += tc.name.length;
1621
+ totalChars += JSON.stringify(tc.arguments).length;
1622
+ }
1623
+ }
1624
+ }
1625
+ return Math.ceil(totalChars / 4); // ~4 chars per token
1626
+ }
1627
+ /**
1628
+ * Get audit log (if human-in-loop is enabled).
1629
+ */
1630
+ getAuditLog() {
1631
+ return this.safety?.humanInLoop?.getAuditLog() || [];
1632
+ }
1633
+ // =========================================================================
1634
+ // MULTI-AGENT METHODS (Lesson 17)
1635
+ // =========================================================================
1636
+ /**
1637
+ * Run a task with a multi-agent team.
1638
+ * Requires multiAgent to be enabled in config.
1639
+ */
1640
+ async runWithTeam(task, roles) {
1641
+ if (!this.multiAgent) {
1642
+ throw new Error('Multi-agent not enabled. Enable it in config to use runWithTeam()');
1643
+ }
1644
+ this.observability?.logger?.info('Running with team', { task: task.goal, roles: roles.map(r => r.name) });
1645
+ // Register roles if not already registered
1646
+ for (const role of roles) {
1647
+ this.multiAgent.registerRole(role);
1648
+ }
1649
+ // Set up event forwarding
1650
+ this.multiAgent.on(event => {
1651
+ switch (event.type) {
1652
+ case 'agent.spawn':
1653
+ this.emit({ type: 'multiagent.spawn', agentId: event.agentId, role: event.role });
1654
+ break;
1655
+ case 'agent.complete':
1656
+ this.emit({ type: 'multiagent.complete', agentId: event.agentId, success: event.result.success });
1657
+ break;
1658
+ case 'consensus.start':
1659
+ this.emit({ type: 'consensus.start', strategy: event.strategy });
1660
+ break;
1661
+ case 'consensus.reached':
1662
+ this.emit({ type: 'consensus.reached', agreed: event.decision.agreed, result: event.decision.result });
1663
+ break;
1664
+ }
1665
+ });
1666
+ const result = await this.multiAgent.runWithTeam(task, {
1667
+ roles,
1668
+ consensusStrategy: this.config.multiAgent && isFeatureEnabled(this.config.multiAgent)
1669
+ ? this.config.multiAgent.consensusStrategy || 'voting'
1670
+ : 'voting',
1671
+ communicationMode: 'broadcast',
1672
+ });
1673
+ return result;
1674
+ }
1675
+ /**
1676
+ * Add a role to the multi-agent manager.
1677
+ */
1678
+ addRole(role) {
1679
+ if (!this.multiAgent) {
1680
+ throw new Error('Multi-agent not enabled');
1681
+ }
1682
+ this.multiAgent.registerRole(role);
1683
+ }
1684
+ // =========================================================================
1685
+ // REACT METHODS (Lesson 18)
1686
+ // =========================================================================
1687
+ /**
1688
+ * Run a task using the ReAct (Reasoning + Acting) pattern.
1689
+ * Provides explicit reasoning traces.
1690
+ */
1691
+ async runWithReAct(task) {
1692
+ if (!this.react) {
1693
+ throw new Error('ReAct not enabled. Enable it in config to use runWithReAct()');
1694
+ }
1695
+ this.observability?.logger?.info('Running with ReAct', { task });
1696
+ // Set up event forwarding
1697
+ this.react.on(event => {
1698
+ switch (event.type) {
1699
+ case 'react.thought':
1700
+ this.emit({ type: 'react.thought', step: event.step, thought: event.thought });
1701
+ break;
1702
+ case 'react.action':
1703
+ this.emit({ type: 'react.action', step: event.step, action: event.action.tool, input: event.action.input });
1704
+ break;
1705
+ case 'react.observation':
1706
+ this.emit({ type: 'react.observation', step: event.step, observation: event.observation });
1707
+ break;
1708
+ case 'react.answer':
1709
+ this.emit({ type: 'react.answer', answer: event.answer });
1710
+ break;
1711
+ }
1712
+ });
1713
+ const trace = await this.react.run(task);
1714
+ // Store trace in memory if available
1715
+ if (this.memory && trace.finalAnswer) {
1716
+ this.memory.storeConversation([
1717
+ { role: 'user', content: task },
1718
+ { role: 'assistant', content: trace.finalAnswer },
1719
+ ]);
1720
+ }
1721
+ return trace;
1722
+ }
1723
+ /**
1724
+ * Get the ReAct trace formatted as a string.
1725
+ */
1726
+ formatReActTrace(trace) {
1727
+ if (!this.react) {
1728
+ throw new Error('ReAct not enabled');
1729
+ }
1730
+ return this.react.formatTrace(trace);
1731
+ }
1732
+ // =========================================================================
1733
+ // EXECUTION POLICY METHODS (Lesson 23)
1734
+ // =========================================================================
1735
+ /**
1736
+ * Create a permission grant for a tool.
1737
+ * Allows temporary, scoped permissions.
1738
+ */
1739
+ createPermissionGrant(options) {
1740
+ if (!this.executionPolicy) {
1741
+ throw new Error('Execution policies not enabled');
1742
+ }
1743
+ const grant = this.executionPolicy.createGrant({
1744
+ toolName: options.toolName,
1745
+ argPattern: options.argPattern,
1746
+ grantedBy: options.grantedBy || 'user',
1747
+ expiresAt: options.expiresAt,
1748
+ maxUsages: options.maxUsages,
1749
+ reason: options.reason,
1750
+ });
1751
+ this.emit({ type: 'grant.created', grantId: grant.id, tool: options.toolName });
1752
+ return grant.id;
1753
+ }
1754
+ /**
1755
+ * Revoke a permission grant.
1756
+ */
1757
+ revokePermissionGrant(grantId) {
1758
+ if (!this.executionPolicy) {
1759
+ throw new Error('Execution policies not enabled');
1760
+ }
1761
+ return this.executionPolicy.revokeGrant(grantId);
1762
+ }
1763
+ /**
1764
+ * Get active permission grants.
1765
+ */
1766
+ getActiveGrants() {
1767
+ if (!this.executionPolicy) {
1768
+ return [];
1769
+ }
1770
+ return this.executionPolicy.getActiveGrants();
1771
+ }
1772
+ // =========================================================================
1773
+ // ECONOMICS METHODS (Token Budget)
1774
+ // =========================================================================
1775
+ /**
1776
+ * Get current budget usage.
1777
+ */
1778
+ getBudgetUsage() {
1779
+ if (!this.economics)
1780
+ return null;
1781
+ const usage = this.economics.getUsage();
1782
+ const budget = this.economics.getBudget();
1783
+ return {
1784
+ tokens: usage.tokens,
1785
+ cost: usage.cost,
1786
+ duration: usage.duration,
1787
+ iterations: usage.iterations,
1788
+ percentUsed: Math.max((usage.tokens / budget.maxTokens) * 100, (usage.cost / budget.maxCost) * 100, (usage.duration / budget.maxDuration) * 100),
1789
+ };
1790
+ }
1791
+ /**
1792
+ * Get current budget limits.
1793
+ */
1794
+ getBudgetLimits() {
1795
+ if (!this.economics)
1796
+ return null;
1797
+ const budget = this.economics.getBudget();
1798
+ return {
1799
+ maxTokens: budget.maxTokens,
1800
+ maxCost: budget.maxCost,
1801
+ maxDuration: budget.maxDuration,
1802
+ maxIterations: budget.maxIterations,
1803
+ };
1804
+ }
1805
+ /**
1806
+ * Get progress tracking info.
1807
+ */
1808
+ getProgress() {
1809
+ if (!this.economics)
1810
+ return null;
1811
+ return this.economics.getProgress();
1812
+ }
1813
+ /**
1814
+ * Extend the budget limits.
1815
+ */
1816
+ extendBudget(extension) {
1817
+ if (this.economics) {
1818
+ this.economics.extendBudget(extension);
1819
+ }
1820
+ }
1821
+ // =========================================================================
1822
+ // THREAD MANAGEMENT METHODS (Lesson 24)
1823
+ // =========================================================================
1824
+ /**
1825
+ * Create a checkpoint of the current state.
1826
+ * Useful before risky operations.
1827
+ */
1828
+ createCheckpoint(label) {
1829
+ if (!this.threadManager) {
1830
+ throw new Error('Thread management not enabled. Enable it in config to use createCheckpoint()');
1831
+ }
1832
+ const checkpoint = this.threadManager.createCheckpoint({
1833
+ label,
1834
+ agentState: this.state,
1835
+ });
1836
+ this.emit({ type: 'checkpoint.created', checkpointId: checkpoint.id, label });
1837
+ this.observability?.logger?.info('Checkpoint created', { checkpointId: checkpoint.id, label });
1838
+ return checkpoint;
1839
+ }
1840
+ /**
1841
+ * Restore from a checkpoint.
1842
+ */
1843
+ restoreCheckpoint(checkpointId) {
1844
+ if (!this.threadManager) {
1845
+ throw new Error('Thread management not enabled');
1846
+ }
1847
+ const state = this.threadManager.restoreCheckpoint(checkpointId);
1848
+ if (!state) {
1849
+ return false;
1850
+ }
1851
+ // Restore agent state
1852
+ this.state.messages = state.messages;
1853
+ this.state.metrics = state.metrics;
1854
+ this.state.iteration = state.iteration;
1855
+ this.emit({ type: 'checkpoint.restored', checkpointId });
1856
+ this.observability?.logger?.info('Checkpoint restored', { checkpointId });
1857
+ return true;
1858
+ }
1859
+ /**
1860
+ * Rollback the conversation by N messages.
1861
+ */
1862
+ rollback(steps) {
1863
+ if (!this.threadManager) {
1864
+ throw new Error('Thread management not enabled');
1865
+ }
1866
+ // Sync state.messages to threadManager before rollback (messages may have been added directly)
1867
+ const thread = this.threadManager.getActiveThread();
1868
+ thread.messages = [...this.state.messages];
1869
+ const success = this.threadManager.rollback(steps);
1870
+ if (success) {
1871
+ // Sync back to state
1872
+ this.state.messages = this.threadManager.getMessages();
1873
+ this.emit({ type: 'rollback', steps });
1874
+ this.observability?.logger?.info('Rolled back', { steps });
1875
+ }
1876
+ return success;
1877
+ }
1878
+ /**
1879
+ * Fork the current conversation into a new branch.
1880
+ * Useful for exploring alternatives.
1881
+ */
1882
+ fork(name) {
1883
+ if (!this.threadManager) {
1884
+ throw new Error('Thread management not enabled');
1885
+ }
1886
+ const thread = this.threadManager.fork({ name });
1887
+ this.emit({ type: 'thread.forked', threadId: thread.id, parentId: thread.parentId || 'main' });
1888
+ this.observability?.logger?.info('Thread forked', { threadId: thread.id, name });
1889
+ return thread.id;
1890
+ }
1891
+ /**
1892
+ * Switch to a different thread.
1893
+ */
1894
+ switchThread(threadId) {
1895
+ if (!this.threadManager) {
1896
+ throw new Error('Thread management not enabled');
1897
+ }
1898
+ const fromId = this.threadManager.getActiveThread().id;
1899
+ const success = this.threadManager.switchThread(threadId);
1900
+ if (success) {
1901
+ this.state.messages = this.threadManager.getMessages();
1902
+ this.emit({ type: 'thread.switched', fromId, toId: threadId });
1903
+ }
1904
+ return success;
1905
+ }
1906
+ /**
1907
+ * Get all threads.
1908
+ */
1909
+ getAllThreads() {
1910
+ if (!this.threadManager) {
1911
+ return [];
1912
+ }
1913
+ return this.threadManager.getAllThreads();
1914
+ }
1915
+ /**
1916
+ * Get checkpoints for the current thread.
1917
+ */
1918
+ getCheckpoints() {
1919
+ if (!this.threadManager) {
1920
+ return [];
1921
+ }
1922
+ return this.threadManager.getThreadCheckpoints();
1923
+ }
1924
+ /**
1925
+ * Automatically create checkpoint if enabled in config.
1926
+ * Safe to call after each Q&A cycle - handles all checks internally.
1927
+ * @param force - If true, bypasses frequency check and always creates checkpoint
1928
+ * @returns The created checkpoint, or null if conditions not met
1929
+ */
1930
+ autoCheckpoint(force = false) {
1931
+ // Check if thread management is enabled
1932
+ if (!this.threadManager) {
1933
+ return null;
1934
+ }
1935
+ // Check if auto-checkpoint is enabled
1936
+ const threadsConfig = this.config.threads;
1937
+ if (!threadsConfig || typeof threadsConfig === 'boolean' || !threadsConfig.autoCheckpoint) {
1938
+ return null;
1939
+ }
1940
+ // Check frequency (every N iterations, default 5) - unless forced
1941
+ if (!force) {
1942
+ const frequency = threadsConfig.checkpointFrequency || 5;
1943
+ if (this.state.iteration % frequency !== 0) {
1944
+ return null;
1945
+ }
1946
+ }
1947
+ // Create the checkpoint
1948
+ const label = `auto-iter-${this.state.iteration}`;
1949
+ return this.createCheckpoint(label);
1950
+ }
1951
+ // =========================================================================
1952
+ // AGENT REGISTRY METHODS (Subagent Support)
1953
+ // =========================================================================
1954
+ /**
1955
+ * Get all registered agents (built-in + user-defined).
1956
+ */
1957
+ getAgents() {
1958
+ if (!this.agentRegistry) {
1959
+ return [];
1960
+ }
1961
+ return this.agentRegistry.getAllAgents();
1962
+ }
1963
+ /**
1964
+ * Get a specific agent by name.
1965
+ */
1966
+ getAgent(name) {
1967
+ return this.agentRegistry?.getAgent(name);
1968
+ }
1969
+ /**
1970
+ * Find agents matching a natural language query.
1971
+ * Use this for NL-based agent selection.
1972
+ */
1973
+ findAgentsForTask(query, limit = 3) {
1974
+ if (!this.agentRegistry) {
1975
+ return [];
1976
+ }
1977
+ return this.agentRegistry.findMatchingAgents(query, limit);
1978
+ }
1979
+ /**
1980
+ * Register a custom agent at runtime.
1981
+ */
1982
+ registerAgent(definition) {
1983
+ if (!this.agentRegistry) {
1984
+ throw new Error('Agent registry not initialized');
1985
+ }
1986
+ this.agentRegistry.registerAgent(definition);
1987
+ this.emit({ type: 'agent.registered', name: definition.name });
1988
+ this.observability?.logger?.info('Agent registered', { name: definition.name });
1989
+ }
1990
+ /**
1991
+ * Unregister an agent.
1992
+ */
1993
+ unregisterAgent(name) {
1994
+ if (!this.agentRegistry) {
1995
+ return false;
1996
+ }
1997
+ const success = this.agentRegistry.unregisterAgent(name);
1998
+ if (success) {
1999
+ this.emit({ type: 'agent.unregistered', name });
2000
+ }
2001
+ return success;
2002
+ }
2003
+ /**
2004
+ * Spawn an agent to execute a task.
2005
+ * Returns the result when the agent completes.
2006
+ */
2007
+ async spawnAgent(agentName, task) {
2008
+ if (!this.agentRegistry) {
2009
+ return {
2010
+ success: false,
2011
+ output: 'Agent registry not initialized',
2012
+ metrics: { tokens: 0, duration: 0, toolCalls: 0 },
2013
+ };
2014
+ }
2015
+ const agentDef = this.agentRegistry.getAgent(agentName);
2016
+ if (!agentDef) {
2017
+ return {
2018
+ success: false,
2019
+ output: `Agent not found: ${agentName}`,
2020
+ metrics: { tokens: 0, duration: 0, toolCalls: 0 },
2021
+ };
2022
+ }
2023
+ this.emit({ type: 'agent.spawn', agentId: `spawn-${Date.now()}`, name: agentName, task });
2024
+ this.observability?.logger?.info('Spawning agent', { name: agentName, task });
2025
+ const startTime = Date.now();
2026
+ try {
2027
+ // Filter tools for this agent
2028
+ const agentTools = filterToolsForAgent(agentDef, Array.from(this.tools.values()));
2029
+ // Resolve model - abstract tiers (fast/balanced/quality) should use parent's model
2030
+ // Only use agentDef.model if it's an actual model ID (contains '/')
2031
+ const resolvedModel = (agentDef.model && agentDef.model.includes('/'))
2032
+ ? agentDef.model
2033
+ : this.config.model;
2034
+ // Create a sub-agent with the agent's config
2035
+ const subAgent = new ProductionAgent({
2036
+ provider: this.provider,
2037
+ tools: agentTools,
2038
+ systemPrompt: agentDef.systemPrompt,
2039
+ model: resolvedModel,
2040
+ maxIterations: agentDef.maxIterations || 30,
2041
+ // Inherit some features but keep subagent simpler
2042
+ memory: false,
2043
+ planning: false,
2044
+ reflection: false,
2045
+ observability: this.config.observability,
2046
+ sandbox: this.config.sandbox,
2047
+ humanInLoop: this.config.humanInLoop,
2048
+ executionPolicy: this.config.executionPolicy,
2049
+ threads: false,
2050
+ // Disable hooks console output in subagents - parent handles event display
2051
+ hooks: this.config.hooks === false ? false : {
2052
+ enabled: true,
2053
+ builtIn: { logging: false, timing: false, metrics: false },
2054
+ custom: [],
2055
+ },
2056
+ });
2057
+ // Forward events from subagent
2058
+ subAgent.subscribe(event => {
2059
+ // Just forward the event as-is - the agent.spawn event already logged the agent name
2060
+ this.emit(event);
2061
+ });
2062
+ // Run the task
2063
+ const result = await subAgent.run(task);
2064
+ const duration = Date.now() - startTime;
2065
+ const spawnResult = {
2066
+ success: result.success,
2067
+ output: result.response || result.error || '',
2068
+ metrics: {
2069
+ tokens: result.metrics.totalTokens,
2070
+ duration,
2071
+ toolCalls: result.metrics.toolCalls,
2072
+ },
2073
+ };
2074
+ this.emit({ type: 'agent.complete', agentId: agentName, success: result.success });
2075
+ await subAgent.cleanup();
2076
+ return spawnResult;
2077
+ }
2078
+ catch (err) {
2079
+ const error = err instanceof Error ? err.message : String(err);
2080
+ this.emit({ type: 'agent.error', agentId: agentName, error });
2081
+ return {
2082
+ success: false,
2083
+ output: `Agent error: ${error}`,
2084
+ metrics: { tokens: 0, duration: Date.now() - startTime, toolCalls: 0 },
2085
+ };
2086
+ }
2087
+ }
2088
+ /**
2089
+ * Get a formatted list of available agents.
2090
+ */
2091
+ formatAgentList() {
2092
+ if (!this.agentRegistry) {
2093
+ return 'No agents available';
2094
+ }
2095
+ return formatAgentList(this.agentRegistry.getAllAgents());
2096
+ }
2097
+ // =========================================================================
2098
+ // NL ROUTING METHODS (Intelligent Agent Selection)
2099
+ // =========================================================================
2100
+ /**
2101
+ * Use LLM to suggest the best agent(s) for a given task.
2102
+ * Returns ranked suggestions with confidence scores.
2103
+ */
2104
+ async suggestAgentForTask(task) {
2105
+ if (!this.agentRegistry) {
2106
+ return { suggestions: [], shouldDelegate: false };
2107
+ }
2108
+ // First, get keyword-based matches
2109
+ const keywordMatches = this.agentRegistry.findMatchingAgents(task, 5);
2110
+ // If no LLM provider, fall back to keyword matching
2111
+ if (!this.provider) {
2112
+ return {
2113
+ suggestions: keywordMatches.map((agent, i) => ({
2114
+ agent,
2115
+ confidence: 0.9 - i * 0.15,
2116
+ reason: `Keyword match: ${agent.capabilities?.slice(0, 3).join(', ') || agent.description.split('.')[0]}`,
2117
+ })),
2118
+ shouldDelegate: keywordMatches.length > 0,
2119
+ delegateAgent: keywordMatches[0]?.name,
2120
+ };
2121
+ }
2122
+ // Build agent descriptions for LLM
2123
+ const agents = this.agentRegistry.getAllAgents();
2124
+ const agentDescriptions = agents.map(a => `- ${a.name}: ${a.description}${a.capabilities?.length ? ` (can: ${a.capabilities.join(', ')})` : ''}`).join('\n');
2125
+ // Ask LLM to classify the task
2126
+ const classificationPrompt = `You are a task routing assistant. Given a user task, determine which specialized agent (if any) should handle it.
2127
+
2128
+ Available agents:
2129
+ ${agentDescriptions}
2130
+
2131
+ User task: "${task}"
2132
+
2133
+ Respond in JSON format:
2134
+ {
2135
+ "analysis": "Brief analysis of what the task requires",
2136
+ "bestAgent": "agent name or null if main agent should handle it",
2137
+ "confidence": 0.0 to 1.0,
2138
+ "reason": "Why this agent is best suited",
2139
+ "alternatives": ["other suitable agents in order of preference"]
2140
+ }
2141
+
2142
+ If the task is a simple question or doesn't need specialized handling, set bestAgent to null.`;
2143
+ try {
2144
+ const response = await this.provider.chat([
2145
+ { role: 'system', content: 'You are a task routing classifier. Always respond with valid JSON.' },
2146
+ { role: 'user', content: classificationPrompt },
2147
+ ], {
2148
+ model: this.config.model,
2149
+ });
2150
+ // Parse the JSON response
2151
+ const jsonMatch = response.content.match(/\{[\s\S]*\}/);
2152
+ if (!jsonMatch) {
2153
+ // Fallback to keyword matching
2154
+ return {
2155
+ suggestions: keywordMatches.map((agent, i) => ({
2156
+ agent,
2157
+ confidence: 0.7 - i * 0.1,
2158
+ reason: 'Keyword-based match',
2159
+ })),
2160
+ shouldDelegate: false,
2161
+ };
2162
+ }
2163
+ const classification = JSON.parse(jsonMatch[0]);
2164
+ // Build suggestions
2165
+ const suggestions = [];
2166
+ if (classification.bestAgent) {
2167
+ const bestAgent = this.agentRegistry.getAgent(classification.bestAgent);
2168
+ if (bestAgent) {
2169
+ suggestions.push({
2170
+ agent: bestAgent,
2171
+ confidence: classification.confidence,
2172
+ reason: classification.reason,
2173
+ });
2174
+ }
2175
+ }
2176
+ // Add alternatives
2177
+ for (let i = 0; i < (classification.alternatives || []).length; i++) {
2178
+ const altName = classification.alternatives[i];
2179
+ const altAgent = this.agentRegistry.getAgent(altName);
2180
+ if (altAgent && !suggestions.find(s => s.agent.name === altName)) {
2181
+ suggestions.push({
2182
+ agent: altAgent,
2183
+ confidence: Math.max(0.3, classification.confidence - 0.2 - i * 0.1),
2184
+ reason: 'Alternative option',
2185
+ });
2186
+ }
2187
+ }
2188
+ // Determine if we should delegate
2189
+ const shouldDelegate = classification.confidence >= 0.7 && classification.bestAgent !== null;
2190
+ return {
2191
+ suggestions,
2192
+ shouldDelegate,
2193
+ delegateAgent: shouldDelegate ? classification.bestAgent || undefined : undefined,
2194
+ };
2195
+ }
2196
+ catch (error) {
2197
+ // On error, fall back to keyword matching
2198
+ this.observability?.logger?.warn('Agent suggestion LLM call failed', { error });
2199
+ return {
2200
+ suggestions: keywordMatches.map((agent, i) => ({
2201
+ agent,
2202
+ confidence: 0.6 - i * 0.1,
2203
+ reason: 'Keyword-based fallback',
2204
+ })),
2205
+ shouldDelegate: false,
2206
+ };
2207
+ }
2208
+ }
2209
+ /**
2210
+ * Run a task with automatic agent routing.
2211
+ * If a specialized agent is highly suited, delegates to it.
2212
+ * Otherwise runs with the main agent.
2213
+ */
2214
+ async runWithAutoRouting(task, options = {}) {
2215
+ const { confidenceThreshold = 0.8, confirmDelegate } = options;
2216
+ // Get agent suggestions
2217
+ const { suggestions, shouldDelegate, delegateAgent } = await this.suggestAgentForTask(task);
2218
+ // Check if we should delegate
2219
+ if (shouldDelegate && delegateAgent) {
2220
+ const topSuggestion = suggestions[0];
2221
+ // If confirmation callback provided, ask user
2222
+ if (confirmDelegate && topSuggestion) {
2223
+ const confirmed = await confirmDelegate(topSuggestion.agent, topSuggestion.reason);
2224
+ if (!confirmed) {
2225
+ // User declined, run with main agent
2226
+ return this.run(task);
2227
+ }
2228
+ }
2229
+ // Only auto-delegate if confidence exceeds threshold
2230
+ if (topSuggestion && topSuggestion.confidence >= confidenceThreshold) {
2231
+ this.emit({
2232
+ type: 'agent.spawn',
2233
+ agentId: `auto-${Date.now()}`,
2234
+ name: delegateAgent,
2235
+ task,
2236
+ });
2237
+ return this.spawnAgent(delegateAgent, task);
2238
+ }
2239
+ }
2240
+ // Run with main agent
2241
+ return this.run(task);
2242
+ }
2243
+ // =========================================================================
2244
+ // CANCELLATION METHODS
2245
+ // =========================================================================
2246
+ /**
2247
+ * Request cancellation of the current operation.
2248
+ * The agent will attempt to stop gracefully.
2249
+ */
2250
+ cancel(reason) {
2251
+ if (!this.cancellation) {
2252
+ console.warn('[ProductionAgent] Cancellation not enabled');
2253
+ return;
2254
+ }
2255
+ this.cancellation.cancel(reason);
2256
+ this.state.status = 'paused';
2257
+ this.observability?.logger?.info('Cancellation requested', { reason });
2258
+ }
2259
+ /**
2260
+ * Check if cancellation has been requested.
2261
+ */
2262
+ isCancelled() {
2263
+ return this.cancellation?.isCancelled ?? false;
2264
+ }
2265
+ // =========================================================================
2266
+ // RESOURCE MONITORING METHODS
2267
+ // =========================================================================
2268
+ /**
2269
+ * Get current resource usage.
2270
+ */
2271
+ getResourceUsage() {
2272
+ return this.resourceManager?.getUsage() || null;
2273
+ }
2274
+ /**
2275
+ * Get formatted resource status string.
2276
+ */
2277
+ getResourceStatus() {
2278
+ return this.resourceManager?.getStatusString() || null;
2279
+ }
2280
+ // =========================================================================
2281
+ // LSP (LANGUAGE SERVER) METHODS
2282
+ // =========================================================================
2283
+ /**
2284
+ * Start LSP servers for the workspace.
2285
+ * Auto-detects languages based on project files.
2286
+ */
2287
+ async startLSP(workspaceRoot) {
2288
+ if (!this.lspManager)
2289
+ return [];
2290
+ return this.lspManager.autoStart(workspaceRoot);
2291
+ }
2292
+ /**
2293
+ * Get code definition location.
2294
+ */
2295
+ async getLSPDefinition(file, line, col) {
2296
+ return this.lspManager?.getDefinition(file, line, col) || null;
2297
+ }
2298
+ /**
2299
+ * Get code completions.
2300
+ */
2301
+ async getLSPCompletions(file, line, col) {
2302
+ return this.lspManager?.getCompletions(file, line, col) || [];
2303
+ }
2304
+ /**
2305
+ * Get hover documentation.
2306
+ */
2307
+ async getLSPHover(file, line, col) {
2308
+ return this.lspManager?.getHover(file, line, col) || null;
2309
+ }
2310
+ /**
2311
+ * Get all references to a symbol.
2312
+ */
2313
+ async getLSPReferences(file, line, col) {
2314
+ return this.lspManager?.getReferences(file, line, col) || [];
2315
+ }
2316
+ /**
2317
+ * Get active LSP servers.
2318
+ */
2319
+ getActiveLSPServers() {
2320
+ return this.lspManager?.getActiveServers() || [];
2321
+ }
2322
+ /**
2323
+ * Get LSP diagnostics for a file.
2324
+ */
2325
+ getLSPDiagnostics(file) {
2326
+ return this.lspManager?.getDiagnostics(file) || [];
2327
+ }
2328
+ /**
2329
+ * Get the LSP manager instance (for advanced use).
2330
+ */
2331
+ getLSPManager() {
2332
+ return this.lspManager;
2333
+ }
2334
+ /**
2335
+ * Get LSP-aware file tools.
2336
+ * These tools provide diagnostic feedback after edit/write operations.
2337
+ * Returns the tools if LSP is enabled, empty array otherwise.
2338
+ */
2339
+ getLSPFileTools(options) {
2340
+ if (!this.lspManager) {
2341
+ return [];
2342
+ }
2343
+ return createLSPFileTools({
2344
+ lspManager: this.lspManager,
2345
+ diagnosticDelay: options?.diagnosticDelay ?? 500,
2346
+ includeWarnings: options?.includeWarnings ?? true,
2347
+ });
2348
+ }
2349
+ /**
2350
+ * Replace standard file tools with LSP-aware versions.
2351
+ * Call this after enabling LSP to get diagnostic feedback on edits.
2352
+ */
2353
+ enableLSPFileTools(options) {
2354
+ if (!this.lspManager) {
2355
+ console.warn('[ProductionAgent] LSP not enabled, cannot enable LSP file tools');
2356
+ return;
2357
+ }
2358
+ const lspTools = this.getLSPFileTools(options);
2359
+ for (const tool of lspTools) {
2360
+ this.tools.set(tool.name, tool);
2361
+ }
2362
+ this.observability?.logger?.info('LSP file tools enabled', {
2363
+ tools: lspTools.map(t => t.name),
2364
+ });
2365
+ }
2366
+ // =========================================================================
2367
+ // SEMANTIC CACHE METHODS
2368
+ // =========================================================================
2369
+ /**
2370
+ * Check if a cached response exists for a similar query.
2371
+ */
2372
+ async getCachedResponse(query) {
2373
+ const hit = await this.semanticCache?.get(query);
2374
+ if (hit) {
2375
+ return { response: hit.entry.response, similarity: hit.similarity };
2376
+ }
2377
+ return null;
2378
+ }
2379
+ /**
2380
+ * Cache an LLM response for a query.
2381
+ */
2382
+ async cacheResponse(query, response, metadata) {
2383
+ return await this.semanticCache?.set(query, response, metadata) || null;
2384
+ }
2385
+ /**
2386
+ * Check if a similar query exists in cache (without retrieving).
2387
+ */
2388
+ async hasCachedQuery(query) {
2389
+ return await this.semanticCache?.has(query) || false;
2390
+ }
2391
+ /**
2392
+ * Get cache statistics.
2393
+ */
2394
+ getCacheStats() {
2395
+ return this.semanticCache?.getStats() || { size: 0, totalHits: 0, avgHits: 0, hitRate: 0, totalQueries: 0 };
2396
+ }
2397
+ /**
2398
+ * Clear the semantic cache.
2399
+ */
2400
+ clearCache() {
2401
+ this.semanticCache?.clear();
2402
+ }
2403
+ // =========================================================================
2404
+ // MODE MANAGEMENT METHODS
2405
+ // =========================================================================
2406
+ /**
2407
+ * Get the current agent mode.
2408
+ */
2409
+ getMode() {
2410
+ return this.modeManager.getMode();
2411
+ }
2412
+ /**
2413
+ * Set the agent mode.
2414
+ */
2415
+ setMode(mode) {
2416
+ const parsed = typeof mode === 'string' ? parseMode(mode) : mode;
2417
+ if (parsed) {
2418
+ this.modeManager.setMode(parsed);
2419
+ this.emit({ type: 'mode.changed', from: this.getMode(), to: parsed });
2420
+ }
2421
+ }
2422
+ /**
2423
+ * Cycle to the next mode (for Tab key).
2424
+ */
2425
+ cycleMode() {
2426
+ return this.modeManager.cycleMode();
2427
+ }
2428
+ /**
2429
+ * Get all registered tools.
2430
+ */
2431
+ getTools() {
2432
+ return Array.from(this.tools.values());
2433
+ }
2434
+ /**
2435
+ * Get available tools filtered by current mode.
2436
+ */
2437
+ getModeFilteredTools() {
2438
+ return this.modeManager.filterTools(Array.from(this.tools.values()));
2439
+ }
2440
+ /**
2441
+ * Get mode info for display.
2442
+ */
2443
+ getModeInfo() {
2444
+ return this.modeManager.getModeInfo();
2445
+ }
2446
+ /**
2447
+ * Format mode for terminal prompt.
2448
+ */
2449
+ formatModePrompt() {
2450
+ return this.modeManager.formatModePrompt();
2451
+ }
2452
+ /**
2453
+ * Get list of all available modes.
2454
+ */
2455
+ getAvailableModes() {
2456
+ return formatModeList();
2457
+ }
2458
+ /**
2459
+ * Get system prompt with mode-specific additions.
2460
+ */
2461
+ getSystemPromptWithMode() {
2462
+ const base = this.config.systemPrompt;
2463
+ const modeAddition = this.modeManager.getSystemPromptAddition();
2464
+ return `${base}\n\n${modeAddition}`;
2465
+ }
2466
+ /**
2467
+ * Toggle between build and plan modes.
2468
+ */
2469
+ togglePlanMode() {
2470
+ return this.modeManager.togglePlanMode();
2471
+ }
2472
+ // =========================================================================
2473
+ // PENDING PLAN METHODS (Plan Mode)
2474
+ // =========================================================================
2475
+ /**
2476
+ * Get the current pending plan.
2477
+ */
2478
+ getPendingPlan() {
2479
+ return this.pendingPlanManager.getPendingPlan();
2480
+ }
2481
+ /**
2482
+ * Check if there's a pending plan awaiting approval.
2483
+ */
2484
+ hasPendingPlan() {
2485
+ return this.pendingPlanManager.hasPendingPlan();
2486
+ }
2487
+ /**
2488
+ * Get formatted plan for display.
2489
+ */
2490
+ formatPendingPlan() {
2491
+ return this.pendingPlanManager.formatPlan();
2492
+ }
2493
+ /**
2494
+ * Approve the pending plan and execute the changes.
2495
+ * @param count - If provided, only approve first N changes
2496
+ * @returns Result of executing the approved changes
2497
+ */
2498
+ async approvePlan(count) {
2499
+ const result = this.pendingPlanManager.approve(count);
2500
+ if (result.changes.length === 0) {
2501
+ return { success: true, executed: 0, errors: [] };
2502
+ }
2503
+ // Switch to build mode for execution
2504
+ const previousMode = this.getMode();
2505
+ this.setMode('build');
2506
+ this.emit({ type: 'plan.approved', changeCount: result.changes.length });
2507
+ const errors = [];
2508
+ let executed = 0;
2509
+ // Execute each change
2510
+ for (let i = 0; i < result.changes.length; i++) {
2511
+ const change = result.changes[i];
2512
+ this.emit({ type: 'plan.executing', changeIndex: i, totalChanges: result.changes.length });
2513
+ try {
2514
+ const tool = this.tools.get(change.tool);
2515
+ if (!tool) {
2516
+ errors.push(`Unknown tool: ${change.tool}`);
2517
+ continue;
2518
+ }
2519
+ await tool.execute(change.args);
2520
+ executed++;
2521
+ }
2522
+ catch (err) {
2523
+ const error = err instanceof Error ? err.message : String(err);
2524
+ errors.push(`${change.tool}: ${error}`);
2525
+ }
2526
+ }
2527
+ // Restore previous mode if it wasn't build
2528
+ if (previousMode !== 'build' && previousMode !== 'plan') {
2529
+ this.setMode(previousMode);
2530
+ }
2531
+ return {
2532
+ success: errors.length === 0,
2533
+ executed,
2534
+ errors,
2535
+ };
2536
+ }
2537
+ /**
2538
+ * Reject the pending plan and discard all proposed changes.
2539
+ */
2540
+ rejectPlan() {
2541
+ this.pendingPlanManager.reject();
2542
+ this.emit({ type: 'plan.rejected' });
2543
+ }
2544
+ /**
2545
+ * Clear the pending plan without emitting rejection event.
2546
+ */
2547
+ clearPlan() {
2548
+ this.pendingPlanManager.clear();
2549
+ }
2550
+ /**
2551
+ * Get the number of pending changes.
2552
+ */
2553
+ getPendingChangeCount() {
2554
+ return this.pendingPlanManager.getChangeCount();
2555
+ }
2556
+ // =========================================================================
2557
+ // SKILLS METHODS
2558
+ // =========================================================================
2559
+ /**
2560
+ * Get all loaded skills.
2561
+ */
2562
+ getSkills() {
2563
+ return this.skillManager?.getAllSkills() || [];
2564
+ }
2565
+ /**
2566
+ * Get a specific skill by name.
2567
+ */
2568
+ getSkill(name) {
2569
+ return this.skillManager?.getSkill(name);
2570
+ }
2571
+ /**
2572
+ * Activate a skill by name.
2573
+ */
2574
+ activateSkill(name) {
2575
+ if (!this.skillManager)
2576
+ return false;
2577
+ return this.skillManager.activateSkill(name);
2578
+ }
2579
+ /**
2580
+ * Deactivate a skill by name.
2581
+ */
2582
+ deactivateSkill(name) {
2583
+ if (!this.skillManager)
2584
+ return false;
2585
+ return this.skillManager.deactivateSkill(name);
2586
+ }
2587
+ /**
2588
+ * Get currently active skills.
2589
+ */
2590
+ getActiveSkills() {
2591
+ return this.skillManager?.getActiveSkills() || [];
2592
+ }
2593
+ /**
2594
+ * Check if a skill is active.
2595
+ */
2596
+ isSkillActive(name) {
2597
+ return this.skillManager?.isSkillActive(name) || false;
2598
+ }
2599
+ /**
2600
+ * Find skills matching a query (for auto-activation).
2601
+ */
2602
+ findMatchingSkills(query) {
2603
+ return this.skillManager?.findMatchingSkills(query) || [];
2604
+ }
2605
+ /**
2606
+ * Get formatted list of available skills.
2607
+ */
2608
+ formatSkillList() {
2609
+ if (!this.skillManager)
2610
+ return 'Skills not enabled';
2611
+ return formatSkillList(this.skillManager.getAllSkills());
2612
+ }
2613
+ /**
2614
+ * Cleanup resources.
2615
+ */
2616
+ async cleanup() {
2617
+ this.cancellation?.cleanup();
2618
+ this.resourceManager?.cleanup();
2619
+ await this.lspManager?.cleanup();
2620
+ this.semanticCache?.cleanup();
2621
+ this.skillManager?.cleanup();
2622
+ await this.hooks?.cleanup();
2623
+ this.rules?.cleanup();
2624
+ this.agentRegistry?.cleanup();
2625
+ this.observability?.logger?.info('Agent cleanup complete');
2626
+ }
2627
+ }
2628
+ // =============================================================================
2629
+ // FACTORY
2630
+ // =============================================================================
2631
+ /**
2632
+ * Create a production agent with the given configuration.
2633
+ */
2634
+ export function createProductionAgent(config) {
2635
+ return new ProductionAgent(config);
2636
+ }
2637
+ // =============================================================================
2638
+ // BUILDER PATTERN
2639
+ // =============================================================================
2640
+ /**
2641
+ * Builder for creating customized production agents.
2642
+ */
2643
+ export class ProductionAgentBuilder {
2644
+ config = {};
2645
+ /**
2646
+ * Set the LLM provider.
2647
+ */
2648
+ provider(provider) {
2649
+ this.config.provider = provider;
2650
+ return this;
2651
+ }
2652
+ /**
2653
+ * Set the model.
2654
+ */
2655
+ model(model) {
2656
+ this.config.model = model;
2657
+ return this;
2658
+ }
2659
+ /**
2660
+ * Set the system prompt.
2661
+ */
2662
+ systemPrompt(prompt) {
2663
+ this.config.systemPrompt = prompt;
2664
+ return this;
2665
+ }
2666
+ /**
2667
+ * Add tools.
2668
+ */
2669
+ tools(tools) {
2670
+ this.config.tools = tools;
2671
+ return this;
2672
+ }
2673
+ /**
2674
+ * Configure hooks.
2675
+ */
2676
+ hooks(config) {
2677
+ this.config.hooks = config;
2678
+ return this;
2679
+ }
2680
+ /**
2681
+ * Configure plugins.
2682
+ */
2683
+ plugins(config) {
2684
+ this.config.plugins = config;
2685
+ return this;
2686
+ }
2687
+ /**
2688
+ * Configure memory.
2689
+ */
2690
+ memory(config) {
2691
+ this.config.memory = config;
2692
+ return this;
2693
+ }
2694
+ /**
2695
+ * Configure planning.
2696
+ */
2697
+ planning(config) {
2698
+ this.config.planning = config;
2699
+ return this;
2700
+ }
2701
+ /**
2702
+ * Configure reflection.
2703
+ */
2704
+ reflection(config) {
2705
+ this.config.reflection = config;
2706
+ return this;
2707
+ }
2708
+ /**
2709
+ * Configure observability.
2710
+ */
2711
+ observability(config) {
2712
+ this.config.observability = config;
2713
+ return this;
2714
+ }
2715
+ /**
2716
+ * Configure sandbox.
2717
+ */
2718
+ sandbox(config) {
2719
+ this.config.sandbox = config;
2720
+ return this;
2721
+ }
2722
+ /**
2723
+ * Configure human-in-the-loop.
2724
+ */
2725
+ humanInLoop(config) {
2726
+ this.config.humanInLoop = config;
2727
+ return this;
2728
+ }
2729
+ /**
2730
+ * Configure routing.
2731
+ */
2732
+ routing(config) {
2733
+ this.config.routing = config;
2734
+ return this;
2735
+ }
2736
+ /**
2737
+ * Configure multi-agent coordination (Lesson 17).
2738
+ */
2739
+ multiAgent(config) {
2740
+ this.config.multiAgent = config;
2741
+ return this;
2742
+ }
2743
+ /**
2744
+ * Add a role to multi-agent config.
2745
+ */
2746
+ addRole(role) {
2747
+ // Handle undefined, false, or disabled config
2748
+ if (!this.config.multiAgent) {
2749
+ this.config.multiAgent = { enabled: true, roles: [] };
2750
+ }
2751
+ // Ensure roles array exists
2752
+ const multiAgentConfig = this.config.multiAgent;
2753
+ if (!multiAgentConfig.roles) {
2754
+ multiAgentConfig.roles = [];
2755
+ }
2756
+ multiAgentConfig.roles.push(role);
2757
+ return this;
2758
+ }
2759
+ /**
2760
+ * Configure ReAct pattern (Lesson 18).
2761
+ */
2762
+ react(config) {
2763
+ this.config.react = config;
2764
+ return this;
2765
+ }
2766
+ /**
2767
+ * Configure execution policies (Lesson 23).
2768
+ */
2769
+ executionPolicy(config) {
2770
+ this.config.executionPolicy = config;
2771
+ return this;
2772
+ }
2773
+ /**
2774
+ * Configure thread management (Lesson 24).
2775
+ */
2776
+ threads(config) {
2777
+ this.config.threads = config;
2778
+ return this;
2779
+ }
2780
+ /**
2781
+ * Configure skills system.
2782
+ */
2783
+ skills(config) {
2784
+ this.config.skills = config;
2785
+ return this;
2786
+ }
2787
+ /**
2788
+ * Set max iterations.
2789
+ */
2790
+ maxIterations(max) {
2791
+ this.config.maxIterations = max;
2792
+ return this;
2793
+ }
2794
+ /**
2795
+ * Set timeout.
2796
+ */
2797
+ timeout(ms) {
2798
+ this.config.timeout = ms;
2799
+ return this;
2800
+ }
2801
+ /**
2802
+ * Disable a feature.
2803
+ */
2804
+ disable(feature) {
2805
+ this.config[feature] = false;
2806
+ return this;
2807
+ }
2808
+ /**
2809
+ * Build the agent.
2810
+ */
2811
+ build() {
2812
+ if (!this.config.provider) {
2813
+ throw new Error('Provider is required');
2814
+ }
2815
+ return new ProductionAgent(this.config);
2816
+ }
2817
+ }
2818
+ /**
2819
+ * Start building a production agent.
2820
+ */
2821
+ export function buildAgent() {
2822
+ return new ProductionAgentBuilder();
2823
+ }
2824
+ //# sourceMappingURL=agent.js.map