@amodalai/runtime 0.1.26 → 0.2.1

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 (445) hide show
  1. package/dist/src/__fixtures__/README.md +88 -0
  2. package/dist/src/__fixtures__/e2e.test.js +211 -0
  3. package/dist/src/__fixtures__/e2e.test.js.map +1 -0
  4. package/dist/src/__fixtures__/smoke-agent/amodal.json +11 -0
  5. package/dist/src/__fixtures__/smoke-agent/automations/delivery-callback-test.json +9 -0
  6. package/dist/src/__fixtures__/smoke-agent/automations/test-auto.md +5 -0
  7. package/dist/src/__fixtures__/smoke-agent/connections/mock-api/access.json +11 -0
  8. package/dist/src/__fixtures__/smoke-agent/connections/mock-api/spec.json +4 -0
  9. package/dist/src/__fixtures__/smoke-agent/connections/mock-api/surface.md +9 -0
  10. package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/access.json +3 -0
  11. package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/spec.json +8 -0
  12. package/dist/src/__fixtures__/smoke-agent/evals/basic-eval.md +12 -0
  13. package/dist/src/__fixtures__/smoke-agent/knowledge/test-knowledge.md +3 -0
  14. package/dist/src/__fixtures__/smoke-agent/skills/test-skill/SKILL.md +11 -0
  15. package/dist/src/__fixtures__/smoke-agent/stores/test-items.json +11 -0
  16. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.d.ts +18 -0
  17. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.js +22 -0
  18. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.js.map +1 -0
  19. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/tool.json +17 -0
  20. package/dist/src/__fixtures__/smoke.test.js +1404 -0
  21. package/dist/src/__fixtures__/smoke.test.js.map +1 -0
  22. package/dist/src/__fixtures__/test-env.d.ts +27 -0
  23. package/dist/src/__fixtures__/test-env.js +64 -0
  24. package/dist/src/__fixtures__/test-env.js.map +1 -0
  25. package/dist/src/__fixtures__/test-helpers.d.ts +30 -0
  26. package/dist/src/__fixtures__/test-helpers.js +120 -0
  27. package/dist/src/__fixtures__/test-helpers.js.map +1 -0
  28. package/dist/src/__tests__/test-providers.d.ts +40 -0
  29. package/dist/src/__tests__/test-providers.js +61 -0
  30. package/dist/src/__tests__/test-providers.js.map +1 -0
  31. package/dist/src/agent/agent-types.d.ts +22 -0
  32. package/dist/src/agent/agent-types.js.map +1 -1
  33. package/dist/src/agent/automation-bridge.d.ts +9 -0
  34. package/dist/src/agent/automation-bridge.js +26 -0
  35. package/dist/src/agent/automation-bridge.js.map +1 -1
  36. package/dist/src/agent/automation-bridge.test.js +63 -0
  37. package/dist/src/agent/automation-bridge.test.js.map +1 -1
  38. package/dist/src/agent/local-server.d.ts +1 -8
  39. package/dist/src/agent/local-server.js +398 -163
  40. package/dist/src/agent/local-server.js.map +1 -1
  41. package/dist/src/agent/local-server.test.js +14 -8
  42. package/dist/src/agent/local-server.test.js.map +1 -1
  43. package/dist/src/agent/loop-types.d.ts +254 -0
  44. package/dist/src/agent/loop-types.js +24 -0
  45. package/dist/src/agent/loop-types.js.map +1 -0
  46. package/dist/src/agent/loop.d.ts +31 -0
  47. package/dist/src/agent/loop.js +152 -0
  48. package/dist/src/agent/loop.js.map +1 -0
  49. package/dist/src/agent/loop.test.js +1594 -0
  50. package/dist/src/agent/loop.test.js.map +1 -0
  51. package/dist/src/agent/mcp-config.d.ts +28 -0
  52. package/dist/src/agent/mcp-config.js +57 -0
  53. package/dist/src/agent/mcp-config.js.map +1 -0
  54. package/dist/src/agent/page-builder.js +6 -1
  55. package/dist/src/agent/page-builder.js.map +1 -1
  56. package/dist/src/agent/proactive/delivery-router.d.ts +68 -0
  57. package/dist/src/agent/proactive/delivery-router.js +337 -0
  58. package/dist/src/agent/proactive/delivery-router.js.map +1 -0
  59. package/dist/src/agent/{stores-e2e.test.d.ts → proactive/delivery-router.test.d.ts} +1 -1
  60. package/dist/src/agent/proactive/delivery-router.test.js +455 -0
  61. package/dist/src/agent/proactive/delivery-router.test.js.map +1 -0
  62. package/dist/src/agent/proactive/proactive-runner.d.ts +46 -8
  63. package/dist/src/agent/proactive/proactive-runner.js +67 -37
  64. package/dist/src/agent/proactive/proactive-runner.js.map +1 -1
  65. package/dist/src/agent/proactive/proactive-runner.test.d.ts +1 -1
  66. package/dist/src/agent/proactive/proactive-runner.test.js +73 -87
  67. package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -1
  68. package/dist/src/agent/routes/admin-chat-abort.test.d.ts +6 -0
  69. package/dist/src/agent/routes/admin-chat-abort.test.js +206 -0
  70. package/dist/src/agent/routes/admin-chat-abort.test.js.map +1 -0
  71. package/dist/src/agent/routes/admin-chat.d.ts +15 -3
  72. package/dist/src/agent/routes/admin-chat.js +61 -18
  73. package/dist/src/agent/routes/admin-chat.js.map +1 -1
  74. package/dist/src/agent/routes/automations.js +5 -6
  75. package/dist/src/agent/routes/automations.js.map +1 -1
  76. package/dist/src/agent/routes/evals.d.ts +3 -2
  77. package/dist/src/agent/routes/evals.js +25 -12
  78. package/dist/src/agent/routes/evals.js.map +1 -1
  79. package/dist/src/agent/routes/files.js +7 -9
  80. package/dist/src/agent/routes/files.js.map +1 -1
  81. package/dist/src/agent/routes/inspect.d.ts +6 -2
  82. package/dist/src/agent/routes/inspect.js +31 -17
  83. package/dist/src/agent/routes/inspect.js.map +1 -1
  84. package/dist/src/agent/routes/inspect.test.js +18 -42
  85. package/dist/src/agent/routes/inspect.test.js.map +1 -1
  86. package/dist/src/agent/routes/stores.js +9 -12
  87. package/dist/src/agent/routes/stores.js.map +1 -1
  88. package/dist/src/agent/routes/task.d.ts +15 -3
  89. package/dist/src/agent/routes/task.js +16 -7
  90. package/dist/src/agent/routes/task.js.map +1 -1
  91. package/dist/src/agent/routes/task.test.d.ts +1 -1
  92. package/dist/src/agent/routes/task.test.js +68 -53
  93. package/dist/src/agent/routes/task.test.js.map +1 -1
  94. package/dist/src/agent/routes/webhooks.js +12 -3
  95. package/dist/src/agent/routes/webhooks.js.map +1 -1
  96. package/dist/src/agent/snapshot-server.d.ts +2 -22
  97. package/dist/src/agent/snapshot-server.js +48 -27
  98. package/dist/src/agent/snapshot-server.js.map +1 -1
  99. package/dist/src/agent/states/compacting.d.ts +14 -0
  100. package/dist/src/agent/states/compacting.js +260 -0
  101. package/dist/src/agent/states/compacting.js.map +1 -0
  102. package/dist/src/agent/states/confirming.d.ts +10 -0
  103. package/dist/src/agent/states/confirming.js +79 -0
  104. package/dist/src/agent/states/confirming.js.map +1 -0
  105. package/dist/src/agent/states/dispatching.d.ts +18 -0
  106. package/dist/src/agent/states/dispatching.js +285 -0
  107. package/dist/src/agent/states/dispatching.js.map +1 -0
  108. package/dist/src/agent/states/executing.d.ts +21 -0
  109. package/dist/src/agent/states/executing.js +452 -0
  110. package/dist/src/agent/states/executing.js.map +1 -0
  111. package/dist/src/agent/states/streaming.d.ts +10 -0
  112. package/dist/src/agent/states/streaming.js +169 -0
  113. package/dist/src/agent/states/streaming.js.map +1 -0
  114. package/dist/src/agent/states/thinking.d.ts +13 -0
  115. package/dist/src/agent/states/thinking.js +450 -0
  116. package/dist/src/agent/states/thinking.js.map +1 -0
  117. package/dist/src/agent/token-estimate.d.ts +31 -0
  118. package/dist/src/agent/token-estimate.js +34 -0
  119. package/dist/src/agent/token-estimate.js.map +1 -0
  120. package/dist/src/agent/token-estimate.test.d.ts +6 -0
  121. package/dist/src/agent/token-estimate.test.js +44 -0
  122. package/dist/src/agent/token-estimate.test.js.map +1 -0
  123. package/dist/src/agent/tool-executor-local.js +9 -18
  124. package/dist/src/agent/tool-executor-local.js.map +1 -1
  125. package/dist/src/agent/tool-executor-local.test.js +3 -5
  126. package/dist/src/agent/tool-executor-local.test.js.map +1 -1
  127. package/dist/src/api/create-agent.d.ts +15 -0
  128. package/dist/src/api/create-agent.js +134 -0
  129. package/dist/src/api/create-agent.js.map +1 -0
  130. package/dist/src/api/types.d.ts +66 -0
  131. package/dist/src/api/types.js +7 -0
  132. package/dist/src/api/types.js.map +1 -0
  133. package/dist/src/context/compiler.d.ts +13 -0
  134. package/dist/src/context/compiler.js +358 -0
  135. package/dist/src/context/compiler.js.map +1 -0
  136. package/dist/src/context/compiler.test.d.ts +6 -0
  137. package/dist/src/context/compiler.test.js +532 -0
  138. package/dist/src/context/compiler.test.js.map +1 -0
  139. package/dist/src/context/types.d.ts +110 -0
  140. package/dist/src/context/types.js +7 -0
  141. package/dist/src/context/types.js.map +1 -0
  142. package/dist/src/env-ref.d.ts +13 -0
  143. package/dist/src/env-ref.js +31 -0
  144. package/dist/src/env-ref.js.map +1 -0
  145. package/dist/src/env-ref.test.d.ts +6 -0
  146. package/dist/src/env-ref.test.js +34 -0
  147. package/dist/src/env-ref.test.js.map +1 -0
  148. package/dist/src/errors.d.ts +15 -0
  149. package/dist/src/errors.js +22 -0
  150. package/dist/src/errors.js.map +1 -1
  151. package/dist/src/errors.test.js +2 -2
  152. package/dist/src/errors.test.js.map +1 -1
  153. package/dist/src/events/event-bus.d.ts +54 -0
  154. package/dist/src/events/event-bus.js +84 -0
  155. package/dist/src/events/event-bus.js.map +1 -0
  156. package/dist/src/events/event-bus.test.d.ts +6 -0
  157. package/dist/src/events/event-bus.test.js +112 -0
  158. package/dist/src/events/event-bus.test.js.map +1 -0
  159. package/dist/src/events/events-route.d.ts +36 -0
  160. package/dist/src/events/events-route.js +80 -0
  161. package/dist/src/events/events-route.js.map +1 -0
  162. package/dist/src/events/events-route.test.d.ts +6 -0
  163. package/dist/src/events/events-route.test.js +134 -0
  164. package/dist/src/events/events-route.test.js.map +1 -0
  165. package/dist/src/events/store-event-wrapper.d.ts +19 -0
  166. package/dist/src/events/store-event-wrapper.js +57 -0
  167. package/dist/src/events/store-event-wrapper.js.map +1 -0
  168. package/dist/src/events/store-event-wrapper.test.d.ts +6 -0
  169. package/dist/src/events/store-event-wrapper.test.js +91 -0
  170. package/dist/src/events/store-event-wrapper.test.js.map +1 -0
  171. package/dist/src/index.d.ts +33 -6
  172. package/dist/src/index.js +35 -21
  173. package/dist/src/index.js.map +1 -1
  174. package/dist/src/middleware/auth.d.ts +0 -2
  175. package/dist/src/middleware/auth.js.map +1 -1
  176. package/dist/src/providers/create-provider.d.ts +23 -0
  177. package/dist/src/providers/create-provider.js +185 -0
  178. package/dist/src/providers/create-provider.js.map +1 -0
  179. package/dist/src/providers/create-provider.test.d.ts +6 -0
  180. package/dist/src/providers/create-provider.test.js +95 -0
  181. package/dist/src/providers/create-provider.test.js.map +1 -0
  182. package/dist/src/providers/failover.d.ts +38 -0
  183. package/dist/src/providers/failover.js +147 -0
  184. package/dist/src/providers/failover.js.map +1 -0
  185. package/dist/src/providers/failover.test.d.ts +6 -0
  186. package/dist/src/providers/failover.test.js +169 -0
  187. package/dist/src/providers/failover.test.js.map +1 -0
  188. package/dist/src/providers/search-provider.d.ts +64 -0
  189. package/dist/src/providers/search-provider.js +174 -0
  190. package/dist/src/providers/search-provider.js.map +1 -0
  191. package/dist/src/providers/types.d.ts +118 -0
  192. package/dist/src/providers/types.js +7 -0
  193. package/dist/src/providers/types.js.map +1 -0
  194. package/dist/src/routes/ai-stream.d.ts +28 -10
  195. package/dist/src/routes/ai-stream.js +85 -41
  196. package/dist/src/routes/ai-stream.js.map +1 -1
  197. package/dist/src/routes/chat-new.test.d.ts +6 -0
  198. package/dist/src/routes/chat-new.test.js +107 -0
  199. package/dist/src/routes/chat-new.test.js.map +1 -0
  200. package/dist/src/routes/chat-stream-new.test.d.ts +6 -0
  201. package/dist/src/routes/chat-stream-new.test.js +135 -0
  202. package/dist/src/routes/chat-stream-new.test.js.map +1 -0
  203. package/dist/src/routes/chat-stream.d.ts +20 -4
  204. package/dist/src/routes/chat-stream.js +49 -29
  205. package/dist/src/routes/chat-stream.js.map +1 -1
  206. package/dist/src/routes/chat.d.ts +19 -4
  207. package/dist/src/routes/chat.js +62 -23
  208. package/dist/src/routes/chat.js.map +1 -1
  209. package/dist/src/routes/health.d.ts +3 -2
  210. package/dist/src/routes/health.js.map +1 -1
  211. package/dist/src/routes/route-helpers.d.ts +50 -0
  212. package/dist/src/routes/route-helpers.js +80 -0
  213. package/dist/src/routes/route-helpers.js.map +1 -0
  214. package/dist/src/routes/session-resolver.d.ts +77 -0
  215. package/dist/src/routes/session-resolver.js +109 -0
  216. package/dist/src/routes/session-resolver.js.map +1 -0
  217. package/dist/src/routes/session-resolver.test.d.ts +6 -0
  218. package/dist/src/routes/session-resolver.test.js +207 -0
  219. package/dist/src/routes/session-resolver.test.js.map +1 -0
  220. package/dist/src/routes/webhooks.d.ts +3 -1
  221. package/dist/src/routes/webhooks.js +12 -4
  222. package/dist/src/routes/webhooks.js.map +1 -1
  223. package/dist/src/security/permission-checker.d.ts +80 -0
  224. package/dist/src/security/permission-checker.js +75 -0
  225. package/dist/src/security/permission-checker.js.map +1 -0
  226. package/dist/src/security/permission-checker.test.d.ts +6 -0
  227. package/dist/src/security/permission-checker.test.js +208 -0
  228. package/dist/src/security/permission-checker.test.js.map +1 -0
  229. package/dist/src/server.d.ts +18 -11
  230. package/dist/src/server.js +46 -46
  231. package/dist/src/server.js.map +1 -1
  232. package/dist/src/server.test.d.ts +1 -1
  233. package/dist/src/server.test.js +6 -144
  234. package/dist/src/server.test.js.map +1 -1
  235. package/dist/src/session/drizzle-session-store.d.ts +56 -0
  236. package/dist/src/session/drizzle-session-store.js +203 -0
  237. package/dist/src/session/drizzle-session-store.js.map +1 -0
  238. package/dist/src/session/manager.d.ts +101 -0
  239. package/dist/src/session/manager.js +394 -0
  240. package/dist/src/session/manager.js.map +1 -0
  241. package/dist/src/session/manager.test.d.ts +6 -0
  242. package/dist/src/session/manager.test.js +309 -0
  243. package/dist/src/session/manager.test.js.map +1 -0
  244. package/dist/src/session/pglite-session-store.d.ts +23 -0
  245. package/dist/src/session/pglite-session-store.js +70 -0
  246. package/dist/src/session/pglite-session-store.js.map +1 -0
  247. package/dist/src/session/postgres-session-store.d.ts +44 -0
  248. package/dist/src/session/postgres-session-store.js +138 -0
  249. package/dist/src/session/postgres-session-store.js.map +1 -0
  250. package/dist/src/session/session-builder.d.ts +69 -0
  251. package/dist/src/session/session-builder.js +384 -0
  252. package/dist/src/session/session-builder.js.map +1 -0
  253. package/dist/src/session/session-builder.test.d.ts +6 -0
  254. package/dist/src/session/session-builder.test.js +350 -0
  255. package/dist/src/session/session-builder.test.js.map +1 -0
  256. package/dist/src/session/session-store-selector.d.ts +49 -0
  257. package/dist/src/session/session-store-selector.js +60 -0
  258. package/dist/src/session/session-store-selector.js.map +1 -0
  259. package/dist/src/session/session-store-selector.test.d.ts +6 -0
  260. package/dist/src/session/session-store-selector.test.js +79 -0
  261. package/dist/src/session/session-store-selector.test.js.map +1 -0
  262. package/dist/src/session/store.d.ts +171 -0
  263. package/dist/src/session/store.js +155 -0
  264. package/dist/src/session/store.js.map +1 -0
  265. package/dist/src/session/store.test.d.ts +6 -0
  266. package/dist/src/session/store.test.js +423 -0
  267. package/dist/src/session/store.test.js.map +1 -0
  268. package/dist/src/session/stream-hooks.d.ts +39 -0
  269. package/dist/src/session/stream-hooks.js +7 -0
  270. package/dist/src/session/stream-hooks.js.map +1 -0
  271. package/dist/src/session/tool-context-factory.d.ts +61 -0
  272. package/dist/src/session/tool-context-factory.js +189 -0
  273. package/dist/src/session/tool-context-factory.js.map +1 -0
  274. package/dist/src/session/tool-context-factory.test.d.ts +6 -0
  275. package/dist/src/session/tool-context-factory.test.js +284 -0
  276. package/dist/src/session/tool-context-factory.test.js.map +1 -0
  277. package/dist/src/session/types.d.ts +195 -0
  278. package/dist/src/session/types.js +7 -0
  279. package/dist/src/session/types.js.map +1 -0
  280. package/dist/src/stores/drizzle-store-backend.d.ts +49 -0
  281. package/dist/src/stores/drizzle-store-backend.js +306 -0
  282. package/dist/src/stores/drizzle-store-backend.js.map +1 -0
  283. package/dist/src/stores/drizzle-store-backend.test.d.ts +6 -0
  284. package/dist/src/stores/drizzle-store-backend.test.js +215 -0
  285. package/dist/src/stores/drizzle-store-backend.test.js.map +1 -0
  286. package/dist/src/stores/index.d.ts +4 -0
  287. package/dist/src/stores/index.js +2 -0
  288. package/dist/src/stores/index.js.map +1 -1
  289. package/dist/src/stores/pglite-store-backend.d.ts +16 -19
  290. package/dist/src/stores/pglite-store-backend.js +85 -239
  291. package/dist/src/stores/pglite-store-backend.js.map +1 -1
  292. package/dist/src/stores/postgres-store-backend.d.ts +30 -0
  293. package/dist/src/stores/postgres-store-backend.js +100 -0
  294. package/dist/src/stores/postgres-store-backend.js.map +1 -0
  295. package/dist/src/stores/schema.d.ts +457 -0
  296. package/dist/src/stores/schema.js +59 -0
  297. package/dist/src/stores/schema.js.map +1 -0
  298. package/dist/src/tools/admin-file-tools.d.ts +42 -0
  299. package/dist/src/tools/admin-file-tools.js +714 -0
  300. package/dist/src/tools/admin-file-tools.js.map +1 -0
  301. package/dist/src/tools/admin-file-tools.test.d.ts +6 -0
  302. package/dist/src/tools/admin-file-tools.test.js +521 -0
  303. package/dist/src/tools/admin-file-tools.test.js.map +1 -0
  304. package/dist/src/tools/custom-tool-adapter.d.ts +41 -0
  305. package/dist/src/tools/custom-tool-adapter.js +190 -0
  306. package/dist/src/tools/custom-tool-adapter.js.map +1 -0
  307. package/dist/src/tools/custom-tool-adapter.test.d.ts +6 -0
  308. package/dist/src/tools/custom-tool-adapter.test.js +243 -0
  309. package/dist/src/tools/custom-tool-adapter.test.js.map +1 -0
  310. package/dist/src/tools/dispatch-tool.d.ts +52 -0
  311. package/dist/src/tools/dispatch-tool.js +71 -0
  312. package/dist/src/tools/dispatch-tool.js.map +1 -0
  313. package/dist/src/tools/dispatch-tool.test.d.ts +6 -0
  314. package/dist/src/tools/dispatch-tool.test.js +75 -0
  315. package/dist/src/tools/dispatch-tool.test.js.map +1 -0
  316. package/dist/src/tools/fetch-url-tool.d.ts +23 -0
  317. package/dist/src/tools/fetch-url-tool.js +333 -0
  318. package/dist/src/tools/fetch-url-tool.js.map +1 -0
  319. package/dist/src/tools/fetch-url-tool.test.d.ts +6 -0
  320. package/dist/src/tools/fetch-url-tool.test.js +228 -0
  321. package/dist/src/tools/fetch-url-tool.test.js.map +1 -0
  322. package/dist/src/tools/mcp-tool-adapter.d.ts +18 -0
  323. package/dist/src/tools/mcp-tool-adapter.js +135 -0
  324. package/dist/src/tools/mcp-tool-adapter.js.map +1 -0
  325. package/dist/src/tools/mcp-tool-adapter.test.d.ts +6 -0
  326. package/dist/src/tools/mcp-tool-adapter.test.js +226 -0
  327. package/dist/src/tools/mcp-tool-adapter.test.js.map +1 -0
  328. package/dist/src/tools/registry.d.ts +25 -0
  329. package/dist/src/tools/registry.js +72 -0
  330. package/dist/src/tools/registry.js.map +1 -0
  331. package/dist/src/tools/registry.test.d.ts +6 -0
  332. package/dist/src/tools/registry.test.js +120 -0
  333. package/dist/src/tools/registry.test.js.map +1 -0
  334. package/dist/src/tools/request-tool.d.ts +42 -0
  335. package/dist/src/tools/request-tool.js +190 -0
  336. package/dist/src/tools/request-tool.js.map +1 -0
  337. package/dist/src/tools/request-tool.test.d.ts +6 -0
  338. package/dist/src/tools/request-tool.test.js +253 -0
  339. package/dist/src/tools/request-tool.test.js.map +1 -0
  340. package/dist/src/tools/store-tools.d.ts +29 -0
  341. package/dist/src/tools/store-tools.js +224 -0
  342. package/dist/src/tools/store-tools.js.map +1 -0
  343. package/dist/src/tools/store-tools.test.d.ts +6 -0
  344. package/dist/src/tools/store-tools.test.js +215 -0
  345. package/dist/src/tools/store-tools.test.js.map +1 -0
  346. package/dist/src/tools/types.d.ts +129 -0
  347. package/dist/src/tools/types.js +7 -0
  348. package/dist/src/tools/types.js.map +1 -0
  349. package/dist/src/tools/web-search-tool.d.ts +31 -0
  350. package/dist/src/tools/web-search-tool.js +170 -0
  351. package/dist/src/tools/web-search-tool.js.map +1 -0
  352. package/dist/src/tools/web-search-tool.test.d.ts +6 -0
  353. package/dist/src/tools/web-search-tool.test.js +153 -0
  354. package/dist/src/tools/web-search-tool.test.js.map +1 -0
  355. package/dist/src/tools/web-tools-shared.d.ts +21 -0
  356. package/dist/src/tools/web-tools-shared.js +32 -0
  357. package/dist/src/tools/web-tools-shared.js.map +1 -0
  358. package/dist/src/types.d.ts +40 -12
  359. package/dist/src/types.js +16 -2
  360. package/dist/src/types.js.map +1 -1
  361. package/dist/tsconfig.tsbuildinfo +1 -1
  362. package/package.json +27 -4
  363. package/dist/src/__tests__/sse-contract.test.js +0 -464
  364. package/dist/src/__tests__/sse-contract.test.js.map +0 -1
  365. package/dist/src/__tests__/tools.test.js +0 -583
  366. package/dist/src/__tests__/tools.test.js.map +0 -1
  367. package/dist/src/agent/agent-runner.d.ts +0 -33
  368. package/dist/src/agent/agent-runner.js +0 -1040
  369. package/dist/src/agent/agent-runner.js.map +0 -1
  370. package/dist/src/agent/custom-tools-e2e.test.d.ts +0 -6
  371. package/dist/src/agent/custom-tools-e2e.test.js +0 -566
  372. package/dist/src/agent/custom-tools-e2e.test.js.map +0 -1
  373. package/dist/src/agent/request-helper.d.ts +0 -16
  374. package/dist/src/agent/request-helper.js +0 -96
  375. package/dist/src/agent/request-helper.js.map +0 -1
  376. package/dist/src/agent/session-store.d.ts +0 -62
  377. package/dist/src/agent/session-store.js +0 -151
  378. package/dist/src/agent/session-store.js.map +0 -1
  379. package/dist/src/agent/stores-e2e.test.js +0 -433
  380. package/dist/src/agent/stores-e2e.test.js.map +0 -1
  381. package/dist/src/agent/tool-context-builder.d.ts +0 -11
  382. package/dist/src/agent/tool-context-builder.js +0 -102
  383. package/dist/src/agent/tool-context-builder.js.map +0 -1
  384. package/dist/src/agent/tool-context-builder.test.d.ts +0 -6
  385. package/dist/src/agent/tool-context-builder.test.js +0 -152
  386. package/dist/src/agent/tool-context-builder.test.js.map +0 -1
  387. package/dist/src/agent/write-repo-file.test.js +0 -270
  388. package/dist/src/agent/write-repo-file.test.js.map +0 -1
  389. package/dist/src/cron/heartbeat-runner.d.ts +0 -21
  390. package/dist/src/cron/heartbeat-runner.js +0 -79
  391. package/dist/src/cron/heartbeat-runner.js.map +0 -1
  392. package/dist/src/cron/heartbeat-runner.test.d.ts +0 -6
  393. package/dist/src/cron/heartbeat-runner.test.js +0 -120
  394. package/dist/src/cron/heartbeat-runner.test.js.map +0 -1
  395. package/dist/src/cron/heartbeat-scheduler.d.ts +0 -26
  396. package/dist/src/cron/heartbeat-scheduler.js +0 -55
  397. package/dist/src/cron/heartbeat-scheduler.js.map +0 -1
  398. package/dist/src/cron/heartbeat-scheduler.test.d.ts +0 -6
  399. package/dist/src/cron/heartbeat-scheduler.test.js +0 -61
  400. package/dist/src/cron/heartbeat-scheduler.test.js.map +0 -1
  401. package/dist/src/routes/ai-stream.test.d.ts +0 -6
  402. package/dist/src/routes/ai-stream.test.js +0 -586
  403. package/dist/src/routes/ai-stream.test.js.map +0 -1
  404. package/dist/src/routes/ask-user-response.d.ts +0 -30
  405. package/dist/src/routes/ask-user-response.js +0 -61
  406. package/dist/src/routes/ask-user-response.js.map +0 -1
  407. package/dist/src/routes/ask-user-response.test.d.ts +0 -6
  408. package/dist/src/routes/ask-user-response.test.js +0 -88
  409. package/dist/src/routes/ask-user-response.test.js.map +0 -1
  410. package/dist/src/routes/chat-stream.test.d.ts +0 -6
  411. package/dist/src/routes/chat-stream.test.js +0 -155
  412. package/dist/src/routes/chat-stream.test.js.map +0 -1
  413. package/dist/src/routes/chat.test.d.ts +0 -6
  414. package/dist/src/routes/chat.test.js +0 -99
  415. package/dist/src/routes/chat.test.js.map +0 -1
  416. package/dist/src/routes/widget-actions.d.ts +0 -49
  417. package/dist/src/routes/widget-actions.js +0 -78
  418. package/dist/src/routes/widget-actions.js.map +0 -1
  419. package/dist/src/session/admin-file-tools.d.ts +0 -136
  420. package/dist/src/session/admin-file-tools.js +0 -240
  421. package/dist/src/session/admin-file-tools.js.map +0 -1
  422. package/dist/src/session/custom-tool-adapter.d.ts +0 -74
  423. package/dist/src/session/custom-tool-adapter.js +0 -180
  424. package/dist/src/session/custom-tool-adapter.js.map +0 -1
  425. package/dist/src/session/history-converter.d.ts +0 -21
  426. package/dist/src/session/history-converter.js +0 -59
  427. package/dist/src/session/history-converter.js.map +0 -1
  428. package/dist/src/session/history-converter.test.d.ts +0 -6
  429. package/dist/src/session/history-converter.test.js +0 -130
  430. package/dist/src/session/history-converter.test.js.map +0 -1
  431. package/dist/src/session/session-manager.d.ts +0 -219
  432. package/dist/src/session/session-manager.js +0 -915
  433. package/dist/src/session/session-manager.js.map +0 -1
  434. package/dist/src/session/session-manager.test.d.ts +0 -6
  435. package/dist/src/session/session-manager.test.js +0 -455
  436. package/dist/src/session/session-manager.test.js.map +0 -1
  437. package/dist/src/session/session-runner.d.ts +0 -45
  438. package/dist/src/session/session-runner.js +0 -719
  439. package/dist/src/session/session-runner.js.map +0 -1
  440. package/dist/src/session/session-runner.test.d.ts +0 -6
  441. package/dist/src/session/session-runner.test.js +0 -834
  442. package/dist/src/session/session-runner.test.js.map +0 -1
  443. /package/dist/src/{__tests__/sse-contract.test.d.ts → __fixtures__/e2e.test.d.ts} +0 -0
  444. /package/dist/src/{__tests__/tools.test.d.ts → __fixtures__/smoke.test.d.ts} +0 -0
  445. /package/dist/src/agent/{write-repo-file.test.d.ts → loop.test.d.ts} +0 -0
@@ -1,915 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Amodal Labs, Inc.
4
- * SPDX-License-Identifier: MIT
5
- */
6
- import { randomUUID } from 'node:crypto';
7
- import { LOCAL_APP_ID } from '../constants.js';
8
- import { AmodalConfig, Scheduler, ROOT_SCHEDULER_ID, ApprovalMode, PolicyDecision, buildDefaultPrompt, resolveScopeLabels, generateFieldGuidance, generateAlternativeLookupGuidance, PlanModeManager, McpManager, ensureAdminAgent, loadAdminAgent, } from '@amodalai/core';
9
- import { convertSessionMessagesToHistory } from './history-converter.js';
10
- import { log } from '../logger.js';
11
- /**
12
- * Resolve env: references in a string record.
13
- * "env:VAR_NAME" → process.env.VAR_NAME value
14
- */
15
- function resolveEnvRefs(record) {
16
- const resolved = {};
17
- for (const [key, value] of Object.entries(record)) {
18
- if (value.startsWith('env:')) {
19
- const envVar = value.slice(4);
20
- resolved[key] = process.env[envVar] ?? '';
21
- }
22
- else {
23
- resolved[key] = value;
24
- }
25
- }
26
- return resolved;
27
- }
28
- function serializeBundle(bundle) {
29
- // Convert Map fields to plain objects for JSON serialization
30
- const serializable = {
31
- ...bundle,
32
- connections: Object.fromEntries(bundle.connections),
33
- };
34
- return JSON.stringify(serializable, null, 2);
35
- }
36
- const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
37
- const DEFAULT_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
38
- const ASK_USER_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
39
- /**
40
- * Manages per-request sessions: creates Config + GeminiClient + Scheduler
41
- * instances, tracks them by ID, and cleans up expired sessions.
42
- */
43
- export class SessionManager {
44
- sessions = new Map();
45
- baseParams;
46
- ttlMs;
47
- bundle;
48
- toolExecutor;
49
- shellExecutor;
50
- sharedStoreBackend;
51
- sessionStore;
52
- bundleProvider;
53
- cleanupTimer = null;
54
- /** Deduplicates concurrent hydration requests for the same conversation */
55
- pendingHydrations = new Map();
56
- /** Shared MCP manager for all sessions (lazy-initialized, reused) */
57
- sharedMcpManager;
58
- /** Persistent MCP manager for inspect operations (lazy-initialized) */
59
- inspectMcp;
60
- inspectMcpInitialized = false;
61
- constructor(options) {
62
- this.baseParams = options.baseParams;
63
- this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
64
- this.bundle = options.bundle;
65
- if (this.bundle) {
66
- log.debug(`Bundle loaded:\n${serializeBundle(this.bundle)}`, 'session');
67
- }
68
- this.toolExecutor = options.toolExecutor;
69
- this.shellExecutor = options.shellExecutor;
70
- this.sharedStoreBackend = options.storeBackend;
71
- this.sessionStore = options.sessionStore;
72
- this.bundleProvider = options.bundleProvider;
73
- const cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
74
- this.cleanupTimer = setInterval(() => void this.cleanup(), cleanupIntervalMs);
75
- // Don't keep the process alive just for cleanup
76
- this.cleanupTimer.unref();
77
- }
78
- /**
79
- * Create a new session with optional role override and auth context.
80
- * When auth context is provided, the session is configured with
81
- * the caller's API key and org/app context.
82
- */
83
- async create(role, auth, sessionType, pinnedModel, deployId) {
84
- const sessionId = randomUUID();
85
- const sessionParams = {
86
- ...this.baseParams,
87
- sessionId,
88
- approvalMode: ApprovalMode.YOLO,
89
- interactive: false,
90
- noBrowser: true,
91
- coreTools: [],
92
- policyEngineConfig: {
93
- approvalMode: ApprovalMode.YOLO,
94
- defaultDecision: PolicyDecision.ALLOW,
95
- rules: [
96
- // Global ALLOW at high priority — overrides any TOML rules
97
- // that might require ASK_USER (which fails in non-interactive mode)
98
- { decision: PolicyDecision.ALLOW, priority: 9999 },
99
- ],
100
- },
101
- };
102
- if (role) {
103
- sessionParams.activeRole = role;
104
- }
105
- // Resolve bundle: static bundle (local dev), or dynamic via bundleProvider (hosted)
106
- const bundle = this.bundle ?? (deployId && this.bundleProvider ? await this.bundleProvider(deployId, auth?.token) : null);
107
- // Inject bundle config into session params
108
- if (bundle) {
109
- log.debug(`Session ${sessionId} using bundle:\n${serializeBundle(bundle)}`, 'session');
110
- sessionParams.coreTools = this.buildCoreToolsList(bundle);
111
- const { buildConnectionsMap } = await import('@amodalai/core');
112
- const connectionsMap = buildConnectionsMap(bundle.connections, bundle.resolvedCredentials);
113
- sessionParams.connections = connectionsMap;
114
- sessionParams.appDocuments = bundle.knowledge.map((k) => ({
115
- id: k.name,
116
- scope_type: 'application',
117
- scope_id: 'local',
118
- title: k.title ?? k.name,
119
- category: 'system_docs',
120
- body: k.body,
121
- tags: [],
122
- status: 'active',
123
- created_by: 'local',
124
- created_at: new Date().toISOString(),
125
- updated_at: new Date().toISOString(),
126
- }));
127
- sessionParams.bundleSkills = bundle.skills.map((s) => ({
128
- name: s.name,
129
- description: s.description ?? '',
130
- body: s.body,
131
- }));
132
- sessionParams.basePrompt = bundle.config.basePrompt;
133
- sessionParams.agentName = bundle.config.name;
134
- sessionParams.agentContext = bundle.config.userContext ?? bundle.config.description;
135
- // Model config from bundle
136
- const mainModel = bundle.config.models?.main;
137
- if (mainModel) {
138
- sessionParams.modelConfig = {
139
- provider: mainModel.provider ?? 'anthropic',
140
- model: mainModel.model,
141
- };
142
- }
143
- // Stores
144
- if (bundle.stores.length > 0) {
145
- sessionParams.stores = bundle.stores;
146
- }
147
- }
148
- const config = new AmodalConfig(sessionParams);
149
- // Skip the full upstream Config.initialize() which hangs trying to scan
150
- // files, discover agents, and authenticate with Gemini. Instead, do a
151
- // minimal init: create tool registry, initializeAuth(), registerTools().
152
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
153
- const upstreamRaw = config.getUpstreamConfig();
154
- // Initialize storage (required by many upstream components)
155
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
156
- const storage = (upstreamRaw['storage'] ?? upstreamRaw['_storage']);
157
- if (storage?.initialize) {
158
- await storage.initialize();
159
- }
160
- // Create agent registry and tool registry if not already created
161
- if (!upstreamRaw['toolRegistry'] && !upstreamRaw['_toolRegistry']) {
162
- // Agent registry must exist before tool registry (createToolRegistry references it).
163
- // Use a minimal stub — we register Amodal subagents separately.
164
- if (!upstreamRaw['agentRegistry']) {
165
- upstreamRaw['agentRegistry'] = {
166
- getAllDefinitions: () => [],
167
- agents: new Map(),
168
- allDefinitions: new Map(),
169
- initialize: async () => { },
170
- };
171
- }
172
- // Prompt registry stub (referenced by some tools)
173
- if (!upstreamRaw['promptRegistry']) {
174
- upstreamRaw['promptRegistry'] = { getPrompts: () => [] };
175
- }
176
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
177
- const upstreamConfig = config.getUpstreamConfig();
178
- const registry = await upstreamConfig.createToolRegistry();
179
- upstreamRaw['_toolRegistry'] = registry;
180
- }
181
- // Initialize auth (replaces content generator for non-Google providers)
182
- await config.initializeAuth();
183
- // Register amodal tools (request, present, knowledge, stores)
184
- await config.registerTools();
185
- // Register custom tools from bundle tools/ directory
186
- if (this.bundle && this.bundle.tools.length > 0) {
187
- const { CustomToolAdapter } = await import('./custom-tool-adapter.js');
188
- const { LocalToolExecutor } = await import('../agent/tool-executor-local.js');
189
- const executor = this.toolExecutor ?? new LocalToolExecutor();
190
- const registry = config.getUpstreamConfig().getToolRegistry();
191
- for (const tool of this.bundle.tools) {
192
- if (tool.confirm === 'never')
193
- continue; // hidden from LLM
194
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- partial session for tool context
195
- const placeholder = { config, toolExecutor: executor };
196
- const adapter = new CustomToolAdapter(tool, placeholder, executor);
197
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- adapter matches upstream tool interface
198
- registry.registerTool(adapter);
199
- }
200
- log.debug(`Registered ${String(this.bundle.tools.length)} custom tool(s)`, 'session');
201
- }
202
- // Set model on upstream config so GeminiClient.startChat() can resolve it
203
- if (sessionParams.modelConfig?.model) {
204
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
205
- const upstreamForModel = config.getUpstreamConfig();
206
- if (!upstreamForModel.model && !upstreamForModel._model) {
207
- upstreamForModel['model'] = sessionParams.modelConfig.model;
208
- upstreamForModel['_model'] = sessionParams.modelConfig.model;
209
- }
210
- }
211
- // Initialize the GeminiClient (creates the Chat instance)
212
- // Must happen after tool registry and content generator are set up.
213
- const gemClient = config.getGeminiClient();
214
- if (!gemClient.isInitialized()) {
215
- await gemClient.initialize();
216
- }
217
- // Inject app secrets as process env vars so shell_exec commands can
218
- // reference them (e.g. $API_BASE_URL in curl commands). The shell execution
219
- // service inherits process.env for child processes.
220
- // Also add secret names to the sanitization allowlist — without this,
221
- // names like API_KEY get stripped by the /KEY/i pattern.
222
- const connections = config.getConnections();
223
- const connKeys = Object.keys(connections).filter((k) => k !== '_secrets');
224
- log.debug(`connections: ${connKeys.join(', ') || '(none)'}`, 'session');
225
- // App secrets are available to tools via session-scoped getSessionEnv()
226
- // (through ToolContext). They are NOT injected into process.env to prevent
227
- // cross-app secret leakage in multi-session runtimes.
228
- const secrets = connections['_secrets'];
229
- if (secrets && typeof secrets === 'object') {
230
- const secretCount = Object.keys(secrets).length;
231
- log.debug(`${secretCount} secrets available via session env`, 'session');
232
- }
233
- else {
234
- log.debug(`no _secrets found in connections`, 'session');
235
- }
236
- // Platform tool disabling is handled via the disabled_platform_tools
237
- // field on the application record (propagated through the bundle config).
238
- // Pinned model (from hydrated session) takes highest priority
239
- if (pinnedModel) {
240
- config.setModelConfig({
241
- provider: pinnedModel.provider,
242
- model: pinnedModel.model,
243
- });
244
- }
245
- // Build modelConfig from env vars if not already set by the SDK or pinned model.
246
- // This enables non-Gemini providers for non-platform sessions.
247
- // Runtime default: anthropic/claude-sonnet-4-20250514 (matches platform-api default).
248
- if (!config.getModelConfig()) {
249
- const llmProvider = process.env['LLM_PROVIDER'] ?? 'anthropic';
250
- if (llmProvider !== 'google') {
251
- config.setModelConfig({
252
- provider: llmProvider,
253
- model: process.env['MODEL'] ?? 'claude-sonnet-4-20250514',
254
- baseUrl: process.env['PROVIDER_BASE_URL'],
255
- });
256
- }
257
- }
258
- // Initialize store backend if stores are configured.
259
- // Must happen before initializeAuth/tool registration so store tools
260
- // are available when registerAmodalTools runs.
261
- // In local dev mode, use the shared store backend from options.
262
- const stores = config.getStores();
263
- let storeBackend = this.sharedStoreBackend;
264
- if (stores.length > 0 && !storeBackend) {
265
- try {
266
- const { PGLiteStoreBackend } = await import('../stores/pglite-store-backend.js');
267
- const backend = new PGLiteStoreBackend();
268
- await backend.initialize(stores);
269
- config.setStoreBackend(backend);
270
- storeBackend = backend;
271
- log.info(`Initialized store backend (${String(stores.length)} store(s))`, 'session');
272
- }
273
- catch (err) {
274
- log.error(`Failed to init store backend: ${err instanceof Error ? err.message : String(err)}`, 'session');
275
- }
276
- }
277
- // Initialize the LLM content generator. This must always run — the
278
- // upstream GeminiClient requires an initialized content generator even
279
- // when the runtime uses non-Gemini providers (Anthropic, OpenAI).
280
- await config.initializeAuth();
281
- const geminiClient = config.getGeminiClient();
282
- // Override the upstream Gemini CLI system prompt with the Amodal default
283
- // or the user's custom basePrompt. The upstream prompt is Gemini CLI-specific
284
- // and not appropriate for the Amodal agent runtime.
285
- const systemPrompt = config.getBasePrompt() ?? buildDefaultPrompt({
286
- name: config.getAgentName() ?? 'Amodal Agent',
287
- description: config.getAgentDescription(),
288
- agentContext: config.getAgentContext(),
289
- agentOverride: bundle?.agents?.main,
290
- connections: bundle?.connections ? Array.from(bundle.connections.values()).map((conn) => ({
291
- name: conn.name,
292
- endpoints: (conn.surface ?? [])
293
- .filter((ep) => ep.included)
294
- .map((ep) => ({ method: ep.method, path: ep.path, description: ep.description })),
295
- entities: conn.entities,
296
- rules: conn.rules,
297
- })) : undefined,
298
- skills: bundle?.skills?.map((s) => ({
299
- name: s.name,
300
- description: s.description ?? '',
301
- trigger: s.trigger,
302
- body: s.body,
303
- })),
304
- knowledge: bundle?.knowledge?.map((k) => ({
305
- name: k.name,
306
- title: k.title,
307
- body: k.body,
308
- })),
309
- ...(bundle?.connections ? (() => {
310
- const { scopeLabels } = resolveScopeLabels(bundle.connections, []);
311
- const fieldGuidance = generateFieldGuidance(bundle.connections, []);
312
- const altLookup = generateAlternativeLookupGuidance(bundle.connections);
313
- return {
314
- fieldGuidance: fieldGuidance || undefined,
315
- scopeLabels: Object.keys(scopeLabels).length > 0 ? scopeLabels : undefined,
316
- alternativeLookupGuidance: altLookup || undefined,
317
- };
318
- })() : {}),
319
- });
320
- try {
321
- geminiClient.getChat().setSystemInstruction(systemPrompt);
322
- }
323
- catch {
324
- // Chat may not be initialized yet in some edge cases — non-fatal
325
- }
326
- // Remove upstream Gemini CLI built-in agents that aren't relevant to Amodal,
327
- // then register Amodal subagents loaded via the SDK.
328
- const UPSTREAM_AGENTS_TO_REMOVE = ['codebase_investigator', 'cli_help', 'generalist'];
329
- try {
330
- const upstream = config.getUpstreamConfig();
331
- const agentRegistry = upstream.getAgentRegistry();
332
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
333
- const registryRaw = agentRegistry;
334
- const toolRegistry = upstream.getToolRegistry();
335
- // Remove upstream built-in agents
336
- for (const name of UPSTREAM_AGENTS_TO_REMOVE) {
337
- registryRaw.agents.delete(name);
338
- registryRaw.allDefinitions.delete(name);
339
- try {
340
- toolRegistry.unregisterTool(name);
341
- }
342
- catch { /* may not exist as tool */ }
343
- }
344
- // Register Amodal subagents from the deployment.
345
- // Bundle subagents (from SDK/platform) override platform defaults with the same name.
346
- // disabledSubagents from amodal.json config filters out specific agents.
347
- const bundleSubagents = config.getBundleSubagents();
348
- const disabledSubagents = new Set(config.getDisabledSubagents());
349
- for (const sub of bundleSubagents) {
350
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
351
- const sa = sub;
352
- // Skip disabled subagents
353
- if (disabledSubagents.has(sa.name))
354
- continue;
355
- const agentDef = {
356
- kind: 'local',
357
- name: sa.name,
358
- displayName: sa.displayName,
359
- description: sa.description,
360
- inputConfig: {
361
- inputSchema: {
362
- type: 'object',
363
- properties: {
364
- request: { type: 'string', description: `The task for the ${sa.displayName} agent.` },
365
- },
366
- required: ['request'],
367
- },
368
- },
369
- modelConfig: { model: 'inherit' },
370
- promptConfig: { systemPrompt: sa.prompt },
371
- runConfig: { maxSteps: 15 },
372
- toolConfig: sa.tools ? { tools: sa.tools } : undefined,
373
- };
374
- registryRaw.agents.set(sa.name, agentDef);
375
- registryRaw.allDefinitions.set(sa.name, agentDef);
376
- }
377
- // Re-register all agents as SubagentTools so the LLM can invoke them
378
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
379
- const configWithSubagents = upstream;
380
- if (typeof configWithSubagents.registerSubAgentTools === 'function') {
381
- configWithSubagents.registerSubAgentTools(toolRegistry);
382
- }
383
- }
384
- catch {
385
- // Non-fatal — registry may not be initialized
386
- }
387
- // Register store tools on the upstream tool registry.
388
- // This happens after config.initialize() (which ran registerAmodalTools),
389
- // so we need to register them separately here.
390
- if (storeBackend && stores.length > 0) {
391
- try {
392
- const { StoreWriteTool, StoreBatchTool, StoreQueryTool } = await import('@amodalai/core');
393
- const upstream = config.getUpstreamConfig();
394
- const toolRegistry = upstream.getToolRegistry();
395
- const messageBus = config.getMessageBus();
396
- const appId = config.getAppId() ?? auth?.applicationId ?? LOCAL_APP_ID;
397
- for (const store of stores) {
398
- toolRegistry.registerTool(
399
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- tool types match upstream interface
400
- new StoreWriteTool(store, storeBackend, appId, messageBus));
401
- toolRegistry.registerTool(
402
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- tool types match upstream interface
403
- new StoreBatchTool(store, storeBackend, appId, messageBus));
404
- }
405
- toolRegistry.registerTool(
406
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
407
- new StoreQueryTool(stores, storeBackend, appId, messageBus));
408
- // Refresh the GeminiClient's tool list so the LLM sees the new tools
409
- await geminiClient.setTools();
410
- log.debug(`Registered ${String(stores.length)} store tool(s) + query_store`, 'session');
411
- }
412
- catch (err) {
413
- log.error(`Failed to register store tools: ${err instanceof Error ? err.message : String(err)}`, 'session');
414
- }
415
- }
416
- const scheduler = new Scheduler({
417
- config: config.getUpstreamConfig(),
418
- messageBus: config.getMessageBus(),
419
- getPreferredEditor: () => undefined,
420
- schedulerId: ROOT_SCHEDULER_ID,
421
- });
422
- const now = Date.now();
423
- const mc = config.getModelConfig();
424
- const session = {
425
- id: sessionId,
426
- config,
427
- geminiClient,
428
- scheduler,
429
- createdAt: now,
430
- lastAccessedAt: now,
431
- orgId: auth?.orgId,
432
- sessionType,
433
- pendingAskUser: new Map(),
434
- accumulatedMessages: [],
435
- model: mc?.model ?? config.getModel(),
436
- provider: mc?.provider,
437
- storeBackend,
438
- planModeManager: new PlanModeManager(),
439
- toolExecutor: this.toolExecutor,
440
- shellExecutor: this.shellExecutor,
441
- appId: auth?.applicationId ?? LOCAL_APP_ID,
442
- };
443
- // Share MCP connection across sessions — connect once, reuse for all
444
- if (this.bundle && !this.sharedMcpManager) {
445
- await this.initSharedMcp(this.bundle);
446
- }
447
- if (this.sharedMcpManager) {
448
- session.mcpManager = this.sharedMcpManager;
449
- // Register MCP tools on the upstream tool registry so the Gemini client can see them
450
- try {
451
- const upstream = config.getUpstreamConfig();
452
- const toolRegistry = upstream.getToolRegistry();
453
- const mcpTools = session.mcpManager.getDiscoveredTools();
454
- for (const mcpTool of mcpTools) {
455
- // Adapter matching upstream DeclarativeTool interface (build/silentBuild/schema/getSchema)
456
- const mcpSession = session;
457
- const adapter = {
458
- name: mcpTool.name,
459
- displayName: mcpTool.name,
460
- description: mcpTool.description,
461
- kind: 'declarative',
462
- parameterSchema: mcpTool.parameters,
463
- get isReadOnly() { return true; },
464
- get toolAnnotations() { return undefined; },
465
- get schema() { return this.getSchema(); },
466
- getSchema() {
467
- return {
468
- name: mcpTool.name,
469
- description: mcpTool.description,
470
- parametersJsonSchema: mcpTool.parameters,
471
- };
472
- },
473
- build(params) {
474
- return {
475
- name: mcpTool.name,
476
- params,
477
- execute: async () => adapter.validateBuildAndExecute(params),
478
- };
479
- },
480
- silentBuild(params) {
481
- return this.build(params);
482
- },
483
- async validateBuildAndExecute(params) {
484
- try {
485
- const result = await mcpSession.mcpManager.callTool(mcpTool.name, params);
486
- const output = result.content
487
- .map((c) => c.type === 'text' && c.text ? c.text : `[${c.type}]`)
488
- .join('\n');
489
- return {
490
- llmContent: result.isError ? `Error: ${output}` : output,
491
- returnDisplay: output.slice(0, 200),
492
- ...(result.isError ? { error: { message: output, type: 'EXECUTION_FAILED' } } : {}),
493
- };
494
- }
495
- catch (err) {
496
- const msg = err instanceof Error ? err.message : String(err);
497
- return { llmContent: `Error: ${msg}`, returnDisplay: msg, error: { message: msg, type: 'EXECUTION_FAILED' } };
498
- }
499
- },
500
- };
501
- toolRegistry.registerTool(adapter); // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
502
- }
503
- await geminiClient.setTools();
504
- log.debug(`Registered ${String(mcpTools.length)} MCP tools on tool registry`, 'mcp');
505
- }
506
- catch (err) {
507
- const msg = err instanceof Error ? err.message : String(err);
508
- log.error(`Failed to register MCP tools: ${msg}`, 'mcp');
509
- }
510
- }
511
- // Guard: reject sessions with no agent configuration.
512
- // If there are no skills, no base prompt, and no agent context, the agent
513
- // would fall back to a generic assistant response — which is never correct
514
- // for a deployed agent.
515
- const hasSkills = sessionParams.bundleSkills && sessionParams.bundleSkills.length > 0;
516
- const hasPrompt = !!sessionParams.basePrompt;
517
- const hasContext = !!sessionParams.agentContext;
518
- if (!hasSkills && !hasPrompt && !hasContext && deployId) {
519
- throw new Error(`Agent not configured: deploy ${deployId} has no skills, base prompt, or context. ` +
520
- 'Ensure the deployment snapshot includes agent configuration.');
521
- }
522
- this.sessions.set(sessionId, session);
523
- return session;
524
- }
525
- /**
526
- * Get an existing session by ID, updating its last-accessed timestamp.
527
- * Returns undefined if the session doesn't exist.
528
- */
529
- get(id) {
530
- const session = this.sessions.get(id);
531
- if (session) {
532
- session.lastAccessedAt = Date.now();
533
- }
534
- return session;
535
- }
536
- /**
537
- * Hydrate a session from stored conversation history in the platform API.
538
- *
539
- * When a user sends a message to a session that expired from the in-memory
540
- * cache, this method fetches the conversation from the DB, creates a fresh
541
- * runtime session (new config, tools, KB, secrets), seeds its LLM history
542
- * with the stored messages, and registers it under the original conversation ID.
543
- *
544
- * Returns null if hydration cannot proceed (no platform API, missing auth,
545
- * fetch failure, or no stored messages).
546
- */
547
- async hydrate(conversationId, role, auth, sessionType) {
548
- if (!this.sessionStore)
549
- return null;
550
- // Deduplicate concurrent hydration requests for the same conversation
551
- const pending = this.pendingHydrations.get(conversationId);
552
- if (pending)
553
- return pending;
554
- const hydrationPromise = this.doHydrate(conversationId, role, auth, sessionType);
555
- this.pendingHydrations.set(conversationId, hydrationPromise);
556
- try {
557
- return await hydrationPromise;
558
- }
559
- finally {
560
- this.pendingHydrations.delete(conversationId);
561
- }
562
- }
563
- async doHydrate(conversationId, role, auth, sessionType) {
564
- // Fetch stored conversation via pluggable session store
565
- if (!this.sessionStore) {
566
- return null;
567
- }
568
- let record;
569
- try {
570
- record = await this.sessionStore.getSession(conversationId, {
571
- appId: auth?.applicationId,
572
- token: auth?.token,
573
- });
574
- }
575
- catch (err) {
576
- log.error(`Error fetching conversation ${conversationId}: ${err instanceof Error ? err.message : String(err)}`, 'hydrate');
577
- return null;
578
- }
579
- if (!record)
580
- return null;
581
- // No messages → nothing to hydrate
582
- if (!record.messages || record.messages.length === 0)
583
- return null;
584
- // Create a fully initialized session (fresh config, tools, KB, secrets).
585
- // Pin the model/provider from the original session so it survives model changes.
586
- const pinnedModel = record.model && record.provider
587
- ? { provider: record.provider, model: record.model }
588
- : undefined;
589
- const session = await this.create(role, auth, sessionType, pinnedModel);
590
- // Remove auto-generated ID from the Map, re-register under the original conversationId
591
- this.sessions.delete(session.id);
592
- session.id = conversationId;
593
- this.sessions.set(conversationId, session);
594
- // Convert stored messages to Gemini Content[] and seed LLM history
595
- const history = convertSessionMessagesToHistory(record.messages);
596
- if (history.length > 0) {
597
- session.geminiClient.setHistory(history);
598
- }
599
- // Pre-populate accumulatedMessages so saveSessionHistory() appends correctly
600
- session.accumulatedMessages = [...record.messages];
601
- log.debug(`Hydrated conversation ${conversationId} with ${record.messages.length} messages (${history.length} history entries)`, 'hydrate');
602
- return session;
603
- }
604
- /**
605
- * Destroy a session, flushing its audit log.
606
- */
607
- async destroy(id) {
608
- const session = this.sessions.get(id);
609
- if (!session)
610
- return;
611
- this.sessions.delete(id);
612
- // Only close the store backend if it was created for this session (not the shared one)
613
- if (session.storeBackend && session.storeBackend !== this.sharedStoreBackend) {
614
- try {
615
- await session.storeBackend.close();
616
- }
617
- catch {
618
- // Best-effort — PGLite may already be closed
619
- }
620
- }
621
- await session.config.shutdownAudit();
622
- }
623
- // ---------------------------------------------------------------------------
624
- // Local dev features
625
- // ---------------------------------------------------------------------------
626
- /**
627
- * Get the bundle (local dev mode).
628
- */
629
- getBundle() {
630
- return this.bundle;
631
- }
632
- /**
633
- * Update the bundle for new sessions (hot reload).
634
- * Existing sessions keep their old config.
635
- */
636
- updateBundle(bundle) {
637
- // Only reset MCP if the MCP config actually changed
638
- const oldMcpConfig = this.bundle ? JSON.stringify(this.buildMcpConfigs(this.bundle)) : '';
639
- const newMcpConfig = JSON.stringify(this.buildMcpConfigs(bundle));
640
- this.bundle = bundle;
641
- log.debug('Config reloaded', 'session');
642
- if (oldMcpConfig !== newMcpConfig) {
643
- log.debug('MCP config changed, resetting connections', 'session');
644
- this.inspectMcpInitialized = false;
645
- this.inspectMcp = undefined;
646
- // Also reset shared MCP so next session gets fresh connections
647
- if (this.sharedMcpManager) {
648
- void this.sharedMcpManager.shutdown().catch(() => { });
649
- this.sharedMcpManager = undefined;
650
- }
651
- }
652
- }
653
- /**
654
- * Re-register a session under a different ID (e.g., restoring original ID on session restore).
655
- */
656
- reregister(session, newId) {
657
- this.sessions.delete(session.id);
658
- session.id = newId;
659
- this.sessions.set(newId, session);
660
- }
661
- /**
662
- * Create an admin session for the config chat.
663
- * Uses admin agent skills/knowledge but the current bundle's connections/stores.
664
- *
665
- * Temporarily swaps bundle fields so create() builds the prompt with admin
666
- * content, then restores the original bundle. This mirrors the old approach
667
- * of building an adminRepo overlay.
668
- */
669
- async createAdminSession(getPort) {
670
- if (!this.bundle) {
671
- throw new Error('Admin sessions require a bundle');
672
- }
673
- if (this.bundle.source !== 'local') {
674
- throw new Error('Admin sessions are only available for local repos');
675
- }
676
- const agentDir = await ensureAdminAgent(this.bundle.origin);
677
- const adminContent = await loadAdminAgent(agentDir);
678
- // Save original bundle fields
679
- const origSkills = this.bundle.skills;
680
- const origKnowledge = this.bundle.knowledge;
681
- const origAgents = this.bundle.agents;
682
- const origAutomations = this.bundle.automations;
683
- // Swap in admin content so create() builds the prompt correctly
684
- this.bundle.skills = adminContent.skills;
685
- this.bundle.knowledge = adminContent.knowledge;
686
- this.bundle.agents = {
687
- main: adminContent.agentPrompt ?? undefined,
688
- simple: undefined,
689
- subagents: [],
690
- };
691
- this.bundle.automations = [];
692
- let session;
693
- try {
694
- session = await this.create('admin');
695
- }
696
- finally {
697
- // Restore original bundle fields
698
- this.bundle.skills = origSkills;
699
- this.bundle.knowledge = origKnowledge;
700
- this.bundle.agents = origAgents;
701
- this.bundle.automations = origAutomations;
702
- }
703
- session.appId = 'admin';
704
- // Register admin file tools (read/write/delete agent files)
705
- try {
706
- const { createReadRepoFileTool, createWriteRepoFileTool, createDeleteRepoFileTool } = await import('./admin-file-tools.js');
707
- const repoRoot = this.bundle.origin;
708
- const upstream = session.config.getUpstreamConfig();
709
- const toolRegistry = upstream.getToolRegistry();
710
- toolRegistry.registerTool(createReadRepoFileTool(repoRoot)); // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
711
- toolRegistry.registerTool(createWriteRepoFileTool(repoRoot)); // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
712
- toolRegistry.registerTool(createDeleteRepoFileTool(repoRoot)); // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
713
- // Internal API tool — lets admin query eval results, health, context, etc.
714
- if (getPort) {
715
- const { createInternalApiTool } = await import('./admin-file-tools.js');
716
- toolRegistry.registerTool(createInternalApiTool(getPort)); // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
717
- }
718
- await session.geminiClient.setTools();
719
- log.debug('Registered admin tools (file tools + internal_api)', 'admin');
720
- }
721
- catch (err) {
722
- const msg = err instanceof Error ? err.message : String(err);
723
- log.error(`Failed to register file tools: ${msg}`, 'admin');
724
- }
725
- return session;
726
- }
727
- /**
728
- * Get a persistent MCP manager for inspect/health operations.
729
- * Lazy-initialized on first call, reused across requests.
730
- */
731
- async getInspectMcpManager() {
732
- if (this.inspectMcpInitialized)
733
- return this.inspectMcp;
734
- this.inspectMcpInitialized = true;
735
- if (!this.bundle)
736
- return undefined;
737
- // Build MCP server configs from bundle connections
738
- const mcpServers = this.buildMcpConfigs(this.bundle);
739
- if (Object.keys(mcpServers).length === 0)
740
- return undefined;
741
- const manager = new McpManager();
742
- try {
743
- await manager.startServers(mcpServers);
744
- this.inspectMcp = manager;
745
- return manager;
746
- }
747
- catch (err) {
748
- const msg = err instanceof Error ? err.message : String(err);
749
- log.error(`MCP initialization failed: ${msg}`, 'inspect');
750
- this.inspectMcp = manager;
751
- return manager;
752
- }
753
- }
754
- /**
755
- * Initialize MCP servers for a session from bundle connections.
756
- */
757
- /**
758
- * Initialize the shared MCP manager (once, reused across sessions).
759
- * Avoids reconnecting MCP servers for every eval/judge/admin session.
760
- */
761
- async initSharedMcp(bundle) {
762
- const mcpServers = this.buildMcpConfigs(bundle);
763
- if (Object.keys(mcpServers).length === 0)
764
- return;
765
- const manager = new McpManager();
766
- try {
767
- await manager.startServers(mcpServers);
768
- if (manager.connectedCount > 0) {
769
- this.sharedMcpManager = manager;
770
- }
771
- }
772
- catch (err) {
773
- const msg = err instanceof Error ? err.message : String(err);
774
- log.error(`MCP initialization failed: ${msg}`, 'session');
775
- }
776
- }
777
- /**
778
- * Build the list of upstream core tools to enable based on bundle config.
779
- * Only tools relevant to the Amodal runtime are included.
780
- */
781
- buildCoreToolsList(bundle) {
782
- const tools = [
783
- // Always available
784
- 'enter_plan_mode',
785
- 'exit_plan_mode',
786
- 'ask_user',
787
- ];
788
- // Shell execution (opt-in via config.sandbox.shellExec)
789
- if (bundle.config.sandbox?.shellExec) {
790
- tools.push('shell');
791
- }
792
- return tools;
793
- }
794
- /**
795
- * Build MCP server configs from bundle connections.
796
- */
797
- buildMcpConfigs(bundle) {
798
- const mcpServers = {};
799
- for (const [name, conn] of bundle.connections) {
800
- if (conn.spec.protocol === 'mcp') {
801
- const resolvedHeaders = conn.spec.headers ? resolveEnvRefs(conn.spec.headers) : undefined;
802
- const resolvedEnv = conn.spec.env ? resolveEnvRefs(conn.spec.env) : undefined;
803
- mcpServers[name] = {
804
- transport: conn.spec.transport ?? 'stdio',
805
- command: conn.spec.command,
806
- args: conn.spec.args,
807
- env: resolvedEnv,
808
- url: conn.spec.url,
809
- headers: resolvedHeaders,
810
- trust: conn.spec.trust,
811
- };
812
- }
813
- }
814
- if (bundle.mcpServers) {
815
- for (const [name, config] of Object.entries(bundle.mcpServers)) {
816
- if (!mcpServers[name]) {
817
- mcpServers[name] = config;
818
- }
819
- }
820
- }
821
- return mcpServers;
822
- }
823
- /**
824
- * Remove sessions that have been idle longer than the TTL.
825
- */
826
- async cleanup() {
827
- const now = Date.now();
828
- const expired = [];
829
- for (const [id, session] of this.sessions) {
830
- if (now - session.lastAccessedAt > this.ttlMs) {
831
- expired.push(id);
832
- }
833
- }
834
- for (const id of expired) {
835
- await this.destroy(id);
836
- }
837
- return expired.length;
838
- }
839
- /**
840
- * Shutdown all sessions and stop the cleanup timer.
841
- */
842
- async shutdown() {
843
- if (this.cleanupTimer) {
844
- clearInterval(this.cleanupTimer);
845
- this.cleanupTimer = null;
846
- }
847
- // Shutdown MCP managers for all sessions
848
- for (const session of this.sessions.values()) {
849
- if (session.mcpManager) {
850
- await session.mcpManager.shutdown().catch(() => { });
851
- }
852
- }
853
- // Shutdown persistent inspect MCP manager
854
- if (this.inspectMcp) {
855
- await this.inspectMcp.shutdown().catch(() => { });
856
- this.inspectMcp = undefined;
857
- this.inspectMcpInitialized = false;
858
- }
859
- const ids = [...this.sessions.keys()];
860
- for (const id of ids) {
861
- await this.destroy(id);
862
- }
863
- }
864
- /**
865
- * Wait for a user's response to an ask_user prompt.
866
- * Resolves when the user submits answers via the HTTP endpoint.
867
- * Rejects on timeout or if the signal is aborted.
868
- */
869
- waitForAskUserResponse(session, askId, signal) {
870
- return new Promise((resolve, reject) => {
871
- const timer = setTimeout(() => {
872
- session.pendingAskUser.delete(askId);
873
- reject(new Error('ask_user response timed out'));
874
- }, ASK_USER_TIMEOUT_MS);
875
- const onAbort = () => {
876
- clearTimeout(timer);
877
- session.pendingAskUser.delete(askId);
878
- reject(new Error('ask_user aborted'));
879
- };
880
- signal.addEventListener('abort', onAbort, { once: true });
881
- session.pendingAskUser.set(askId, {
882
- resolve: (answers) => {
883
- clearTimeout(timer);
884
- signal.removeEventListener('abort', onAbort);
885
- session.pendingAskUser.delete(askId);
886
- resolve(answers);
887
- },
888
- reject: (reason) => {
889
- clearTimeout(timer);
890
- signal.removeEventListener('abort', onAbort);
891
- session.pendingAskUser.delete(askId);
892
- reject(reason);
893
- },
894
- });
895
- });
896
- }
897
- /**
898
- * Resolve a pending ask_user with user-provided answers.
899
- * Returns true if the ask_id was found and resolved, false otherwise.
900
- */
901
- resolveAskUser(session, askId, answers) {
902
- const pending = session.pendingAskUser.get(askId);
903
- if (!pending)
904
- return false;
905
- pending.resolve(answers);
906
- return true;
907
- }
908
- /**
909
- * Number of active sessions.
910
- */
911
- get size() {
912
- return this.sessions.size;
913
- }
914
- }
915
- //# sourceMappingURL=session-manager.js.map