@dexto/core 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (523) hide show
  1. package/README.md +60 -0
  2. package/dist/agent/DextoAgent.cjs +579 -345
  3. package/dist/agent/DextoAgent.d.ts +131 -83
  4. package/dist/agent/DextoAgent.d.ts.map +1 -1
  5. package/dist/agent/DextoAgent.js +573 -336
  6. package/dist/agent/agentCard.cjs +4 -2
  7. package/dist/agent/agentCard.d.ts +0 -1
  8. package/dist/agent/agentCard.d.ts.map +1 -1
  9. package/dist/agent/agentCard.js +4 -2
  10. package/dist/agent/index.cjs +3 -7
  11. package/dist/agent/index.d.ts +3 -3
  12. package/dist/agent/index.d.ts.map +1 -1
  13. package/dist/agent/index.js +7 -6
  14. package/dist/agent/schemas.cjs +164 -64
  15. package/dist/agent/schemas.d.ts +2605 -517
  16. package/dist/agent/schemas.d.ts.map +1 -1
  17. package/dist/agent/schemas.js +167 -64
  18. package/dist/agent/state-manager.cjs +28 -23
  19. package/dist/agent/state-manager.d.ts +4 -1
  20. package/dist/agent/state-manager.d.ts.map +1 -1
  21. package/dist/agent/state-manager.js +28 -23
  22. package/dist/{preferences/constants.cjs → agent/types.cjs} +2 -14
  23. package/dist/agent/types.d.ts +54 -0
  24. package/dist/agent/types.d.ts.map +1 -0
  25. package/dist/agent/types.js +0 -0
  26. package/dist/approval/errors.cjs +89 -8
  27. package/dist/approval/errors.d.ts +5 -3
  28. package/dist/approval/errors.d.ts.map +1 -1
  29. package/dist/approval/errors.js +89 -8
  30. package/dist/approval/{providers/factory.d.ts → factory.d.ts} +2 -2
  31. package/dist/approval/factory.d.ts.map +1 -0
  32. package/dist/approval/{providers/factory.js → factory.js} +1 -1
  33. package/dist/approval/index.cjs +4 -6
  34. package/dist/approval/index.d.ts +3 -5
  35. package/dist/approval/index.d.ts.map +1 -1
  36. package/dist/approval/index.js +4 -5
  37. package/dist/approval/manager.cjs +140 -37
  38. package/dist/approval/manager.d.ts +56 -17
  39. package/dist/approval/manager.d.ts.map +1 -1
  40. package/dist/approval/manager.js +141 -38
  41. package/dist/approval/schemas.cjs +9 -1
  42. package/dist/approval/schemas.d.ts +120 -35
  43. package/dist/approval/schemas.d.ts.map +1 -1
  44. package/dist/approval/schemas.js +9 -2
  45. package/dist/approval/types.cjs +14 -2
  46. package/dist/approval/types.d.ts +64 -12
  47. package/dist/approval/types.d.ts.map +1 -1
  48. package/dist/approval/types.js +12 -1
  49. package/dist/context/compression/middle-removal.cjs +11 -11
  50. package/dist/context/compression/middle-removal.d.ts +3 -1
  51. package/dist/context/compression/middle-removal.d.ts.map +1 -1
  52. package/dist/context/compression/middle-removal.js +11 -11
  53. package/dist/context/compression/oldest-removal.cjs +18 -5
  54. package/dist/context/compression/oldest-removal.d.ts +3 -1
  55. package/dist/context/compression/oldest-removal.d.ts.map +1 -1
  56. package/dist/context/compression/oldest-removal.js +18 -5
  57. package/dist/context/manager.cjs +94 -67
  58. package/dist/context/manager.d.ts +13 -10
  59. package/dist/context/manager.d.ts.map +1 -1
  60. package/dist/context/manager.js +94 -67
  61. package/dist/context/utils.cjs +79 -65
  62. package/dist/context/utils.d.ts +15 -12
  63. package/dist/context/utils.d.ts.map +1 -1
  64. package/dist/context/utils.js +45 -31
  65. package/dist/errors/DextoRuntimeError.d.ts +5 -5
  66. package/dist/errors/DextoRuntimeError.d.ts.map +1 -1
  67. package/dist/errors/result-bridge.cjs +2 -3
  68. package/dist/errors/result-bridge.d.ts +5 -3
  69. package/dist/errors/result-bridge.d.ts.map +1 -1
  70. package/dist/errors/result-bridge.js +1 -2
  71. package/dist/errors/types.cjs +1 -2
  72. package/dist/errors/types.d.ts +5 -8
  73. package/dist/errors/types.d.ts.map +1 -1
  74. package/dist/errors/types.js +1 -2
  75. package/dist/events/index.cjs +125 -55
  76. package/dist/events/index.d.ts +204 -97
  77. package/dist/events/index.d.ts.map +1 -1
  78. package/dist/events/index.js +123 -55
  79. package/dist/filesystem/filesystem-service.cjs +40 -30
  80. package/dist/filesystem/filesystem-service.d.ts +9 -1
  81. package/dist/filesystem/filesystem-service.d.ts.map +1 -1
  82. package/dist/filesystem/filesystem-service.js +40 -30
  83. package/dist/filesystem/path-validator.cjs +4 -3
  84. package/dist/filesystem/path-validator.d.ts +3 -1
  85. package/dist/filesystem/path-validator.d.ts.map +1 -1
  86. package/dist/filesystem/path-validator.js +4 -3
  87. package/dist/filesystem/types.d.ts +3 -3
  88. package/dist/filesystem/types.d.ts.map +1 -1
  89. package/dist/index.browser.cjs +7 -0
  90. package/dist/index.browser.d.ts +2 -0
  91. package/dist/index.browser.d.ts.map +1 -1
  92. package/dist/index.browser.js +4 -0
  93. package/dist/index.cjs +0 -7
  94. package/dist/index.d.ts +12 -3
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +0 -4
  97. package/dist/llm/formatters/anthropic.cjs +32 -21
  98. package/dist/llm/formatters/anthropic.d.ts +3 -0
  99. package/dist/llm/formatters/anthropic.d.ts.map +1 -1
  100. package/dist/llm/formatters/anthropic.js +32 -21
  101. package/dist/llm/formatters/factory.cjs +6 -7
  102. package/dist/llm/formatters/factory.d.ts +2 -1
  103. package/dist/llm/formatters/factory.d.ts.map +1 -1
  104. package/dist/llm/formatters/factory.js +4 -5
  105. package/dist/llm/formatters/openai.cjs +38 -9
  106. package/dist/llm/formatters/openai.d.ts +3 -0
  107. package/dist/llm/formatters/openai.d.ts.map +1 -1
  108. package/dist/llm/formatters/openai.js +38 -9
  109. package/dist/llm/formatters/vercel.cjs +49 -8
  110. package/dist/llm/formatters/vercel.d.ts +3 -0
  111. package/dist/llm/formatters/vercel.d.ts.map +1 -1
  112. package/dist/llm/formatters/vercel.js +49 -8
  113. package/dist/llm/registry.cjs +153 -17
  114. package/dist/llm/registry.d.ts +5 -2
  115. package/dist/llm/registry.d.ts.map +1 -1
  116. package/dist/llm/registry.js +143 -7
  117. package/dist/llm/resolver.cjs +4 -4
  118. package/dist/llm/resolver.d.ts +3 -2
  119. package/dist/llm/resolver.d.ts.map +1 -1
  120. package/dist/llm/resolver.js +4 -4
  121. package/dist/llm/schemas.cjs +6 -3
  122. package/dist/llm/schemas.d.ts +51 -17
  123. package/dist/llm/schemas.d.ts.map +1 -1
  124. package/dist/llm/schemas.js +5 -3
  125. package/dist/llm/services/anthropic.cjs +216 -183
  126. package/dist/llm/services/anthropic.d.ts +3 -1
  127. package/dist/llm/services/anthropic.d.ts.map +1 -1
  128. package/dist/llm/services/anthropic.js +217 -184
  129. package/dist/llm/services/factory.cjs +15 -9
  130. package/dist/llm/services/factory.d.ts +2 -1
  131. package/dist/llm/services/factory.d.ts.map +1 -1
  132. package/dist/llm/services/factory.js +15 -9
  133. package/dist/llm/services/openai.cjs +262 -225
  134. package/dist/llm/services/openai.d.ts +3 -1
  135. package/dist/llm/services/openai.d.ts.map +1 -1
  136. package/dist/llm/services/openai.js +263 -226
  137. package/dist/llm/services/test-utils.integration.cjs +58 -12
  138. package/dist/llm/services/test-utils.integration.d.ts.map +1 -1
  139. package/dist/llm/services/test-utils.integration.js +58 -12
  140. package/dist/llm/services/types.d.ts +9 -0
  141. package/dist/llm/services/types.d.ts.map +1 -1
  142. package/dist/llm/services/vercel.cjs +163 -111
  143. package/dist/llm/services/vercel.d.ts +3 -1
  144. package/dist/llm/services/vercel.d.ts.map +1 -1
  145. package/dist/llm/services/vercel.js +157 -105
  146. package/dist/llm/tokenizer/factory.cjs +2 -2
  147. package/dist/llm/tokenizer/factory.d.ts +3 -1
  148. package/dist/llm/tokenizer/factory.d.ts.map +1 -1
  149. package/dist/llm/tokenizer/factory.js +2 -2
  150. package/dist/llm/tokenizer/openai.cjs +16 -9
  151. package/dist/llm/tokenizer/openai.d.ts +4 -1
  152. package/dist/llm/tokenizer/openai.d.ts.map +1 -1
  153. package/dist/llm/tokenizer/openai.js +16 -9
  154. package/dist/llm/validation.cjs +8 -9
  155. package/dist/llm/validation.d.ts +3 -1
  156. package/dist/llm/validation.d.ts.map +1 -1
  157. package/dist/llm/validation.js +5 -6
  158. package/dist/logger/factory.cjs +54 -0
  159. package/dist/logger/factory.d.ts +36 -0
  160. package/dist/logger/factory.d.ts.map +1 -0
  161. package/dist/logger/factory.js +31 -0
  162. package/dist/logger/index.cjs +42 -3
  163. package/dist/logger/index.d.ts +17 -1
  164. package/dist/logger/index.d.ts.map +1 -1
  165. package/dist/logger/index.js +26 -1
  166. package/dist/logger/logger.cjs +30 -17
  167. package/dist/logger/logger.d.ts.map +1 -1
  168. package/dist/logger/logger.js +30 -17
  169. package/dist/logger/v2/dexto-logger.cjs +141 -0
  170. package/dist/logger/v2/dexto-logger.d.ts +54 -0
  171. package/dist/logger/v2/dexto-logger.d.ts.map +1 -0
  172. package/dist/logger/v2/dexto-logger.js +118 -0
  173. package/dist/{preferences → logger/v2}/error-codes.cjs +11 -10
  174. package/dist/logger/v2/error-codes.d.ts +13 -0
  175. package/dist/logger/v2/error-codes.d.ts.map +1 -0
  176. package/dist/logger/v2/error-codes.js +13 -0
  177. package/dist/logger/v2/errors.cjs +107 -0
  178. package/dist/logger/v2/errors.d.ts +32 -0
  179. package/dist/logger/v2/errors.d.ts.map +1 -0
  180. package/dist/logger/v2/errors.js +84 -0
  181. package/dist/logger/v2/schemas.cjs +57 -0
  182. package/dist/logger/v2/schemas.d.ts +147 -0
  183. package/dist/logger/v2/schemas.d.ts.map +1 -0
  184. package/dist/logger/v2/schemas.js +33 -0
  185. package/dist/logger/v2/transport-factory.cjs +53 -0
  186. package/dist/logger/v2/transport-factory.d.ts +21 -0
  187. package/dist/logger/v2/transport-factory.d.ts.map +1 -0
  188. package/dist/logger/v2/transport-factory.js +29 -0
  189. package/dist/logger/v2/transports/console-transport.cjs +79 -0
  190. package/dist/logger/v2/transports/console-transport.d.ts +23 -0
  191. package/dist/logger/v2/transports/console-transport.d.ts.map +1 -0
  192. package/dist/logger/v2/transports/console-transport.js +46 -0
  193. package/dist/logger/v2/transports/file-transport.cjs +161 -0
  194. package/dist/logger/v2/transports/file-transport.d.ts +46 -0
  195. package/dist/logger/v2/transports/file-transport.d.ts.map +1 -0
  196. package/dist/logger/v2/transports/file-transport.js +128 -0
  197. package/dist/logger/v2/types.cjs +49 -0
  198. package/dist/logger/v2/types.d.ts +123 -0
  199. package/dist/logger/v2/types.d.ts.map +1 -0
  200. package/dist/logger/v2/types.js +26 -0
  201. package/dist/mcp/manager.cjs +88 -78
  202. package/dist/mcp/manager.d.ts +3 -1
  203. package/dist/mcp/manager.d.ts.map +1 -1
  204. package/dist/mcp/manager.js +88 -78
  205. package/dist/mcp/mcp-client.cjs +109 -79
  206. package/dist/mcp/mcp-client.d.ts +3 -0
  207. package/dist/mcp/mcp-client.d.ts.map +1 -1
  208. package/dist/mcp/mcp-client.js +102 -72
  209. package/dist/memory/manager.cjs +9 -7
  210. package/dist/memory/manager.d.ts +3 -1
  211. package/dist/memory/manager.d.ts.map +1 -1
  212. package/dist/memory/manager.js +9 -7
  213. package/dist/memory/schemas.d.ts +6 -6
  214. package/dist/plugins/manager.cjs +21 -19
  215. package/dist/plugins/manager.d.ts +3 -1
  216. package/dist/plugins/manager.d.ts.map +1 -1
  217. package/dist/plugins/manager.js +21 -19
  218. package/dist/plugins/schemas.d.ts +9 -9
  219. package/dist/plugins/types.d.ts +2 -2
  220. package/dist/plugins/types.d.ts.map +1 -1
  221. package/dist/process/command-validator.cjs +30 -20
  222. package/dist/process/command-validator.d.ts +4 -1
  223. package/dist/process/command-validator.d.ts.map +1 -1
  224. package/dist/process/command-validator.js +30 -20
  225. package/dist/process/process-service.cjs +23 -21
  226. package/dist/process/process-service.d.ts +3 -1
  227. package/dist/process/process-service.d.ts.map +1 -1
  228. package/dist/process/process-service.js +23 -21
  229. package/dist/prompts/prompt-manager.cjs +25 -18
  230. package/dist/prompts/prompt-manager.d.ts +3 -1
  231. package/dist/prompts/prompt-manager.d.ts.map +1 -1
  232. package/dist/prompts/prompt-manager.js +25 -18
  233. package/dist/prompts/providers/custom-prompt-provider.cjs +11 -7
  234. package/dist/prompts/providers/custom-prompt-provider.d.ts +3 -1
  235. package/dist/prompts/providers/custom-prompt-provider.d.ts.map +1 -1
  236. package/dist/prompts/providers/custom-prompt-provider.js +11 -7
  237. package/dist/prompts/providers/file-prompt-provider.cjs +14 -12
  238. package/dist/prompts/providers/file-prompt-provider.d.ts +3 -1
  239. package/dist/prompts/providers/file-prompt-provider.d.ts.map +1 -1
  240. package/dist/prompts/providers/file-prompt-provider.js +14 -12
  241. package/dist/prompts/providers/mcp-prompt-provider.cjs +7 -6
  242. package/dist/prompts/providers/mcp-prompt-provider.d.ts +3 -1
  243. package/dist/prompts/providers/mcp-prompt-provider.d.ts.map +1 -1
  244. package/dist/prompts/providers/mcp-prompt-provider.js +7 -6
  245. package/dist/prompts/providers/starter-prompt-provider.cjs +7 -5
  246. package/dist/prompts/providers/starter-prompt-provider.d.ts +3 -1
  247. package/dist/prompts/providers/starter-prompt-provider.d.ts.map +1 -1
  248. package/dist/prompts/providers/starter-prompt-provider.js +7 -5
  249. package/dist/prompts/schemas.d.ts +3 -3
  250. package/dist/resources/handlers/blob-handler.cjs +15 -11
  251. package/dist/resources/handlers/blob-handler.d.ts +3 -1
  252. package/dist/resources/handlers/blob-handler.d.ts.map +1 -1
  253. package/dist/resources/handlers/blob-handler.js +15 -11
  254. package/dist/resources/handlers/factory.cjs +3 -3
  255. package/dist/resources/handlers/factory.d.ts +2 -1
  256. package/dist/resources/handlers/factory.d.ts.map +1 -1
  257. package/dist/resources/handlers/factory.js +3 -3
  258. package/dist/resources/handlers/filesystem-handler.cjs +10 -8
  259. package/dist/resources/handlers/filesystem-handler.d.ts +3 -1
  260. package/dist/resources/handlers/filesystem-handler.d.ts.map +1 -1
  261. package/dist/resources/handlers/filesystem-handler.js +10 -8
  262. package/dist/resources/internal-provider.cjs +28 -20
  263. package/dist/resources/internal-provider.d.ts +3 -1
  264. package/dist/resources/internal-provider.d.ts.map +1 -1
  265. package/dist/resources/internal-provider.js +28 -20
  266. package/dist/resources/manager.cjs +34 -25
  267. package/dist/resources/manager.d.ts +3 -1
  268. package/dist/resources/manager.d.ts.map +1 -1
  269. package/dist/resources/manager.js +34 -25
  270. package/dist/resources/schemas.d.ts +6 -6
  271. package/dist/search/search-service.cjs +8 -6
  272. package/dist/search/search-service.d.ts +3 -1
  273. package/dist/search/search-service.d.ts.map +1 -1
  274. package/dist/search/search-service.js +8 -6
  275. package/dist/session/chat-session.cjs +40 -27
  276. package/dist/session/chat-session.d.ts +10 -7
  277. package/dist/session/chat-session.d.ts.map +1 -1
  278. package/dist/session/chat-session.js +40 -27
  279. package/dist/session/history/database.cjs +18 -11
  280. package/dist/session/history/database.d.ts +3 -1
  281. package/dist/session/history/database.d.ts.map +1 -1
  282. package/dist/session/history/database.js +18 -11
  283. package/dist/session/history/factory.cjs +2 -2
  284. package/dist/session/history/factory.d.ts +5 -1
  285. package/dist/session/history/factory.d.ts.map +1 -1
  286. package/dist/session/history/factory.js +2 -2
  287. package/dist/session/session-manager.cjs +37 -53
  288. package/dist/session/session-manager.d.ts +3 -17
  289. package/dist/session/session-manager.d.ts.map +1 -1
  290. package/dist/session/session-manager.js +37 -53
  291. package/dist/session/title-generator.cjs +3 -2
  292. package/dist/session/title-generator.d.ts +2 -1
  293. package/dist/session/title-generator.d.ts.map +1 -1
  294. package/dist/session/title-generator.js +3 -2
  295. package/dist/storage/blob/factory.cjs +9 -18
  296. package/dist/storage/blob/factory.d.ts +5 -4
  297. package/dist/storage/blob/factory.d.ts.map +1 -1
  298. package/dist/storage/blob/factory.js +8 -17
  299. package/dist/storage/blob/local-blob-store.cjs +25 -32
  300. package/dist/storage/blob/local-blob-store.d.ts +3 -2
  301. package/dist/storage/blob/local-blob-store.d.ts.map +1 -1
  302. package/dist/storage/blob/local-blob-store.js +25 -32
  303. package/dist/storage/blob/memory-blob-store.cjs +326 -0
  304. package/dist/storage/blob/memory-blob-store.d.ts +66 -0
  305. package/dist/storage/blob/memory-blob-store.d.ts.map +1 -0
  306. package/dist/storage/blob/memory-blob-store.js +303 -0
  307. package/dist/storage/blob/schemas.cjs +3 -1
  308. package/dist/storage/blob/schemas.d.ts +6 -6
  309. package/dist/storage/blob/schemas.d.ts.map +1 -1
  310. package/dist/storage/blob/schemas.js +3 -1
  311. package/dist/storage/cache/factory.cjs +7 -8
  312. package/dist/storage/cache/factory.d.ts +4 -1
  313. package/dist/storage/cache/factory.d.ts.map +1 -1
  314. package/dist/storage/cache/factory.js +4 -5
  315. package/dist/storage/cache/redis-store.cjs +4 -1
  316. package/dist/storage/cache/redis-store.d.ts +3 -1
  317. package/dist/storage/cache/redis-store.d.ts.map +1 -1
  318. package/dist/storage/cache/redis-store.js +4 -1
  319. package/dist/storage/database/factory.cjs +13 -16
  320. package/dist/storage/database/factory.d.ts +5 -3
  321. package/dist/storage/database/factory.d.ts.map +1 -1
  322. package/dist/storage/database/factory.js +9 -12
  323. package/dist/storage/database/postgres-store.cjs +4 -1
  324. package/dist/storage/database/postgres-store.d.ts +3 -1
  325. package/dist/storage/database/postgres-store.d.ts.map +1 -1
  326. package/dist/storage/database/postgres-store.js +4 -1
  327. package/dist/storage/database/schemas.cjs +3 -4
  328. package/dist/storage/database/schemas.d.ts +8 -16
  329. package/dist/storage/database/schemas.d.ts.map +1 -1
  330. package/dist/storage/database/schemas.js +3 -4
  331. package/dist/storage/database/sqlite-store.cjs +17 -45
  332. package/dist/storage/database/sqlite-store.d.ts +3 -3
  333. package/dist/storage/database/sqlite-store.d.ts.map +1 -1
  334. package/dist/storage/database/sqlite-store.js +17 -45
  335. package/dist/storage/schemas.cjs +3 -1
  336. package/dist/storage/schemas.d.ts +16 -23
  337. package/dist/storage/schemas.d.ts.map +1 -1
  338. package/dist/storage/schemas.js +3 -1
  339. package/dist/storage/storage-manager.cjs +15 -15
  340. package/dist/storage/storage-manager.d.ts +6 -6
  341. package/dist/storage/storage-manager.d.ts.map +1 -1
  342. package/dist/storage/storage-manager.js +15 -15
  343. package/dist/systemPrompt/contributors.cjs +15 -15
  344. package/dist/systemPrompt/contributors.d.ts +5 -3
  345. package/dist/systemPrompt/contributors.d.ts.map +1 -1
  346. package/dist/systemPrompt/contributors.js +15 -15
  347. package/dist/systemPrompt/manager.cjs +11 -8
  348. package/dist/systemPrompt/manager.d.ts +4 -2
  349. package/dist/systemPrompt/manager.d.ts.map +1 -1
  350. package/dist/systemPrompt/manager.js +11 -8
  351. package/dist/systemPrompt/schemas.cjs +21 -1
  352. package/dist/systemPrompt/schemas.d.ts +53 -53
  353. package/dist/systemPrompt/schemas.d.ts.map +1 -1
  354. package/dist/systemPrompt/schemas.js +11 -1
  355. package/dist/telemetry/decorators.cjs +54 -15
  356. package/dist/telemetry/decorators.d.ts.map +1 -1
  357. package/dist/telemetry/decorators.js +54 -15
  358. package/dist/telemetry/utils.cjs +21 -14
  359. package/dist/telemetry/utils.d.ts +7 -3
  360. package/dist/telemetry/utils.d.ts.map +1 -1
  361. package/dist/telemetry/utils.js +21 -14
  362. package/dist/tools/confirmation/allowed-tools-provider/factory.cjs +2 -2
  363. package/dist/tools/confirmation/allowed-tools-provider/factory.d.ts +2 -1
  364. package/dist/tools/confirmation/allowed-tools-provider/factory.d.ts.map +1 -1
  365. package/dist/tools/confirmation/allowed-tools-provider/factory.js +2 -2
  366. package/dist/tools/confirmation/allowed-tools-provider/storage.cjs +7 -6
  367. package/dist/tools/confirmation/allowed-tools-provider/storage.d.ts +3 -1
  368. package/dist/tools/confirmation/allowed-tools-provider/storage.d.ts.map +1 -1
  369. package/dist/tools/confirmation/allowed-tools-provider/storage.js +7 -6
  370. package/dist/tools/errors.cjs +2 -1
  371. package/dist/tools/errors.d.ts.map +1 -1
  372. package/dist/tools/errors.js +2 -1
  373. package/dist/tools/internal-tools/constants.cjs +2 -1
  374. package/dist/tools/internal-tools/constants.d.ts +1 -1
  375. package/dist/tools/internal-tools/constants.d.ts.map +1 -1
  376. package/dist/tools/internal-tools/constants.js +2 -1
  377. package/dist/tools/internal-tools/implementations/bash-exec-tool.cjs +1 -1
  378. package/dist/tools/internal-tools/implementations/bash-exec-tool.js +1 -1
  379. package/dist/tools/internal-tools/implementations/delegate-to-url-tool.cjs +192 -0
  380. package/dist/tools/internal-tools/implementations/delegate-to-url-tool.d.ts +33 -0
  381. package/dist/tools/internal-tools/implementations/delegate-to-url-tool.d.ts.map +1 -0
  382. package/dist/tools/internal-tools/implementations/delegate-to-url-tool.js +169 -0
  383. package/dist/tools/internal-tools/provider.cjs +21 -17
  384. package/dist/tools/internal-tools/provider.d.ts +3 -1
  385. package/dist/tools/internal-tools/provider.d.ts.map +1 -1
  386. package/dist/tools/internal-tools/provider.js +21 -17
  387. package/dist/tools/internal-tools/registry.cjs +5 -0
  388. package/dist/tools/internal-tools/registry.d.ts.map +1 -1
  389. package/dist/tools/internal-tools/registry.js +5 -0
  390. package/dist/tools/schemas.cjs +16 -4
  391. package/dist/tools/schemas.d.ts +21 -9
  392. package/dist/tools/schemas.d.ts.map +1 -1
  393. package/dist/tools/schemas.js +15 -4
  394. package/dist/tools/tool-manager.cjs +64 -47
  395. package/dist/tools/tool-manager.d.ts +4 -2
  396. package/dist/tools/tool-manager.d.ts.map +1 -1
  397. package/dist/tools/tool-manager.js +61 -44
  398. package/dist/tools/types.d.ts +0 -4
  399. package/dist/tools/types.d.ts.map +1 -1
  400. package/dist/utils/env-file.cjs +118 -0
  401. package/dist/utils/env-file.d.ts +5 -0
  402. package/dist/utils/env-file.d.ts.map +1 -0
  403. package/dist/utils/env-file.js +85 -0
  404. package/dist/utils/error-conversion.cjs +23 -1
  405. package/dist/utils/error-conversion.d.ts +2 -1
  406. package/dist/utils/error-conversion.d.ts.map +1 -1
  407. package/dist/utils/error-conversion.js +23 -1
  408. package/dist/utils/execution-context.d.ts.map +1 -1
  409. package/dist/utils/fs-walk.d.ts.map +1 -1
  410. package/dist/utils/index.cjs +7 -9
  411. package/dist/utils/index.d.ts +3 -4
  412. package/dist/utils/index.d.ts.map +1 -1
  413. package/dist/utils/index.js +3 -4
  414. package/dist/utils/path.cjs +22 -57
  415. package/dist/utils/path.d.ts +8 -7
  416. package/dist/utils/path.d.ts.map +1 -1
  417. package/dist/utils/path.js +21 -54
  418. package/dist/utils/result.cjs +37 -14
  419. package/dist/utils/result.d.ts.map +1 -1
  420. package/dist/utils/result.js +37 -14
  421. package/dist/utils/schema.cjs +2 -3
  422. package/dist/utils/schema.d.ts +2 -1
  423. package/dist/utils/schema.d.ts.map +1 -1
  424. package/dist/utils/schema.js +1 -2
  425. package/dist/utils/service-initializer.cjs +87 -61
  426. package/dist/utils/service-initializer.d.ts +4 -2
  427. package/dist/utils/service-initializer.d.ts.map +1 -1
  428. package/dist/utils/service-initializer.js +69 -43
  429. package/package.json +7 -3
  430. package/dist/Dexto.cjs +0 -251
  431. package/dist/Dexto.d.ts +0 -191
  432. package/dist/Dexto.d.ts.map +0 -1
  433. package/dist/Dexto.js +0 -228
  434. package/dist/agent/registry/error-codes.cjs +0 -44
  435. package/dist/agent/registry/error-codes.d.ts +0 -21
  436. package/dist/agent/registry/error-codes.d.ts.map +0 -1
  437. package/dist/agent/registry/error-codes.js +0 -21
  438. package/dist/agent/registry/errors.cjs +0 -188
  439. package/dist/agent/registry/errors.d.ts +0 -63
  440. package/dist/agent/registry/errors.d.ts.map +0 -1
  441. package/dist/agent/registry/errors.js +0 -165
  442. package/dist/agent/registry/registry.cjs +0 -479
  443. package/dist/agent/registry/registry.d.ts +0 -130
  444. package/dist/agent/registry/registry.d.ts.map +0 -1
  445. package/dist/agent/registry/registry.js +0 -453
  446. package/dist/agent/registry/types.cjs +0 -74
  447. package/dist/agent/registry/types.d.ts +0 -142
  448. package/dist/agent/registry/types.d.ts.map +0 -1
  449. package/dist/agent/registry/types.js +0 -48
  450. package/dist/agent/registry/user-registry.cjs +0 -140
  451. package/dist/agent/registry/user-registry.d.ts +0 -34
  452. package/dist/agent/registry/user-registry.d.ts.map +0 -1
  453. package/dist/agent/registry/user-registry.js +0 -105
  454. package/dist/approval/providers/event-based-approval-provider.cjs +0 -156
  455. package/dist/approval/providers/event-based-approval-provider.d.ts +0 -39
  456. package/dist/approval/providers/event-based-approval-provider.d.ts.map +0 -1
  457. package/dist/approval/providers/event-based-approval-provider.js +0 -133
  458. package/dist/approval/providers/factory.d.ts.map +0 -1
  459. package/dist/approval/providers/noop-approval-provider.cjs +0 -54
  460. package/dist/approval/providers/noop-approval-provider.d.ts +0 -18
  461. package/dist/approval/providers/noop-approval-provider.d.ts.map +0 -1
  462. package/dist/approval/providers/noop-approval-provider.js +0 -31
  463. package/dist/config/agent-resolver.cjs +0 -153
  464. package/dist/config/agent-resolver.d.ts +0 -14
  465. package/dist/config/agent-resolver.d.ts.map +0 -1
  466. package/dist/config/agent-resolver.js +0 -123
  467. package/dist/config/error-codes.cjs +0 -39
  468. package/dist/config/error-codes.d.ts +0 -16
  469. package/dist/config/error-codes.d.ts.map +0 -1
  470. package/dist/config/error-codes.js +0 -16
  471. package/dist/config/errors.cjs +0 -126
  472. package/dist/config/errors.d.ts +0 -34
  473. package/dist/config/errors.d.ts.map +0 -1
  474. package/dist/config/errors.js +0 -103
  475. package/dist/config/index.cjs +0 -26
  476. package/dist/config/index.d.ts +0 -4
  477. package/dist/config/index.d.ts.map +0 -1
  478. package/dist/config/index.js +0 -3
  479. package/dist/config/loader.cjs +0 -119
  480. package/dist/config/loader.d.ts +0 -16
  481. package/dist/config/loader.d.ts.map +0 -1
  482. package/dist/config/loader.js +0 -86
  483. package/dist/config/writer.cjs +0 -182
  484. package/dist/config/writer.d.ts +0 -35
  485. package/dist/config/writer.d.ts.map +0 -1
  486. package/dist/config/writer.js +0 -147
  487. package/dist/preferences/constants.d.ts +0 -2
  488. package/dist/preferences/constants.d.ts.map +0 -1
  489. package/dist/preferences/constants.js +0 -5
  490. package/dist/preferences/error-codes.d.ts +0 -8
  491. package/dist/preferences/error-codes.d.ts.map +0 -1
  492. package/dist/preferences/error-codes.js +0 -12
  493. package/dist/preferences/errors.cjs +0 -75
  494. package/dist/preferences/errors.d.ts +0 -18
  495. package/dist/preferences/errors.d.ts.map +0 -1
  496. package/dist/preferences/errors.js +0 -51
  497. package/dist/preferences/index.cjs +0 -55
  498. package/dist/preferences/index.d.ts +0 -6
  499. package/dist/preferences/index.d.ts.map +0 -1
  500. package/dist/preferences/index.js +0 -32
  501. package/dist/preferences/loader.cjs +0 -138
  502. package/dist/preferences/loader.d.ts +0 -51
  503. package/dist/preferences/loader.d.ts.map +0 -1
  504. package/dist/preferences/loader.js +0 -110
  505. package/dist/preferences/schemas.cjs +0 -75
  506. package/dist/preferences/schemas.d.ts +0 -110
  507. package/dist/preferences/schemas.d.ts.map +0 -1
  508. package/dist/preferences/schemas.js +0 -49
  509. package/dist/utils/api-key-store.cjs +0 -56
  510. package/dist/utils/api-key-store.d.ts +0 -24
  511. package/dist/utils/api-key-store.d.ts.map +0 -1
  512. package/dist/utils/api-key-store.js +0 -31
  513. package/dist/utils/env.cjs +0 -154
  514. package/dist/utils/env.d.ts +0 -28
  515. package/dist/utils/env.d.ts.map +0 -1
  516. package/dist/utils/env.js +0 -119
  517. package/dist/utils/port-utils.cjs +0 -37
  518. package/dist/utils/port-utils.d.ts +0 -10
  519. package/dist/utils/port-utils.d.ts.map +0 -1
  520. package/dist/utils/port-utils.js +0 -14
  521. package/dist/utils/port-utils.spec.cjs +0 -26
  522. package/dist/utils/port-utils.spec.js +0 -25
  523. /package/dist/approval/{providers/factory.cjs → factory.cjs} +0 -0
@@ -8,16 +8,19 @@ import { expandMessageReferences } from "../resources/index.js";
8
8
  import { expandBlobReferences } from "../context/utils.js";
9
9
  import { PromptManager } from "../prompts/index.js";
10
10
  import { SessionError } from "../session/index.js";
11
- import { logger } from "../logger/index.js";
11
+ import { createLogger } from "../logger/factory.js";
12
+ import { DextoLogComponent } from "../logger/v2/types.js";
12
13
  import { InstrumentClass } from "../telemetry/decorators.js";
14
+ import { trace, context, propagation } from "@opentelemetry/api";
13
15
  import { LLMUpdatesSchema } from "../llm/schemas.js";
14
16
  import { resolveAndValidateLLMConfig } from "../llm/resolver.js";
15
17
  import { validateInputForLLM } from "../llm/validation.js";
18
+ import { LLMError } from "../llm/errors.js";
16
19
  import { AgentError } from "./errors.js";
17
20
  import { MCPError } from "../mcp/errors.js";
21
+ import { DextoRuntimeError } from "../errors/DextoRuntimeError.js";
18
22
  import { ensureOk } from "../errors/result-bridge.js";
19
23
  import { fail, zodToIssues } from "../utils/result.js";
20
- import { DextoValidationError } from "../errors/DextoValidationError.js";
21
24
  import { resolveAndValidateMcpServerConfig } from "../mcp/resolver.js";
22
25
  import {
23
26
  getSupportedProviders,
@@ -27,11 +30,11 @@ import {
27
30
  } from "../llm/registry.js";
28
31
  import { createAgentServices } from "../utils/service-initializer.js";
29
32
  import { AgentConfigSchema } from "./schemas.js";
30
- import { getDextoPath } from "../utils/path.js";
33
+ import {
34
+ AgentEventBus,
35
+ STREAMING_EVENTS
36
+ } from "../events/index.js";
31
37
  import { safeStringify } from "../utils/safe-stringify.js";
32
- import { loadAgentConfig } from "../config/loader.js";
33
- import { promises as fs } from "fs";
34
- import { parseDocument } from "yaml";
35
38
  import { deriveHeuristicTitle, generateSessionTitle } from "../session/title-generator.js";
36
39
  const requiredServices = [
37
40
  "mcpManager",
@@ -55,10 +58,22 @@ _DextoAgent_decorators = [InstrumentClass({
55
58
  ]
56
59
  })];
57
60
  class DextoAgent {
61
+ /**
62
+ * Creates a DextoAgent instance.
63
+ *
64
+ * @param config - Agent configuration (validated and enriched)
65
+ * @param configPath - Optional path to config file (for relative path resolution)
66
+ */
58
67
  constructor(config, configPath) {
59
68
  this.configPath = configPath;
60
69
  this.config = AgentConfigSchema.parse(config);
61
- logger.info("DextoAgent created.");
70
+ this.logger = createLogger({
71
+ config: this.config.logger,
72
+ agentId: this.config.agentId,
73
+ component: DextoLogComponent.AGENT
74
+ });
75
+ this.agentEventBus = new AgentEventBus();
76
+ this.logger.info("DextoAgent created.");
62
77
  }
63
78
  /**
64
79
  * These services are public for use by the outside world
@@ -77,19 +92,20 @@ class DextoAgent {
77
92
  services;
78
93
  // Search service for conversation search
79
94
  searchService;
80
- // Default session for backward compatibility
81
- defaultSession = null;
82
- // Current default session ID for loadSession functionality
83
- currentDefaultSessionId = "default";
84
95
  // Track initialization state
85
96
  _isStarted = false;
86
97
  _isStopped = false;
87
- // Store config for async initialization
98
+ // Store config for async initialization (accessible before start() for setup)
88
99
  config;
89
- // Event subscribers (e.g., WebSocket, Webhook handlers)
100
+ // Event subscribers (e.g., SSE, Webhook handlers)
90
101
  eventSubscribers = /* @__PURE__ */ new Set();
91
102
  // Telemetry instance for distributed tracing
92
103
  telemetry;
104
+ // Approval handler for manual tool confirmation and elicitation
105
+ // Set via setApprovalHandler() before start() if needed
106
+ approvalHandler;
107
+ // Logger instance for this agent (dependency injection)
108
+ logger;
93
109
  /**
94
110
  * Starts the agent by initializing all async services.
95
111
  * This method handles storage backends, MCP connections, session manager initialization, and other async operations.
@@ -102,8 +118,13 @@ class DextoAgent {
102
118
  throw AgentError.alreadyStarted();
103
119
  }
104
120
  try {
105
- logger.info("Starting DextoAgent...");
106
- const services = await createAgentServices(this.config, this.configPath);
121
+ this.logger.info("Starting DextoAgent...");
122
+ const services = await createAgentServices(
123
+ this.config,
124
+ this.configPath,
125
+ this.logger,
126
+ this.agentEventBus
127
+ );
107
128
  for (const service of requiredServices) {
108
129
  if (!services[service]) {
109
130
  throw AgentError.initializationFailed(
@@ -111,12 +132,31 @@ class DextoAgent {
111
132
  );
112
133
  }
113
134
  }
135
+ const needsHandler = this.config.toolConfirmation.mode === "manual" || this.config.elicitation.enabled;
136
+ if (needsHandler && !this.approvalHandler) {
137
+ const reasons = [];
138
+ if (this.config.toolConfirmation.mode === "manual") {
139
+ reasons.push('tool confirmation mode is "manual"');
140
+ }
141
+ if (this.config.elicitation.enabled) {
142
+ reasons.push("elicitation is enabled");
143
+ }
144
+ throw AgentError.initializationFailed(
145
+ `An approval handler is required but not configured (${reasons.join(" and ")}).
146
+ Either:
147
+ \u2022 Call agent.setApprovalHandler() before starting
148
+ \u2022 Set toolConfirmation: { mode: "auto-approve" } or { mode: "auto-deny" }
149
+ \u2022 Disable elicitation: { enabled: false }`
150
+ );
151
+ }
152
+ if (this.approvalHandler) {
153
+ services.approvalManager.setHandler(this.approvalHandler);
154
+ }
114
155
  Object.assign(this, {
115
156
  mcpManager: services.mcpManager,
116
157
  toolManager: services.toolManager,
117
158
  resourceManager: services.resourceManager,
118
159
  systemPromptManager: services.systemPromptManager,
119
- agentEventBus: services.agentEventBus,
120
160
  stateManager: services.stateManager,
121
161
  sessionManager: services.sessionManager,
122
162
  memoryManager: services.memoryManager,
@@ -128,20 +168,25 @@ class DextoAgent {
128
168
  this.resourceManager,
129
169
  this.config,
130
170
  this.agentEventBus,
131
- services.storageManager.getDatabase()
171
+ services.storageManager.getDatabase(),
172
+ this.logger
132
173
  );
133
174
  await promptManager.initialize();
134
175
  Object.assign(this, { promptManager });
135
176
  this._isStarted = true;
136
177
  this._isStopped = false;
137
- logger.info("DextoAgent started successfully.");
178
+ this.logger.info("DextoAgent started successfully.");
138
179
  for (const subscriber of this.eventSubscribers) {
139
180
  subscriber.subscribe(this.agentEventBus);
140
181
  }
141
- const logPath = getDextoPath("logs", "dexto.log");
142
- console.log(`\u{1F4CB} Logs available at: ${logPath}`);
182
+ const fileTransport = this.config.logger?.transports?.find((t) => t.type === "file");
183
+ if (fileTransport && "path" in fileTransport) {
184
+ console.log(`\u{1F4CB} Logs available at: ${fileTransport.path}`);
185
+ }
143
186
  } catch (error) {
144
- logger.error("Failed to start DextoAgent", error);
187
+ this.logger.error("Failed to start DextoAgent", {
188
+ error: error instanceof Error ? error.message : String(error)
189
+ });
145
190
  throw error;
146
191
  }
147
192
  }
@@ -154,19 +199,19 @@ class DextoAgent {
154
199
  */
155
200
  async stop() {
156
201
  if (this._isStopped) {
157
- logger.warn("Agent is already stopped");
202
+ this.logger.warn("Agent is already stopped");
158
203
  return;
159
204
  }
160
205
  if (!this._isStarted) {
161
206
  throw AgentError.notStarted();
162
207
  }
163
208
  try {
164
- logger.info("Stopping DextoAgent...");
209
+ this.logger.info("Stopping DextoAgent...");
165
210
  const shutdownErrors = [];
166
211
  try {
167
212
  if (this.sessionManager) {
168
213
  await this.sessionManager.cleanup();
169
- logger.debug("SessionManager cleaned up successfully");
214
+ this.logger.debug("SessionManager cleaned up successfully");
170
215
  }
171
216
  } catch (error) {
172
217
  const err = error instanceof Error ? error : new Error(String(error));
@@ -175,7 +220,7 @@ class DextoAgent {
175
220
  try {
176
221
  if (this.services?.pluginManager) {
177
222
  await this.services.pluginManager.cleanup();
178
- logger.debug("PluginManager cleaned up successfully");
223
+ this.logger.debug("PluginManager cleaned up successfully");
179
224
  }
180
225
  } catch (error) {
181
226
  const err = error instanceof Error ? error : new Error(String(error));
@@ -184,7 +229,7 @@ class DextoAgent {
184
229
  try {
185
230
  if (this.mcpManager) {
186
231
  await this.mcpManager.disconnectAll();
187
- logger.debug("MCPManager disconnected all clients successfully");
232
+ this.logger.debug("MCPManager disconnected all clients successfully");
188
233
  }
189
234
  } catch (error) {
190
235
  const err = error instanceof Error ? error : new Error(String(error));
@@ -193,7 +238,7 @@ class DextoAgent {
193
238
  try {
194
239
  if (this.services?.storageManager) {
195
240
  await this.services.storageManager.disconnect();
196
- logger.debug("Storage manager disconnected successfully");
241
+ this.logger.debug("Storage manager disconnected successfully");
197
242
  }
198
243
  } catch (error) {
199
244
  const err = error instanceof Error ? error : new Error(String(error));
@@ -203,18 +248,20 @@ class DextoAgent {
203
248
  this._isStarted = false;
204
249
  if (shutdownErrors.length > 0) {
205
250
  const errorMessages = shutdownErrors.map((e) => e.message).join("; ");
206
- logger.warn(`DextoAgent stopped with some errors: ${errorMessages}`);
251
+ this.logger.warn(`DextoAgent stopped with some errors: ${errorMessages}`);
207
252
  } else {
208
- logger.info("DextoAgent stopped successfully.");
253
+ this.logger.info("DextoAgent stopped successfully.");
209
254
  }
210
255
  } catch (error) {
211
- logger.error("Failed to stop DextoAgent", error);
256
+ this.logger.error("Failed to stop DextoAgent", {
257
+ error: error instanceof Error ? error.message : String(error)
258
+ });
212
259
  throw error;
213
260
  }
214
261
  }
215
262
  /**
216
263
  * Register an event subscriber that will be automatically re-subscribed on agent restart.
217
- * Subscribers are typically API layer components (WebSocket, Webhook handlers) that need
264
+ * Subscribers are typically API layer components (SSE, Webhook handlers) that need
218
265
  * to receive agent events. If the agent is already started, the subscriber is immediately subscribed.
219
266
  *
220
267
  * @param subscriber - Object implementing AgentEventSubscriber interface
@@ -256,11 +303,11 @@ class DextoAgent {
256
303
  */
257
304
  ensureStarted() {
258
305
  if (this._isStopped) {
259
- logger.warn("Agent is stopped");
306
+ this.logger.warn("Agent is stopped");
260
307
  throw AgentError.stopped();
261
308
  }
262
309
  if (!this._isStarted) {
263
- logger.warn("Agent is not started");
310
+ this.logger.warn("Agent is not started");
264
311
  throw AgentError.notStarted();
265
312
  }
266
313
  }
@@ -272,110 +319,347 @@ class DextoAgent {
272
319
  * @param textInput - The user's text message or query to process
273
320
  * @param imageDataInput - Optional image data and MIME type for multimodal input
274
321
  * @param fileDataInput - Optional file data and MIME type for file input
275
- * @param sessionId - Optional session ID for multi-session scenarios
322
+ * @param sessionId - Session ID for the conversation (required)
323
+ * @param stream - Whether to stream the response (default: false)
276
324
  * @returns Promise that resolves to the AI's response text, or null if no significant response
277
325
  * @throws Error if processing fails
278
326
  */
279
327
  async run(textInput, imageDataInput, fileDataInput, sessionId, stream = false) {
280
328
  this.ensureStarted();
281
- try {
282
- const targetSessionId = sessionId || this.currentDefaultSessionId;
283
- const llmConfig = this.stateManager.getLLMConfig(targetSessionId);
284
- const validation = validateInputForLLM(
285
- {
286
- text: textInput,
287
- ...imageDataInput && { imageData: imageDataInput },
288
- ...fileDataInput && { fileData: fileDataInput }
289
- },
290
- {
291
- provider: llmConfig.provider,
292
- model: llmConfig.model
293
- }
294
- );
295
- ensureOk(validation);
296
- const existingSession = await this.sessionManager.getSession(targetSessionId);
297
- const session = existingSession || await this.sessionManager.createSession(targetSessionId);
298
- logger.debug(
299
- `DextoAgent.run: sessionId=${targetSessionId}, textLength=${textInput?.length ?? 0}, hasImage=${Boolean(
300
- imageDataInput
301
- )}, hasFile=${Boolean(fileDataInput)}`
302
- );
303
- let finalText = textInput;
304
- let finalImageData = imageDataInput;
305
- if (textInput && textInput.includes("@")) {
306
- try {
307
- const resources = await this.resourceManager.list();
308
- const expansion = await expandMessageReferences(
309
- textInput,
310
- resources,
311
- (uri) => this.resourceManager.read(uri)
312
- );
313
- if (expansion.unresolvedReferences.length > 0) {
314
- const unresolvedNames = expansion.unresolvedReferences.map((ref) => ref.originalRef).join(", ");
315
- logger.warn(
316
- `Could not resolve ${expansion.unresolvedReferences.length} resource reference(s): ${unresolvedNames}`
317
- );
318
- }
319
- const MAX_EXPANDED_SIZE = 5 * 1024 * 1024;
320
- const expandedSize = Buffer.byteLength(expansion.expandedMessage, "utf-8");
321
- if (expandedSize > MAX_EXPANDED_SIZE) {
322
- logger.warn(
323
- `Expanded message size (${(expandedSize / 1024 / 1024).toFixed(2)}MB) exceeds limit (${MAX_EXPANDED_SIZE / 1024 / 1024}MB). Content may be truncated.`
329
+ if (!sessionId || typeof sessionId !== "string") {
330
+ throw new Error("sessionId is required and must be a non-empty string");
331
+ }
332
+ const targetSessionId = sessionId;
333
+ const activeContext = context.active();
334
+ const span = trace.getActiveSpan();
335
+ if (span) {
336
+ span.setAttribute("sessionId", targetSessionId);
337
+ }
338
+ const existingBaggage = propagation.getBaggage(activeContext);
339
+ const baggageEntries = {};
340
+ if (existingBaggage) {
341
+ existingBaggage.getAllEntries().forEach(([key, entry]) => {
342
+ baggageEntries[key] = { ...entry };
343
+ });
344
+ }
345
+ baggageEntries.sessionId = { ...baggageEntries.sessionId, value: targetSessionId };
346
+ const updatedContext = propagation.setBaggage(
347
+ activeContext,
348
+ propagation.createBaggage(baggageEntries)
349
+ );
350
+ const verifyBaggage = propagation.getBaggage(updatedContext);
351
+ this.logger.debug(
352
+ `Baggage after setting sessionId: ${JSON.stringify(
353
+ Array.from(verifyBaggage?.getAllEntries() || [])
354
+ )}`
355
+ );
356
+ return await context.with(updatedContext, async () => {
357
+ try {
358
+ const llmConfig = this.stateManager.getLLMConfig(targetSessionId);
359
+ const validation = validateInputForLLM(
360
+ {
361
+ text: textInput,
362
+ ...imageDataInput && { imageData: imageDataInput },
363
+ ...fileDataInput && { fileData: fileDataInput }
364
+ },
365
+ {
366
+ provider: llmConfig.provider,
367
+ model: llmConfig.model
368
+ },
369
+ this.logger
370
+ );
371
+ ensureOk(validation, this.logger);
372
+ const session = await this.sessionManager.getSession(targetSessionId) || await this.sessionManager.createSession(targetSessionId);
373
+ this.logger.debug(
374
+ `DextoAgent.run: sessionId=${targetSessionId}, textLength=${textInput?.length ?? 0}, hasImage=${Boolean(
375
+ imageDataInput
376
+ )}, hasFile=${Boolean(fileDataInput)}`
377
+ );
378
+ let finalText = textInput;
379
+ let finalImageData = imageDataInput;
380
+ if (textInput && textInput.includes("@")) {
381
+ try {
382
+ const resources = await this.resourceManager.list();
383
+ const expansion = await expandMessageReferences(
384
+ textInput,
385
+ resources,
386
+ (uri) => this.resourceManager.read(uri)
324
387
  );
325
- }
326
- finalText = expansion.expandedMessage;
327
- if (expansion.extractedImages.length > 0 && !imageDataInput) {
328
- const firstImage = expansion.extractedImages[0];
329
- if (firstImage) {
330
- finalImageData = {
331
- image: firstImage.image,
332
- mimeType: firstImage.mimeType
333
- };
334
- logger.debug(
335
- `Using extracted image: ${firstImage.name} (${firstImage.mimeType})`
388
+ if (expansion.unresolvedReferences.length > 0) {
389
+ const unresolvedNames = expansion.unresolvedReferences.map((ref) => ref.originalRef).join(", ");
390
+ this.logger.warn(
391
+ `Could not resolve ${expansion.unresolvedReferences.length} resource reference(s): ${unresolvedNames}`
336
392
  );
337
393
  }
394
+ const MAX_EXPANDED_SIZE = 5 * 1024 * 1024;
395
+ const expandedSize = Buffer.byteLength(expansion.expandedMessage, "utf-8");
396
+ if (expandedSize > MAX_EXPANDED_SIZE) {
397
+ this.logger.warn(
398
+ `Expanded message size (${(expandedSize / 1024 / 1024).toFixed(2)}MB) exceeds limit (${MAX_EXPANDED_SIZE / 1024 / 1024}MB). Content may be truncated.`
399
+ );
400
+ }
401
+ finalText = expansion.expandedMessage;
402
+ if (expansion.extractedImages.length > 0 && !imageDataInput) {
403
+ const firstImage = expansion.extractedImages[0];
404
+ if (firstImage) {
405
+ finalImageData = {
406
+ image: firstImage.image,
407
+ mimeType: firstImage.mimeType
408
+ };
409
+ this.logger.debug(
410
+ `Using extracted image: ${firstImage.name} (${firstImage.mimeType})`
411
+ );
412
+ }
413
+ }
414
+ } catch (error) {
415
+ this.logger.error(
416
+ `Failed to expand resource references: ${error instanceof Error ? error.message : String(error)}. Continuing with original message.`
417
+ );
338
418
  }
339
- } catch (error) {
340
- logger.error(
341
- `Failed to expand resource references: ${error instanceof Error ? error.message : String(error)}. Continuing with original message.`
419
+ }
420
+ if (!finalText.trim() && !finalImageData && !fileDataInput) {
421
+ this.logger.warn(
422
+ "Resource expansion resulted in empty content. Using original message."
342
423
  );
424
+ finalText = textInput;
343
425
  }
344
- }
345
- if (!finalText.trim() && !finalImageData && !fileDataInput) {
346
- logger.warn(
347
- "Resource expansion resulted in empty content. Using original message."
426
+ const response = await session.run(
427
+ finalText,
428
+ finalImageData,
429
+ fileDataInput,
430
+ stream
348
431
  );
349
- finalText = textInput;
432
+ this.sessionManager.incrementMessageCount(session.id).catch(
433
+ (error) => this.logger.warn(
434
+ `Failed to increment message count: ${error instanceof Error ? error.message : String(error)}`
435
+ )
436
+ );
437
+ return response;
438
+ } catch (error) {
439
+ this.logger.error(
440
+ `Error during DextoAgent.run: ${error instanceof Error ? error.message : JSON.stringify(error)}`
441
+ );
442
+ throw error;
443
+ }
444
+ });
445
+ }
446
+ /**
447
+ * Generate a complete response (waits for full completion).
448
+ * This is the recommended method for non-streaming use cases.
449
+ *
450
+ * @param message The user's message
451
+ * @param options Configuration options (sessionId is required, imageData, fileData, signal are optional)
452
+ * @returns Promise that resolves to the complete response
453
+ *
454
+ * @example
455
+ * ```typescript
456
+ * const response = await agent.generate("What is 2+2?", { sessionId: "default" });
457
+ * console.log(response.content); // "4"
458
+ * console.log(response.usage.totalTokens); // 50
459
+ * ```
460
+ */
461
+ async generate(message, options) {
462
+ const events = [];
463
+ for await (const event of await this.stream(message, options)) {
464
+ events.push(event);
465
+ }
466
+ const errorEvent = events.find(
467
+ (e) => e.type === "llm:error"
468
+ );
469
+ if (errorEvent) {
470
+ if (errorEvent.error instanceof DextoRuntimeError) {
471
+ throw errorEvent.error;
350
472
  }
351
- void this.maybeGenerateTitle(targetSessionId, finalText, llmConfig);
352
- const response = await session.run(finalText, finalImageData, fileDataInput, stream);
353
- this.sessionManager.incrementMessageCount(session.id).catch(
354
- (error) => logger.warn(
355
- `Failed to increment message count: ${error instanceof Error ? error.message : String(error)}`
356
- )
473
+ const llmConfig = this.stateManager.getLLMConfig(options.sessionId);
474
+ throw LLMError.generationFailed(
475
+ errorEvent.error.message,
476
+ llmConfig.provider,
477
+ llmConfig.model
357
478
  );
358
- return response;
359
- } catch (error) {
360
- logger.error(
361
- `Error during DextoAgent.run: ${error instanceof Error ? error.message : JSON.stringify(error)}`
479
+ }
480
+ const responseEvent = events.find((e) => e.type === "llm:response");
481
+ if (!responseEvent || responseEvent.type !== "llm:response") {
482
+ const llmConfig = this.stateManager.getLLMConfig(options.sessionId);
483
+ throw LLMError.generationFailed(
484
+ "Stream did not complete successfully - no response received",
485
+ llmConfig.provider,
486
+ llmConfig.model
362
487
  );
363
- throw error;
364
488
  }
489
+ const toolCallEvents = events.filter(
490
+ (e) => e.type === "llm:tool-call"
491
+ );
492
+ const toolResultEvents = events.filter(
493
+ (e) => e.type === "llm:tool-result"
494
+ );
495
+ const toolCalls = toolCallEvents.map((tc) => {
496
+ const toolResult = toolResultEvents.find((tr) => tr.callId === tc.callId);
497
+ return {
498
+ toolName: tc.toolName,
499
+ args: tc.args,
500
+ callId: tc.callId || `tool_${Date.now()}`,
501
+ result: toolResult ? {
502
+ success: toolResult.success,
503
+ data: toolResult.sanitized
504
+ } : void 0
505
+ };
506
+ });
507
+ const messageId = `msg_${Date.now()}_${Math.random().toString(36).substring(7)}`;
508
+ const defaultUsage = {
509
+ inputTokens: 0,
510
+ outputTokens: 0,
511
+ totalTokens: 0
512
+ };
513
+ const usage = responseEvent.tokenUsage ?? defaultUsage;
514
+ return {
515
+ content: responseEvent.content,
516
+ reasoning: responseEvent.reasoning,
517
+ usage,
518
+ toolCalls,
519
+ sessionId: options.sessionId,
520
+ messageId
521
+ };
522
+ }
523
+ /**
524
+ * Stream a response (yields events as they arrive).
525
+ * This is the recommended method for real-time streaming UI updates.
526
+ *
527
+ * TODO: Refactor to move AsyncIterator down to LLM service level (Option 1).
528
+ * Streaming message API that returns core AgentEvents in real-time.
529
+ * Only emits STREAMING_EVENTS (tier 1 visibility) - events designed for real-time chat UIs.
530
+ *
531
+ * Events are forwarded directly from the AgentEventBus with no mapping layer,
532
+ * providing a unified event system across all API layers.
533
+ *
534
+ * @param message The user's message
535
+ * @param options Configuration options (sessionId is required, imageData, fileData, signal are optional)
536
+ * @returns AsyncIterator that yields StreamingEvent objects (core events with type property)
537
+ *
538
+ * @example
539
+ * ```typescript
540
+ * for await (const event of await agent.stream("Write a poem", { sessionId: "default" })) {
541
+ * if (event.type === 'llm:chunk') {
542
+ * process.stdout.write(event.content);
543
+ * }
544
+ * if (event.type === 'llm:tool-call') {
545
+ * console.log(`\n[Using ${event.toolName}]\n`);
546
+ * }
547
+ * }
548
+ * ```
549
+ */
550
+ async stream(message, options) {
551
+ this.ensureStarted();
552
+ if (!options.sessionId) {
553
+ throw new Error("sessionId is required in StreamOptions");
554
+ }
555
+ const sessionId = options.sessionId;
556
+ const imageData = options.imageData;
557
+ const fileData = options.fileData;
558
+ const signal = options.signal;
559
+ const eventQueue = [];
560
+ let completed = false;
561
+ let _streamError = null;
562
+ const controller = new AbortController();
563
+ const cleanupSignal = controller.signal;
564
+ const listeners = [];
565
+ const cleanupListeners = () => {
566
+ if (listeners.length === 0) {
567
+ return;
568
+ }
569
+ for (const { event, listener } of listeners) {
570
+ this.agentEventBus.off(event, listener);
571
+ }
572
+ listeners.length = 0;
573
+ };
574
+ if (signal) {
575
+ const abortHandler = () => {
576
+ cleanupListeners();
577
+ controller.abort();
578
+ };
579
+ signal.addEventListener("abort", abortHandler, { once: true });
580
+ }
581
+ for (const eventName of STREAMING_EVENTS) {
582
+ const listener = (data) => {
583
+ if (data.sessionId !== void 0 && data.sessionId !== sessionId) {
584
+ return;
585
+ }
586
+ eventQueue.push({ type: eventName, ...data });
587
+ if (eventName === "llm:response" || eventName === "llm:error" && !data.recoverable) {
588
+ completed = true;
589
+ }
590
+ };
591
+ this.agentEventBus.on(eventName, listener, {
592
+ signal: cleanupSignal
593
+ });
594
+ listeners.push({ event: eventName, listener });
595
+ }
596
+ const imageDataForRun = imageData ? {
597
+ image: typeof imageData.image === "string" ? imageData.image : imageData.image.toString(),
598
+ mimeType: imageData.mimeType || "image/png"
599
+ } : void 0;
600
+ const fileDataForRun = fileData ? {
601
+ data: typeof fileData.data === "string" ? fileData.data : fileData.data.toString(),
602
+ mimeType: fileData.mimeType,
603
+ ...fileData.filename && { filename: fileData.filename }
604
+ } : void 0;
605
+ this.run(message, imageDataForRun, fileDataForRun, sessionId, true).catch((error) => {
606
+ _streamError = error;
607
+ completed = true;
608
+ this.logger.error(
609
+ `Error in DextoAgent.stream: ${error instanceof Error ? error.message : String(error)}`
610
+ );
611
+ eventQueue.push({
612
+ type: "llm:error",
613
+ error,
614
+ recoverable: false,
615
+ context: "run_failed",
616
+ sessionId
617
+ });
618
+ });
619
+ const iterator = {
620
+ async next() {
621
+ while (!completed && eventQueue.length === 0) {
622
+ await new Promise((resolve) => setTimeout(resolve, 0));
623
+ if (signal?.aborted) {
624
+ cleanupListeners();
625
+ controller.abort();
626
+ return { done: true, value: void 0 };
627
+ }
628
+ }
629
+ if (eventQueue.length > 0) {
630
+ return { done: false, value: eventQueue.shift() };
631
+ }
632
+ if (completed) {
633
+ cleanupListeners();
634
+ controller.abort();
635
+ return { done: true, value: void 0 };
636
+ }
637
+ cleanupListeners();
638
+ return { done: true, value: void 0 };
639
+ },
640
+ async return() {
641
+ cleanupListeners();
642
+ controller.abort();
643
+ return { done: true, value: void 0 };
644
+ },
645
+ [Symbol.asyncIterator]() {
646
+ return iterator;
647
+ }
648
+ };
649
+ return iterator;
365
650
  }
366
651
  /**
367
- * Cancels the currently running turn for a session (or the default session).
652
+ * Cancels the currently running turn for a session.
368
653
  * Safe to call even if no run is in progress.
369
- * @param sessionId Optional session id; defaults to current default session
654
+ * @param sessionId Session id (required)
370
655
  * @returns true if a run was in progress and was signaled to abort; false otherwise
371
656
  */
372
657
  async cancel(sessionId) {
373
658
  this.ensureStarted();
374
- const targetSessionId = sessionId || this.currentDefaultSessionId;
375
- if (!sessionId && this.defaultSession && this.defaultSession.id === targetSessionId) {
376
- return this.defaultSession.cancel();
659
+ if (!sessionId || typeof sessionId !== "string") {
660
+ throw new Error("sessionId is required and must be a non-empty string");
377
661
  }
378
- const existing = await this.sessionManager.getSession(targetSessionId, false);
662
+ const existing = await this.sessionManager.getSession(sessionId, false);
379
663
  if (existing) {
380
664
  return existing.cancel();
381
665
  }
@@ -415,9 +699,6 @@ class DextoAgent {
415
699
  */
416
700
  async endSession(sessionId) {
417
701
  this.ensureStarted();
418
- if (sessionId === this.currentDefaultSessionId) {
419
- this.defaultSession = null;
420
- }
421
702
  return this.sessionManager.endSession(sessionId);
422
703
  }
423
704
  /**
@@ -427,9 +708,6 @@ class DextoAgent {
427
708
  */
428
709
  async deleteSession(sessionId) {
429
710
  this.ensureStarted();
430
- if (sessionId === this.currentDefaultSessionId) {
431
- this.defaultSession = null;
432
- }
433
711
  return this.sessionManager.deleteSession(sessionId);
434
712
  }
435
713
  /**
@@ -456,66 +734,63 @@ class DextoAgent {
456
734
  return await this.sessionManager.getSessionTitle(sessionId);
457
735
  }
458
736
  /**
459
- * Background task: generate and persist a session title using the same LLM.
460
- * Runs only for the first user message (messageCount === 0 and no existing title).
461
- * Never throws; timeboxed in the generator.
737
+ * Generate a title for a session on-demand.
738
+ * Uses the first user message content to generate a descriptive title.
739
+ *
740
+ * @param sessionId Session ID to generate title for
741
+ * @returns Promise that resolves to the generated title, or null if generation failed
462
742
  */
463
- async maybeGenerateTitle(sessionId, userText, llmConfig) {
464
- try {
465
- const metadata = await this.sessionManager.getSessionMetadata(sessionId);
466
- if (!metadata) {
467
- logger.debug(
468
- `[SessionTitle] No session metadata available for ${sessionId}, skipping title generation`
469
- );
470
- return;
471
- }
472
- if (metadata.title) {
473
- logger.debug(
474
- `[SessionTitle] Session ${sessionId} already has title '${metadata.title}', skipping`
475
- );
476
- return;
477
- }
478
- if (!userText || !userText.trim()) {
479
- logger.debug(
480
- `[SessionTitle] User text empty for session ${sessionId}, skipping title generation`
481
- );
482
- return;
483
- }
484
- logger.debug(
485
- `[SessionTitle] Checking title generation preconditions for session ${sessionId}`
486
- );
487
- const result = await generateSessionTitle(
488
- llmConfig,
489
- llmConfig.router,
490
- this.toolManager,
491
- this.systemPromptManager,
492
- this.resourceManager,
493
- userText
743
+ async generateSessionTitle(sessionId) {
744
+ this.ensureStarted();
745
+ const metadata = await this.sessionManager.getSessionMetadata(sessionId);
746
+ if (!metadata) {
747
+ throw SessionError.notFound(sessionId);
748
+ }
749
+ if (metadata.title) {
750
+ this.logger.debug(
751
+ `[SessionTitle] Session ${sessionId} already has title '${metadata.title}'`
494
752
  );
495
- if (result.error) {
496
- logger.debug(
497
- `[SessionTitle] LLM title generation failed for ${sessionId}: ${result.error}${result.timedOut ? " (timeout)" : ""}`
498
- );
499
- }
500
- let title = result.title;
501
- if (!title) {
502
- title = deriveHeuristicTitle(userText);
503
- if (title) {
504
- logger.info(`[SessionTitle] Using heuristic title for ${sessionId}: ${title}`);
505
- } else {
506
- logger.debug(
507
- `[SessionTitle] No suitable title derived for session ${sessionId}`
508
- );
509
- return;
510
- }
753
+ return metadata.title;
754
+ }
755
+ const session = await this.sessionManager.getSession(sessionId);
756
+ if (!session) {
757
+ throw SessionError.notFound(sessionId);
758
+ }
759
+ const history = await session.getHistory();
760
+ const firstUserMsg = history.find((m) => m.role === "user");
761
+ if (!firstUserMsg) {
762
+ this.logger.debug(`[SessionTitle] No user message found for session ${sessionId}`);
763
+ return null;
764
+ }
765
+ const userText = typeof firstUserMsg.content === "string" ? firstUserMsg.content : firstUserMsg.content?.filter((p) => p.type === "text").map((p) => p.text).join(" ");
766
+ if (!userText || !userText.trim()) {
767
+ this.logger.debug(`[SessionTitle] Empty user text for session ${sessionId}`);
768
+ return null;
769
+ }
770
+ const llmConfig = this.getEffectiveConfig(sessionId).llm;
771
+ const result = await generateSessionTitle(
772
+ llmConfig,
773
+ llmConfig.router,
774
+ this.toolManager,
775
+ this.systemPromptManager,
776
+ this.resourceManager,
777
+ userText,
778
+ this.logger
779
+ );
780
+ let title = result.title;
781
+ if (!title) {
782
+ title = deriveHeuristicTitle(userText);
783
+ if (title) {
784
+ this.logger.info(`[SessionTitle] Using heuristic title for ${sessionId}: ${title}`);
511
785
  } else {
512
- logger.info(`[SessionTitle] Generated LLM title for ${sessionId}: ${title}`);
786
+ this.logger.debug(`[SessionTitle] No suitable title derived for ${sessionId}`);
787
+ return null;
513
788
  }
514
- await this.sessionManager.setSessionTitle(sessionId, title, { ifUnsetOnly: true });
515
- this.agentEventBus.emit("dexto:sessionTitleUpdated", { sessionId, title });
516
- } catch (err) {
517
- logger.silly(`Title generation skipped/failed for ${sessionId}: ${String(err)}`);
789
+ } else {
790
+ this.logger.info(`[SessionTitle] Generated LLM title for ${sessionId}: ${title}`);
518
791
  }
792
+ await this.sessionManager.setSessionTitle(sessionId, title, { ifUnsetOnly: true });
793
+ return title;
519
794
  }
520
795
  /**
521
796
  * Gets the conversation history for a specific session.
@@ -536,14 +811,16 @@ class DextoAgent {
536
811
  return await Promise.all(
537
812
  history.map(async (message) => ({
538
813
  ...message,
539
- content: await expandBlobReferences(message.content, this.resourceManager).catch(
540
- (error) => {
541
- logger.warn(
542
- `Failed to expand blob references in message: ${error instanceof Error ? error.message : String(error)}`
543
- );
544
- return message.content;
545
- }
546
- )
814
+ content: await expandBlobReferences(
815
+ message.content,
816
+ this.resourceManager,
817
+ this.logger
818
+ ).catch((error) => {
819
+ this.logger.warn(
820
+ `Failed to expand blob references in message: ${error instanceof Error ? error.message : String(error)}`
821
+ );
822
+ return message.content;
823
+ })
547
824
  }))
548
825
  );
549
826
  }
@@ -569,88 +846,23 @@ class DextoAgent {
569
846
  return await this.searchService.searchSessions(query);
570
847
  }
571
848
  /**
572
- * Loads a session as the new "default" session for this agent.
573
- * All subsequent operations that don't specify a session ID will use this session.
574
- * This provides a clean "current working session" pattern for API users.
575
- *
576
- * @param sessionId The session ID to load as default, or null to reset to original default
577
- * @throws Error if session doesn't exist
578
- *
579
- * @example
580
- * ```typescript
581
- * // Load a specific session as default
582
- * await agent.loadSessionAsDefault('project-alpha');
583
- * await agent.run("What's the status?"); // Uses project-alpha session
584
- *
585
- * // Reset to original default
586
- * await agent.loadSessionAsDefault(null);
587
- * await agent.run("Hello"); // Uses 'default' session
588
- * ```
589
- */
590
- async loadSessionAsDefault(sessionId = null) {
591
- this.ensureStarted();
592
- if (sessionId === null) {
593
- this.currentDefaultSessionId = "default";
594
- this.defaultSession = null;
595
- logger.debug("Agent default session reset to original default");
596
- return;
597
- }
598
- const session = await this.sessionManager.getSession(sessionId);
599
- if (!session) {
600
- throw SessionError.notFound(sessionId);
601
- }
602
- this.currentDefaultSessionId = sessionId;
603
- this.defaultSession = null;
604
- logger.info(`Agent default session changed to: ${sessionId}`);
605
- }
606
- /**
607
- * Gets the currently loaded default session ID.
608
- * This reflects the session loaded via loadSession().
609
- *
610
- * @returns The current default session ID
611
- */
612
- getCurrentSessionId() {
613
- this.ensureStarted();
614
- return this.currentDefaultSessionId;
615
- }
616
- /**
617
- * Gets the currently loaded default session.
618
- * This respects the session loaded via loadSession().
619
- *
620
- * @returns The current default ChatSession
621
- */
622
- async getDefaultSession() {
623
- this.ensureStarted();
624
- if (!this.defaultSession || this.defaultSession.id !== this.currentDefaultSessionId) {
625
- this.defaultSession = await this.sessionManager.createSession(
626
- this.currentDefaultSessionId
627
- );
628
- }
629
- return this.defaultSession;
630
- }
631
- /**
632
- * Resets the conversation history for a specific session or the default session.
849
+ * Resets the conversation history for a specific session.
633
850
  * Keeps the session alive but the conversation history is cleared.
634
- * @param sessionId Optional session ID. If not provided, resets the currently loaded default session.
851
+ * @param sessionId Session ID (required)
635
852
  */
636
853
  async resetConversation(sessionId) {
637
854
  this.ensureStarted();
855
+ if (!sessionId || typeof sessionId !== "string") {
856
+ throw new Error("sessionId is required and must be a non-empty string");
857
+ }
638
858
  try {
639
- const targetSessionId = sessionId || this.currentDefaultSessionId;
640
- if (!sessionId) {
641
- if (!this.defaultSession || this.defaultSession.id !== this.currentDefaultSessionId) {
642
- this.defaultSession = await this.sessionManager.createSession(
643
- this.currentDefaultSessionId
644
- );
645
- }
646
- }
647
- await this.sessionManager.resetSession(targetSessionId);
648
- logger.info(`DextoAgent conversation reset for session: ${targetSessionId}`);
649
- this.agentEventBus.emit("dexto:conversationReset", {
650
- sessionId: targetSessionId
859
+ await this.sessionManager.resetSession(sessionId);
860
+ this.logger.info(`DextoAgent conversation reset for session: ${sessionId}`);
861
+ this.agentEventBus.emit("session:reset", {
862
+ sessionId
651
863
  });
652
864
  } catch (error) {
653
- logger.error(
865
+ this.logger.error(
654
866
  `Error during DextoAgent.resetConversation: ${error instanceof Error ? error.message : String(error)}`
655
867
  );
656
868
  throw error;
@@ -704,22 +916,24 @@ class DextoAgent {
704
916
  */
705
917
  async switchLLM(llmUpdates, sessionId) {
706
918
  this.ensureStarted();
707
- logger.debug(`DextoAgent.switchLLM: llmUpdates: ${safeStringify(llmUpdates)}`);
919
+ this.logger.debug(`DextoAgent.switchLLM: llmUpdates: ${safeStringify(llmUpdates)}`);
708
920
  const parseResult = LLMUpdatesSchema.safeParse(llmUpdates);
709
921
  if (!parseResult.success) {
710
922
  const validation = fail(zodToIssues(parseResult.error, "error"));
711
- ensureOk(validation);
923
+ ensureOk(validation, this.logger);
712
924
  throw new Error("Unreachable");
713
925
  }
714
926
  const validatedUpdates = parseResult.data;
715
927
  const currentLLMConfig = sessionId ? this.stateManager.getRuntimeConfig(sessionId).llm : this.stateManager.getRuntimeConfig().llm;
716
- const result = resolveAndValidateLLMConfig(currentLLMConfig, validatedUpdates);
717
- const validatedConfig = ensureOk(result);
928
+ const result = resolveAndValidateLLMConfig(currentLLMConfig, validatedUpdates, this.logger);
929
+ const validatedConfig = ensureOk(result, this.logger);
718
930
  await this.performLLMSwitch(validatedConfig, sessionId);
719
- logger.info(`DextoAgent.switchLLM: LLM switched to: ${safeStringify(validatedConfig)}`);
931
+ this.logger.info(
932
+ `DextoAgent.switchLLM: LLM switched to: ${safeStringify(validatedConfig)}`
933
+ );
720
934
  const warnings = result.issues.filter((issue) => issue.severity === "warning");
721
935
  if (warnings.length > 0) {
722
- logger.warn(
936
+ this.logger.warn(
723
937
  `LLM switch completed with warnings: ${warnings.map((w) => w.message).join(", ")}`
724
938
  );
725
939
  }
@@ -743,7 +957,7 @@ class DextoAgent {
743
957
  }
744
958
  await this.sessionManager.switchLLMForSpecificSession(validatedConfig, sessionScope);
745
959
  } else {
746
- await this.sessionManager.switchLLMForDefaultSession(validatedConfig);
960
+ this.logger.debug("LLM config updated at agent level (no active session switches)");
747
961
  }
748
962
  }
749
963
  /**
@@ -857,31 +1071,35 @@ class DextoAgent {
857
1071
  this.ensureStarted();
858
1072
  const existingServerNames = Object.keys(this.stateManager.getRuntimeConfig().mcpServers);
859
1073
  const validation = resolveAndValidateMcpServerConfig(name, config, existingServerNames);
860
- const validatedConfig = ensureOk(validation);
1074
+ const validatedConfig = ensureOk(validation, this.logger);
861
1075
  this.stateManager.addMcpServer(name, validatedConfig);
862
1076
  try {
863
1077
  await this.mcpManager.connectServer(name, validatedConfig);
864
1078
  await this.toolManager.refresh();
865
- this.agentEventBus.emit("dexto:mcpServerConnected", {
1079
+ this.agentEventBus.emit("mcp:server-connected", {
866
1080
  name,
867
1081
  success: true
868
1082
  });
869
- this.agentEventBus.emit("dexto:availableToolsUpdated", {
1083
+ this.agentEventBus.emit("tools:available-updated", {
870
1084
  tools: Object.keys(await this.toolManager.getAllTools()),
871
1085
  source: "mcp"
872
1086
  });
873
- logger.info(`DextoAgent: Successfully added and connected to MCP server '${name}'.`);
1087
+ this.logger.info(
1088
+ `DextoAgent: Successfully added and connected to MCP server '${name}'.`
1089
+ );
874
1090
  const warnings = validation.issues.filter((i) => i.severity === "warning");
875
1091
  if (warnings.length > 0) {
876
- logger.warn(
1092
+ this.logger.warn(
877
1093
  `MCP server connected with warnings: ${warnings.map((w) => w.message).join(", ")}`
878
1094
  );
879
1095
  }
880
1096
  } catch (error) {
881
1097
  const errorMessage = error instanceof Error ? error.message : String(error);
882
- logger.error(`DextoAgent: Failed to connect to MCP server '${name}': ${errorMessage}`);
1098
+ this.logger.error(
1099
+ `DextoAgent: Failed to connect to MCP server '${name}': ${errorMessage}`
1100
+ );
883
1101
  this.stateManager.removeMcpServer(name);
884
- this.agentEventBus.emit("dexto:mcpServerConnected", {
1102
+ this.agentEventBus.emit("mcp:server-connected", {
885
1103
  name,
886
1104
  success: false,
887
1105
  error: errorMessage
@@ -908,20 +1126,22 @@ class DextoAgent {
908
1126
  async restartMcpServer(name) {
909
1127
  this.ensureStarted();
910
1128
  try {
911
- logger.info(`DextoAgent: Restarting MCP server '${name}'...`);
1129
+ this.logger.info(`DextoAgent: Restarting MCP server '${name}'...`);
912
1130
  await this.mcpManager.restartServer(name);
913
1131
  await this.toolManager.refresh();
914
- this.agentEventBus.emit("dexto:mcpServerRestarted", {
1132
+ this.agentEventBus.emit("mcp:server-restarted", {
915
1133
  serverName: name
916
1134
  });
917
- this.agentEventBus.emit("dexto:availableToolsUpdated", {
1135
+ this.agentEventBus.emit("tools:available-updated", {
918
1136
  tools: Object.keys(await this.toolManager.getAllTools()),
919
1137
  source: "mcp"
920
1138
  });
921
- logger.info(`DextoAgent: Successfully restarted MCP server '${name}'.`);
1139
+ this.logger.info(`DextoAgent: Successfully restarted MCP server '${name}'.`);
922
1140
  } catch (error) {
923
1141
  const errorMessage = error instanceof Error ? error.message : String(error);
924
- logger.error(`DextoAgent: Failed to restart MCP server '${name}': ${errorMessage}`);
1142
+ this.logger.error(
1143
+ `DextoAgent: Failed to restart MCP server '${name}': ${errorMessage}`
1144
+ );
925
1145
  throw error;
926
1146
  }
927
1147
  }
@@ -1036,10 +1256,10 @@ class DextoAgent {
1036
1256
  */
1037
1257
  async getSystemPrompt() {
1038
1258
  this.ensureStarted();
1039
- const context = {
1259
+ const context2 = {
1040
1260
  mcpManager: this.mcpManager
1041
1261
  };
1042
- return await this.systemPromptManager.build(context);
1262
+ return await this.systemPromptManager.build(context2);
1043
1263
  }
1044
1264
  /**
1045
1265
  * Lists all available prompts from all providers (MCP, internal, starter, custom).
@@ -1113,7 +1333,8 @@ class DextoAgent {
1113
1333
  /**
1114
1334
  * Gets the effective configuration for a session or the default configuration.
1115
1335
  * @param sessionId Optional session ID. If not provided, returns default config.
1116
- * @returns The effective configuration object
1336
+ * @returns The effective configuration object (validated with defaults applied)
1337
+ * @remarks Requires agent to be started. Use `agent.config` for pre-start access.
1117
1338
  */
1118
1339
  getEffectiveConfig(sessionId) {
1119
1340
  this.ensureStarted();
@@ -1132,84 +1353,48 @@ class DextoAgent {
1132
1353
  return this.configPath;
1133
1354
  }
1134
1355
  /**
1135
- * Reloads the agent configuration from disk.
1136
- * This will re-read the config file, validate it, and detect what changed.
1137
- * Most configuration changes require a full agent restart to take effect.
1356
+ * Reloads the agent configuration with a new config object.
1357
+ * Validates the new config, detects what changed, and automatically
1358
+ * restarts the agent if necessary to apply the changes.
1138
1359
  *
1139
- * To apply changes: stop the agent and start it again with the new config.
1140
- *
1141
- * @returns Object containing list of changes that require restart
1142
- * @throws Error if config file cannot be read or is invalid
1360
+ * @param newConfig The new agent configuration to apply
1361
+ * @returns Object containing whether agent was restarted and list of changes applied
1362
+ * @throws Error if config is invalid or restart fails
1143
1363
  *
1144
1364
  * TODO: improve hot reload capabilites so that we don't always require a restart
1145
1365
  */
1146
- async reloadConfig() {
1147
- if (!this.configPath) {
1148
- throw AgentError.noConfigPath();
1149
- }
1150
- logger.info(`Reloading agent configuration from: ${this.configPath}`);
1366
+ async reload(newConfig) {
1367
+ this.logger.info("Reloading agent configuration");
1151
1368
  const oldConfig = this.config;
1152
- const newConfig = await loadAgentConfig(this.configPath);
1153
1369
  const validated = AgentConfigSchema.parse(newConfig);
1154
- const restartRequired = this.detectRestartRequiredChanges(oldConfig, validated);
1370
+ const changesApplied = this.detectConfigChanges(oldConfig, validated);
1155
1371
  this.config = validated;
1156
- if (restartRequired.length > 0) {
1157
- logger.warn(
1158
- `Configuration updated. Restart required to apply: ${restartRequired.join(", ")}`
1372
+ let restarted = false;
1373
+ if (changesApplied.length > 0) {
1374
+ this.logger.info(
1375
+ `Configuration changed. Restarting agent to apply: ${changesApplied.join(", ")}`
1159
1376
  );
1377
+ await this.restart();
1378
+ restarted = true;
1379
+ this.logger.info("Agent restarted successfully with new configuration");
1160
1380
  } else {
1161
- logger.info("Agent configuration reloaded successfully (no changes detected)");
1381
+ this.logger.info("Agent configuration reloaded successfully (no changes detected)");
1162
1382
  }
1163
1383
  return {
1164
- restartRequired
1384
+ restarted,
1385
+ changesApplied
1165
1386
  };
1166
1387
  }
1167
- /**
1168
- * Updates and saves the agent configuration to disk.
1169
- * This merges the updates with the raw config from disk, validates, and writes to file.
1170
- * IMPORTANT: This preserves environment variable placeholders (e.g., $OPENAI_API_KEY)
1171
- * to avoid leaking secrets into the config file.
1172
- * @param updates Partial configuration updates to apply
1173
- * @param targetPath Optional path to save to (defaults to current config path)
1174
- * @returns Object containing list of changes that require restart
1175
- * @throws Error if validation fails or file cannot be written
1176
- */
1177
- async updateAndSaveConfig(updates, targetPath) {
1178
- const path = targetPath || this.configPath;
1179
- if (!path) {
1180
- throw AgentError.noConfigPath();
1181
- }
1182
- logger.info(`Updating and saving agent configuration to: ${path}`);
1183
- const rawYaml = await fs.readFile(path, "utf-8");
1184
- const doc = parseDocument(rawYaml);
1185
- const rawConfig = doc.toJSON();
1186
- const updatedRawConfig = { ...rawConfig, ...updates };
1187
- const parsed = AgentConfigSchema.safeParse(updatedRawConfig);
1188
- if (!parsed.success) {
1189
- const result = fail(zodToIssues(parsed.error, "error"));
1190
- throw new DextoValidationError(result.issues);
1191
- }
1192
- for (const [key, value] of Object.entries(updates)) {
1193
- doc.set(key, value);
1194
- }
1195
- const yamlContent = String(doc);
1196
- const tmpPath = `${path}.tmp`;
1197
- await fs.writeFile(tmpPath, yamlContent, "utf-8");
1198
- await fs.rename(tmpPath, path);
1199
- const reloadResult = await this.reloadConfig();
1200
- logger.info(`Agent configuration saved to: ${path}`);
1201
- return reloadResult;
1202
- }
1203
1388
  /**
1204
1389
  * Detects configuration changes that require a full agent restart.
1390
+ * Pure comparison logic - no file I/O.
1205
1391
  * Returns an array of change descriptions.
1206
1392
  *
1207
1393
  * @param oldConfig Previous validated configuration
1208
1394
  * @param newConfig New validated configuration
1209
1395
  * @returns Array of restart-required change descriptions
1210
- * @private
1211
1396
  */
1212
- detectRestartRequiredChanges(oldConfig, newConfig) {
1397
+ detectConfigChanges(oldConfig, newConfig) {
1213
1398
  const changes = [];
1214
1399
  if (JSON.stringify(oldConfig.storage) !== JSON.stringify(newConfig.storage)) {
1215
1400
  changes.push("Storage backend");
@@ -1234,6 +1419,58 @@ class DextoAgent {
1234
1419
  }
1235
1420
  return changes;
1236
1421
  }
1422
+ // ============= APPROVAL HANDLER API =============
1423
+ /**
1424
+ * Set a custom approval handler for manual approval mode.
1425
+ *
1426
+ * When `toolConfirmation.mode` is set to 'manual', an approval handler must be
1427
+ * provided to process tool confirmation requests. The handler will be called
1428
+ * whenever a tool execution requires user approval.
1429
+ *
1430
+ * The handler receives an approval request and must return a promise that resolves
1431
+ * to an approval response with the user's decision (approved/denied/cancelled).
1432
+ *
1433
+ * @param handler The approval handler function
1434
+ *
1435
+ * @example
1436
+ * ```typescript
1437
+ * import { ApprovalStatus } from '@dexto/core';
1438
+ *
1439
+ * agent.setApprovalHandler(async (request) => {
1440
+ * // Present approval request to user (CLI, UI, webhook, etc.)
1441
+ * console.log(`Approve tool: ${request.metadata.toolName}?`);
1442
+ * console.log(`Args: ${JSON.stringify(request.metadata.args)}`);
1443
+ *
1444
+ * // Collect user's decision (this is just an example)
1445
+ * const approved = await getUserInput();
1446
+ *
1447
+ * return {
1448
+ * approvalId: request.approvalId,
1449
+ * status: approved ? ApprovalStatus.APPROVED : ApprovalStatus.DENIED,
1450
+ * sessionId: request.sessionId,
1451
+ * };
1452
+ * });
1453
+ * ```
1454
+ */
1455
+ setApprovalHandler(handler) {
1456
+ this.approvalHandler = handler;
1457
+ if (this._isStarted && this.services) {
1458
+ this.services.approvalManager.setHandler(handler);
1459
+ }
1460
+ this.logger.debug("Approval handler registered");
1461
+ }
1462
+ /**
1463
+ * Clear the current approval handler.
1464
+ *
1465
+ * After calling this, manual approval mode will fail if a tool requires approval.
1466
+ */
1467
+ clearApprovalHandler() {
1468
+ this.approvalHandler = void 0;
1469
+ if (this._isStarted && this.services) {
1470
+ this.services.approvalManager.clearHandler();
1471
+ }
1472
+ this.logger.debug("Approval handler cleared");
1473
+ }
1237
1474
  // ============= AGENT MANAGEMENT =============
1238
1475
  // Note: Agent management methods have been moved to the Dexto orchestrator class.
1239
1476
  // See: /packages/core/src/Dexto.ts