@amodalai/runtime 0.1.25 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/dist/src/__fixtures__/README.md +84 -0
  2. package/dist/src/__fixtures__/smoke-agent/amodal.json +11 -0
  3. package/dist/src/__fixtures__/smoke-agent/automations/test-auto.md +5 -0
  4. package/dist/src/__fixtures__/smoke-agent/connections/mock-api/access.json +11 -0
  5. package/dist/src/__fixtures__/smoke-agent/connections/mock-api/spec.json +4 -0
  6. package/dist/src/__fixtures__/smoke-agent/connections/mock-api/surface.md +9 -0
  7. package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/access.json +3 -0
  8. package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/spec.json +8 -0
  9. package/dist/src/__fixtures__/smoke-agent/evals/basic-eval.md +12 -0
  10. package/dist/src/__fixtures__/smoke-agent/knowledge/test-knowledge.md +3 -0
  11. package/dist/src/__fixtures__/smoke-agent/skills/test-skill/SKILL.md +11 -0
  12. package/dist/src/__fixtures__/smoke-agent/stores/test-items.json +11 -0
  13. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.d.ts +18 -0
  14. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.js +22 -0
  15. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.js.map +1 -0
  16. package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/tool.json +17 -0
  17. package/dist/src/__fixtures__/smoke.test.js +718 -0
  18. package/dist/src/__fixtures__/smoke.test.js.map +1 -0
  19. package/dist/src/{agent/stores-e2e.test.d.ts → __tests__/providers.test.d.ts} +1 -1
  20. package/dist/src/__tests__/providers.test.js +209 -0
  21. package/dist/src/__tests__/providers.test.js.map +1 -0
  22. package/dist/src/__tests__/test-providers.d.ts +40 -0
  23. package/dist/src/__tests__/test-providers.js +61 -0
  24. package/dist/src/__tests__/test-providers.js.map +1 -0
  25. package/dist/src/agent/agent-types.d.ts +2 -2
  26. package/dist/src/agent/config-watcher.test.js +18 -14
  27. package/dist/src/agent/config-watcher.test.js.map +1 -1
  28. package/dist/src/agent/local-server.d.ts +3 -3
  29. package/dist/src/agent/local-server.js +213 -122
  30. package/dist/src/agent/local-server.js.map +1 -1
  31. package/dist/src/agent/local-server.test.js +17 -13
  32. package/dist/src/agent/local-server.test.js.map +1 -1
  33. package/dist/src/agent/loop-types.d.ts +175 -0
  34. package/dist/src/agent/loop-types.js +20 -0
  35. package/dist/src/agent/loop-types.js.map +1 -0
  36. package/dist/src/agent/loop.d.ts +31 -0
  37. package/dist/src/agent/loop.js +139 -0
  38. package/dist/src/agent/loop.js.map +1 -0
  39. package/dist/src/agent/{tool-context-builder.test.d.ts → loop.test.d.ts} +1 -1
  40. package/dist/src/agent/loop.test.js +1030 -0
  41. package/dist/src/agent/loop.test.js.map +1 -0
  42. package/dist/src/agent/mcp-config.d.ts +28 -0
  43. package/dist/src/agent/mcp-config.js +57 -0
  44. package/dist/src/agent/mcp-config.js.map +1 -0
  45. package/dist/src/agent/page-builder.js +6 -1
  46. package/dist/src/agent/page-builder.js.map +1 -1
  47. package/dist/src/agent/proactive/proactive-runner.d.ts +24 -8
  48. package/dist/src/agent/proactive/proactive-runner.js +36 -33
  49. package/dist/src/agent/proactive/proactive-runner.js.map +1 -1
  50. package/dist/src/agent/proactive/proactive-runner.test.d.ts +1 -1
  51. package/dist/src/agent/proactive/proactive-runner.test.js +75 -87
  52. package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -1
  53. package/dist/src/agent/routes/admin-chat.d.ts +15 -3
  54. package/dist/src/agent/routes/admin-chat.js +63 -17
  55. package/dist/src/agent/routes/admin-chat.js.map +1 -1
  56. package/dist/src/agent/routes/automations.js +5 -4
  57. package/dist/src/agent/routes/automations.js.map +1 -1
  58. package/dist/src/agent/routes/evals.d.ts +3 -2
  59. package/dist/src/agent/routes/evals.js +25 -11
  60. package/dist/src/agent/routes/evals.js.map +1 -1
  61. package/dist/src/agent/routes/files.js +7 -6
  62. package/dist/src/agent/routes/files.js.map +1 -1
  63. package/dist/src/agent/routes/inspect.d.ts +6 -2
  64. package/dist/src/agent/routes/inspect.js +32 -15
  65. package/dist/src/agent/routes/inspect.js.map +1 -1
  66. package/dist/src/agent/routes/inspect.test.js +18 -42
  67. package/dist/src/agent/routes/inspect.test.js.map +1 -1
  68. package/dist/src/agent/routes/stores.js +9 -8
  69. package/dist/src/agent/routes/stores.js.map +1 -1
  70. package/dist/src/agent/routes/task.d.ts +15 -3
  71. package/dist/src/agent/routes/task.js +16 -6
  72. package/dist/src/agent/routes/task.js.map +1 -1
  73. package/dist/src/agent/routes/task.test.d.ts +1 -1
  74. package/dist/src/agent/routes/task.test.js +70 -53
  75. package/dist/src/agent/routes/task.test.js.map +1 -1
  76. package/dist/src/agent/routes/webhooks.js +12 -2
  77. package/dist/src/agent/routes/webhooks.js.map +1 -1
  78. package/dist/src/agent/session-store.d.ts +11 -2
  79. package/dist/src/agent/session-store.js +1 -1
  80. package/dist/src/agent/session-store.js.map +1 -1
  81. package/dist/src/agent/snapshot-server.d.ts +2 -22
  82. package/dist/src/agent/snapshot-server.js +50 -27
  83. package/dist/src/agent/snapshot-server.js.map +1 -1
  84. package/dist/src/agent/states/compacting.d.ts +14 -0
  85. package/dist/src/agent/states/compacting.js +258 -0
  86. package/dist/src/agent/states/compacting.js.map +1 -0
  87. package/dist/src/agent/states/confirming.d.ts +10 -0
  88. package/dist/src/agent/states/confirming.js +76 -0
  89. package/dist/src/agent/states/confirming.js.map +1 -0
  90. package/dist/src/agent/states/dispatching.d.ts +18 -0
  91. package/dist/src/agent/states/dispatching.js +241 -0
  92. package/dist/src/agent/states/dispatching.js.map +1 -0
  93. package/dist/src/agent/states/executing.d.ts +21 -0
  94. package/dist/src/agent/states/executing.js +308 -0
  95. package/dist/src/agent/states/executing.js.map +1 -0
  96. package/dist/src/agent/states/streaming.d.ts +10 -0
  97. package/dist/src/agent/states/streaming.js +155 -0
  98. package/dist/src/agent/states/streaming.js.map +1 -0
  99. package/dist/src/agent/states/thinking.d.ts +13 -0
  100. package/dist/src/agent/states/thinking.js +233 -0
  101. package/dist/src/agent/states/thinking.js.map +1 -0
  102. package/dist/src/agent/token-estimate.d.ts +17 -0
  103. package/dist/src/agent/token-estimate.js +13 -0
  104. package/dist/src/agent/token-estimate.js.map +1 -0
  105. package/dist/src/agent/tool-executor-local.js +9 -18
  106. package/dist/src/agent/tool-executor-local.js.map +1 -1
  107. package/dist/src/agent/tool-executor-local.test.js +3 -5
  108. package/dist/src/agent/tool-executor-local.test.js.map +1 -1
  109. package/dist/src/api/create-agent.d.ts +15 -0
  110. package/dist/src/api/create-agent.js +137 -0
  111. package/dist/src/api/create-agent.js.map +1 -0
  112. package/dist/src/api/types.d.ts +68 -0
  113. package/dist/src/api/types.js +7 -0
  114. package/dist/src/api/types.js.map +1 -0
  115. package/dist/src/config.d.ts +96 -0
  116. package/dist/src/config.js +221 -0
  117. package/dist/src/config.js.map +1 -0
  118. package/dist/src/{agent/custom-tools-e2e.test.d.ts → config.test.d.ts} +1 -1
  119. package/dist/src/config.test.js +235 -0
  120. package/dist/src/config.test.js.map +1 -0
  121. package/dist/src/context/compiler.d.ts +13 -0
  122. package/dist/src/context/compiler.js +358 -0
  123. package/dist/src/context/compiler.js.map +1 -0
  124. package/dist/src/context/compiler.test.d.ts +6 -0
  125. package/dist/src/context/compiler.test.js +532 -0
  126. package/dist/src/context/compiler.test.js.map +1 -0
  127. package/dist/src/context/types.d.ts +110 -0
  128. package/dist/src/context/types.js +7 -0
  129. package/dist/src/context/types.js.map +1 -0
  130. package/dist/src/errors.d.ts +145 -0
  131. package/dist/src/errors.js +218 -0
  132. package/dist/src/errors.js.map +1 -0
  133. package/dist/src/errors.test.d.ts +6 -0
  134. package/dist/src/errors.test.js +203 -0
  135. package/dist/src/errors.test.js.map +1 -0
  136. package/dist/src/index.d.ts +38 -6
  137. package/dist/src/index.js +40 -22
  138. package/dist/src/index.js.map +1 -1
  139. package/dist/src/logger.d.ts +15 -31
  140. package/dist/src/logger.js +19 -78
  141. package/dist/src/logger.js.map +1 -1
  142. package/dist/src/logger.test.d.ts +6 -0
  143. package/dist/src/logger.test.js +198 -0
  144. package/dist/src/logger.test.js.map +1 -0
  145. package/dist/src/providers/create-provider.d.ts +23 -0
  146. package/dist/src/providers/create-provider.js +185 -0
  147. package/dist/src/providers/create-provider.js.map +1 -0
  148. package/dist/src/providers/create-provider.test.d.ts +6 -0
  149. package/dist/src/providers/create-provider.test.js +95 -0
  150. package/dist/src/providers/create-provider.test.js.map +1 -0
  151. package/dist/src/providers/failover.d.ts +38 -0
  152. package/dist/src/providers/failover.js +147 -0
  153. package/dist/src/providers/failover.js.map +1 -0
  154. package/dist/src/providers/failover.test.d.ts +6 -0
  155. package/dist/src/providers/failover.test.js +169 -0
  156. package/dist/src/providers/failover.test.js.map +1 -0
  157. package/dist/src/providers/types.d.ts +110 -0
  158. package/dist/src/providers/types.js +7 -0
  159. package/dist/src/providers/types.js.map +1 -0
  160. package/dist/src/routes/ai-stream.d.ts +13 -10
  161. package/dist/src/routes/ai-stream.js +76 -38
  162. package/dist/src/routes/ai-stream.js.map +1 -1
  163. package/dist/src/routes/chat-new.test.d.ts +6 -0
  164. package/dist/src/routes/chat-new.test.js +107 -0
  165. package/dist/src/routes/chat-new.test.js.map +1 -0
  166. package/dist/src/routes/chat-stream-new.test.d.ts +6 -0
  167. package/dist/src/routes/chat-stream-new.test.js +135 -0
  168. package/dist/src/routes/chat-stream-new.test.js.map +1 -0
  169. package/dist/src/routes/chat-stream.d.ts +14 -4
  170. package/dist/src/routes/chat-stream.js +47 -27
  171. package/dist/src/routes/chat-stream.js.map +1 -1
  172. package/dist/src/routes/chat.d.ts +13 -4
  173. package/dist/src/routes/chat.js +60 -22
  174. package/dist/src/routes/chat.js.map +1 -1
  175. package/dist/src/routes/health.d.ts +3 -2
  176. package/dist/src/routes/health.js.map +1 -1
  177. package/dist/src/routes/route-helpers.d.ts +50 -0
  178. package/dist/src/routes/route-helpers.js +80 -0
  179. package/dist/src/routes/route-helpers.js.map +1 -0
  180. package/dist/src/routes/session-resolver.d.ts +72 -0
  181. package/dist/src/routes/session-resolver.js +123 -0
  182. package/dist/src/routes/session-resolver.js.map +1 -0
  183. package/dist/src/routes/session-resolver.test.d.ts +6 -0
  184. package/dist/src/routes/session-resolver.test.js +206 -0
  185. package/dist/src/routes/session-resolver.test.js.map +1 -0
  186. package/dist/src/routes/webhooks.d.ts +3 -1
  187. package/dist/src/routes/webhooks.js +12 -2
  188. package/dist/src/routes/webhooks.js.map +1 -1
  189. package/dist/src/security/permission-checker.d.ts +80 -0
  190. package/dist/src/security/permission-checker.js +75 -0
  191. package/dist/src/security/permission-checker.js.map +1 -0
  192. package/dist/src/security/permission-checker.test.d.ts +6 -0
  193. package/dist/src/security/permission-checker.test.js +208 -0
  194. package/dist/src/security/permission-checker.test.js.map +1 -0
  195. package/dist/src/server.d.ts +12 -11
  196. package/dist/src/server.js +44 -46
  197. package/dist/src/server.js.map +1 -1
  198. package/dist/src/server.test.d.ts +1 -1
  199. package/dist/src/server.test.js +6 -140
  200. package/dist/src/server.test.js.map +1 -1
  201. package/dist/src/session/manager.d.ts +98 -0
  202. package/dist/src/session/manager.js +364 -0
  203. package/dist/src/session/manager.js.map +1 -0
  204. package/dist/src/session/manager.test.d.ts +6 -0
  205. package/dist/src/session/manager.test.js +315 -0
  206. package/dist/src/session/manager.test.js.map +1 -0
  207. package/dist/src/session/session-builder.d.ts +71 -0
  208. package/dist/src/session/session-builder.js +364 -0
  209. package/dist/src/session/session-builder.js.map +1 -0
  210. package/dist/src/session/session-builder.test.d.ts +6 -0
  211. package/dist/src/session/session-builder.test.js +352 -0
  212. package/dist/src/session/session-builder.test.js.map +1 -0
  213. package/dist/src/session/store.d.ts +57 -0
  214. package/dist/src/session/store.js +167 -0
  215. package/dist/src/session/store.js.map +1 -0
  216. package/dist/src/session/store.test.d.ts +6 -0
  217. package/dist/src/session/store.test.js +145 -0
  218. package/dist/src/session/store.test.js.map +1 -0
  219. package/dist/src/session/stream-hooks.d.ts +39 -0
  220. package/dist/src/session/stream-hooks.js +7 -0
  221. package/dist/src/session/stream-hooks.js.map +1 -0
  222. package/dist/src/session/tool-context-factory.d.ts +60 -0
  223. package/dist/src/session/tool-context-factory.js +190 -0
  224. package/dist/src/session/tool-context-factory.js.map +1 -0
  225. package/dist/src/session/tool-context-factory.test.d.ts +6 -0
  226. package/dist/src/session/tool-context-factory.test.js +287 -0
  227. package/dist/src/session/tool-context-factory.test.js.map +1 -0
  228. package/dist/src/session/types.d.ts +188 -0
  229. package/dist/src/session/types.js +7 -0
  230. package/dist/src/session/types.js.map +1 -0
  231. package/dist/src/stores/drizzle-store-backend.d.ts +49 -0
  232. package/dist/src/stores/drizzle-store-backend.js +306 -0
  233. package/dist/src/stores/drizzle-store-backend.js.map +1 -0
  234. package/dist/src/stores/drizzle-store-backend.test.d.ts +6 -0
  235. package/dist/src/stores/drizzle-store-backend.test.js +215 -0
  236. package/dist/src/stores/drizzle-store-backend.test.js.map +1 -0
  237. package/dist/src/stores/index.d.ts +4 -0
  238. package/dist/src/stores/index.js +2 -0
  239. package/dist/src/stores/index.js.map +1 -1
  240. package/dist/src/stores/pglite-store-backend.d.ts +16 -19
  241. package/dist/src/stores/pglite-store-backend.js +85 -233
  242. package/dist/src/stores/pglite-store-backend.js.map +1 -1
  243. package/dist/src/stores/postgres-store-backend.d.ts +30 -0
  244. package/dist/src/stores/postgres-store-backend.js +100 -0
  245. package/dist/src/stores/postgres-store-backend.js.map +1 -0
  246. package/dist/src/stores/schema.d.ts +491 -0
  247. package/dist/src/stores/schema.js +57 -0
  248. package/dist/src/stores/schema.js.map +1 -0
  249. package/dist/src/tools/admin-file-tools.d.ts +13 -0
  250. package/dist/src/tools/admin-file-tools.js +200 -0
  251. package/dist/src/tools/admin-file-tools.js.map +1 -0
  252. package/dist/src/tools/admin-file-tools.test.d.ts +6 -0
  253. package/dist/src/tools/admin-file-tools.test.js +152 -0
  254. package/dist/src/tools/admin-file-tools.test.js.map +1 -0
  255. package/dist/src/tools/custom-tool-adapter.d.ts +41 -0
  256. package/dist/src/tools/custom-tool-adapter.js +190 -0
  257. package/dist/src/tools/custom-tool-adapter.js.map +1 -0
  258. package/dist/src/tools/custom-tool-adapter.test.d.ts +6 -0
  259. package/dist/src/tools/custom-tool-adapter.test.js +244 -0
  260. package/dist/src/tools/custom-tool-adapter.test.js.map +1 -0
  261. package/dist/src/tools/dispatch-tool.d.ts +52 -0
  262. package/dist/src/tools/dispatch-tool.js +71 -0
  263. package/dist/src/tools/dispatch-tool.js.map +1 -0
  264. package/dist/src/tools/dispatch-tool.test.d.ts +6 -0
  265. package/dist/src/tools/dispatch-tool.test.js +75 -0
  266. package/dist/src/tools/dispatch-tool.test.js.map +1 -0
  267. package/dist/src/tools/mcp-tool-adapter.d.ts +18 -0
  268. package/dist/src/tools/mcp-tool-adapter.js +135 -0
  269. package/dist/src/tools/mcp-tool-adapter.js.map +1 -0
  270. package/dist/src/tools/mcp-tool-adapter.test.d.ts +6 -0
  271. package/dist/src/tools/mcp-tool-adapter.test.js +227 -0
  272. package/dist/src/tools/mcp-tool-adapter.test.js.map +1 -0
  273. package/dist/src/tools/registry.d.ts +25 -0
  274. package/dist/src/tools/registry.js +72 -0
  275. package/dist/src/tools/registry.js.map +1 -0
  276. package/dist/src/tools/registry.test.d.ts +6 -0
  277. package/dist/src/tools/registry.test.js +121 -0
  278. package/dist/src/tools/registry.test.js.map +1 -0
  279. package/dist/src/tools/request-tool.d.ts +42 -0
  280. package/dist/src/tools/request-tool.js +190 -0
  281. package/dist/src/tools/request-tool.js.map +1 -0
  282. package/dist/src/tools/request-tool.test.d.ts +6 -0
  283. package/dist/src/tools/request-tool.test.js +254 -0
  284. package/dist/src/tools/request-tool.test.js.map +1 -0
  285. package/dist/src/tools/store-tools.d.ts +29 -0
  286. package/dist/src/tools/store-tools.js +224 -0
  287. package/dist/src/tools/store-tools.js.map +1 -0
  288. package/dist/src/tools/store-tools.test.d.ts +6 -0
  289. package/dist/src/tools/store-tools.test.js +216 -0
  290. package/dist/src/tools/store-tools.test.js.map +1 -0
  291. package/dist/src/tools/types.d.ts +111 -0
  292. package/dist/src/tools/types.js +7 -0
  293. package/dist/src/tools/types.js.map +1 -0
  294. package/dist/src/types.d.ts +20 -12
  295. package/dist/src/types.js +3 -2
  296. package/dist/src/types.js.map +1 -1
  297. package/dist/tsconfig.tsbuildinfo +1 -1
  298. package/package.json +13 -3
  299. package/dist/src/agent/agent-runner.d.ts +0 -33
  300. package/dist/src/agent/agent-runner.js +0 -1040
  301. package/dist/src/agent/agent-runner.js.map +0 -1
  302. package/dist/src/agent/custom-tools-e2e.test.js +0 -566
  303. package/dist/src/agent/custom-tools-e2e.test.js.map +0 -1
  304. package/dist/src/agent/request-helper.d.ts +0 -16
  305. package/dist/src/agent/request-helper.js +0 -96
  306. package/dist/src/agent/request-helper.js.map +0 -1
  307. package/dist/src/agent/stores-e2e.test.js +0 -433
  308. package/dist/src/agent/stores-e2e.test.js.map +0 -1
  309. package/dist/src/agent/tool-context-builder.d.ts +0 -11
  310. package/dist/src/agent/tool-context-builder.js +0 -102
  311. package/dist/src/agent/tool-context-builder.js.map +0 -1
  312. package/dist/src/agent/tool-context-builder.test.js +0 -152
  313. package/dist/src/agent/tool-context-builder.test.js.map +0 -1
  314. package/dist/src/agent/write-repo-file.test.js +0 -270
  315. package/dist/src/agent/write-repo-file.test.js.map +0 -1
  316. package/dist/src/cron/heartbeat-runner.d.ts +0 -21
  317. package/dist/src/cron/heartbeat-runner.js +0 -79
  318. package/dist/src/cron/heartbeat-runner.js.map +0 -1
  319. package/dist/src/cron/heartbeat-runner.test.d.ts +0 -6
  320. package/dist/src/cron/heartbeat-runner.test.js +0 -120
  321. package/dist/src/cron/heartbeat-runner.test.js.map +0 -1
  322. package/dist/src/cron/heartbeat-scheduler.d.ts +0 -26
  323. package/dist/src/cron/heartbeat-scheduler.js +0 -55
  324. package/dist/src/cron/heartbeat-scheduler.js.map +0 -1
  325. package/dist/src/cron/heartbeat-scheduler.test.d.ts +0 -6
  326. package/dist/src/cron/heartbeat-scheduler.test.js +0 -61
  327. package/dist/src/cron/heartbeat-scheduler.test.js.map +0 -1
  328. package/dist/src/routes/ai-stream.test.d.ts +0 -6
  329. package/dist/src/routes/ai-stream.test.js +0 -586
  330. package/dist/src/routes/ai-stream.test.js.map +0 -1
  331. package/dist/src/routes/ask-user-response.d.ts +0 -30
  332. package/dist/src/routes/ask-user-response.js +0 -61
  333. package/dist/src/routes/ask-user-response.js.map +0 -1
  334. package/dist/src/routes/ask-user-response.test.d.ts +0 -6
  335. package/dist/src/routes/ask-user-response.test.js +0 -88
  336. package/dist/src/routes/ask-user-response.test.js.map +0 -1
  337. package/dist/src/routes/chat-stream.test.d.ts +0 -6
  338. package/dist/src/routes/chat-stream.test.js +0 -155
  339. package/dist/src/routes/chat-stream.test.js.map +0 -1
  340. package/dist/src/routes/chat.test.d.ts +0 -6
  341. package/dist/src/routes/chat.test.js +0 -99
  342. package/dist/src/routes/chat.test.js.map +0 -1
  343. package/dist/src/routes/widget-actions.d.ts +0 -49
  344. package/dist/src/routes/widget-actions.js +0 -78
  345. package/dist/src/routes/widget-actions.js.map +0 -1
  346. package/dist/src/session/custom-tool-adapter.d.ts +0 -74
  347. package/dist/src/session/custom-tool-adapter.js +0 -180
  348. package/dist/src/session/custom-tool-adapter.js.map +0 -1
  349. package/dist/src/session/history-converter.d.ts +0 -21
  350. package/dist/src/session/history-converter.js +0 -59
  351. package/dist/src/session/history-converter.js.map +0 -1
  352. package/dist/src/session/history-converter.test.d.ts +0 -6
  353. package/dist/src/session/history-converter.test.js +0 -130
  354. package/dist/src/session/history-converter.test.js.map +0 -1
  355. package/dist/src/session/session-manager.d.ts +0 -219
  356. package/dist/src/session/session-manager.js +0 -915
  357. package/dist/src/session/session-manager.js.map +0 -1
  358. package/dist/src/session/session-manager.test.d.ts +0 -6
  359. package/dist/src/session/session-manager.test.js +0 -451
  360. package/dist/src/session/session-manager.test.js.map +0 -1
  361. package/dist/src/session/session-runner.d.ts +0 -45
  362. package/dist/src/session/session-runner.js +0 -719
  363. package/dist/src/session/session-runner.js.map +0 -1
  364. package/dist/src/session/session-runner.test.d.ts +0 -6
  365. package/dist/src/session/session-runner.test.js +0 -830
  366. package/dist/src/session/session-runner.test.js.map +0 -1
  367. /package/dist/src/{agent/write-repo-file.test.d.ts → __fixtures__/smoke.test.d.ts} +0 -0
@@ -1,17 +1,31 @@
1
1
  /**
2
2
  * @license
3
- * Copyright 2025 Amodal Labs, Inc.
3
+ * Copyright 2026 Amodal Labs, Inc.
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
+ /**
7
+ * Local server for repo-based agent mode.
8
+ *
9
+ * Loads the `.amodal/` config from `config.repoPath`, creates a
10
+ * StandaloneSessionManager, mounts all routes, and optionally watches
11
+ * for config changes (hot reload).
12
+ *
13
+ * Replaces the old initialization sequence that depended on gemini-cli-core's
14
+ * Config, GeminiClient, and upstream ToolRegistry.
15
+ */
6
16
  import express from 'express';
7
17
  import { existsSync } from 'node:fs';
8
18
  import path from 'node:path';
9
19
  import { loadRepo } from '@amodalai/core';
10
- import { SessionManager } from '../session/session-manager.js';
11
- import { LocalShellExecutor } from './shell-executor-local.js';
20
+ import { StandaloneSessionManager } from '../session/manager.js';
21
+ import { PGLiteSessionStore } from '../session/store.js';
22
+ import { buildSessionComponents } from '../session/session-builder.js';
23
+ import { LocalToolExecutor } from './tool-executor-local.js';
24
+ import { buildMcpConfigs } from './mcp-config.js';
12
25
  import { ConfigWatcher } from './config-watcher.js';
13
26
  import { ProactiveRunner } from './proactive/proactive-runner.js';
14
27
  import { createChatStreamRouter } from '../routes/chat-stream.js';
28
+ import { createChatRouter } from '../routes/chat.js';
15
29
  import { createAdminChatRouter } from './routes/admin-chat.js';
16
30
  import { createTaskRouter } from './routes/task.js';
17
31
  import { createInspectRouter } from './routes/inspect.js';
@@ -28,15 +42,10 @@ import { SessionStore } from './session-store.js';
28
42
  import { EvalStore } from './eval-store.js';
29
43
  import { buildPages } from './page-builder.js';
30
44
  import { LOCAL_APP_ID } from '../constants.js';
31
- import { log } from '../logger.js';
45
+ import { log, createLogger } from '../logger.js';
32
46
  const PROVIDER_CHECKS = [
33
- { provider: 'anthropic', envVar: 'ANTHROPIC_API_KEY', url: 'https://api.anthropic.com/v1/models', authHeader: (k) => ({ 'x-api-key': k, 'anthropic-version': '2023-06-01' }) },
34
- { provider: 'openai', envVar: 'OPENAI_API_KEY', url: 'https://api.openai.com/v1/models?limit=1', authHeader: (k) => ({ Authorization: `Bearer ${k}` }) },
35
- { provider: 'google', envVar: 'GOOGLE_API_KEY', url: 'https://generativelanguage.googleapis.com/v1beta/models?pageSize=1', authHeader: (k) => ({ 'x-goog-api-key': k }) },
36
- { provider: 'deepseek', envVar: 'DEEPSEEK_API_KEY', url: 'https://api.deepseek.com/v1/models', authHeader: (k) => ({ Authorization: `Bearer ${k}` }) },
37
- { provider: 'groq', envVar: 'GROQ_API_KEY', url: 'https://api.groq.com/openai/v1/models', authHeader: (k) => ({ Authorization: `Bearer ${k}` }) },
38
- { provider: 'mistral', envVar: 'MISTRAL_API_KEY', url: 'https://api.mistral.ai/v1/models', authHeader: (k) => ({ Authorization: `Bearer ${k}` }) },
39
- { provider: 'xai', envVar: 'XAI_API_KEY', url: 'https://api.x.ai/v1/models', authHeader: (k) => ({ Authorization: `Bearer ${k}` }) },
47
+ { provider: 'anthropic', envVar: 'ANTHROPIC_API_KEY', url: 'https://api.anthropic.com/v1/messages', authHeader: (key) => ({ 'x-api-key': key, 'anthropic-version': '2023-06-01' }) },
48
+ { provider: 'openai', envVar: 'OPENAI_API_KEY', url: 'https://api.openai.com/v1/models', authHeader: (key) => ({ Authorization: `Bearer ${key}` }) },
40
49
  ];
41
50
  async function checkProviders() {
42
51
  const results = await Promise.allSettled(PROVIDER_CHECKS.map(async (check) => {
@@ -45,7 +54,6 @@ async function checkProviders() {
45
54
  return { provider: check.provider, envVar: check.envVar, keySet: false, verified: false };
46
55
  }
47
56
  try {
48
- // globalThis.fetch is available in Node 18+
49
57
  const res = await globalThis.fetch(check.url, {
50
58
  method: 'GET',
51
59
  headers: check.authHeader(key),
@@ -62,46 +70,51 @@ async function checkProviders() {
62
70
  }));
63
71
  return results.map((r) => r.status === 'fulfilled' ? r.value : { provider: 'unknown', envVar: '', keySet: false, verified: false });
64
72
  }
73
+ // ---------------------------------------------------------------------------
74
+ // Local server
75
+ // ---------------------------------------------------------------------------
65
76
  /**
66
77
  * Creates an Express server for repo-based agent mode.
67
78
  *
68
79
  * Loads the `.amodal/` config from `config.repoPath`, creates a
69
- * `SessionManager`, mounts chat/task/inspect/automation/webhook routes,
70
- * and optionally watches for config changes (hot reload).
80
+ * `StandaloneSessionManager`, mounts all routes, and optionally watches
81
+ * for config changes (hot reload).
71
82
  */
72
83
  export async function createLocalServer(config) {
73
- const repo = await loadRepo({ localPath: config.repoPath });
84
+ let bundle = await loadRepo({ localPath: config.repoPath });
74
85
  // Check provider API keys in the background at startup
75
86
  let providerStatuses = PROVIDER_CHECKS.map((c) => ({
76
87
  provider: c.provider, envVar: c.envVar, keySet: !!process.env[c.envVar], verified: false,
77
88
  }));
78
- checkProviders().then((results) => {
89
+ void checkProviders().then((results) => {
79
90
  providerStatuses = results;
80
91
  const verified = results.filter((r) => r.verified).map((r) => r.provider);
81
92
  if (verified.length > 0) {
82
- process.stderr.write(`[dev] Provider keys verified: ${verified.join(', ')}\n`);
93
+ log.info('provider_keys_verified', { providers: verified });
83
94
  }
84
95
  const failed = results.filter((r) => r.keySet && !r.verified);
85
96
  for (const f of failed) {
86
- process.stderr.write(`[dev] Provider key invalid: ${f.provider} (${f.error ?? 'unknown'})\n`);
97
+ log.warn('provider_key_invalid', { provider: f.provider, error: f.error });
87
98
  }
88
- }).catch(() => { });
89
- // Create shell executor if sandbox.shellExec is enabled
90
- const shellExecutor = repo.config.sandbox?.shellExec
91
- ? new LocalShellExecutor()
92
- : undefined;
93
- // Create shared store backend if stores are defined
99
+ }).catch((err) => {
100
+ log.error('provider_check_failed', { error: err instanceof Error ? err.message : String(err) });
101
+ });
102
+ // Create custom tool executor
103
+ const toolExecutor = bundle.tools.length > 0 ? new LocalToolExecutor() : undefined;
104
+ // -------------------------------------------------------------------------
105
+ // Store backend
106
+ // -------------------------------------------------------------------------
94
107
  let storeBackend;
95
108
  let storeBackendType = 'none';
96
- if (repo.stores.length > 0) {
97
- const storeConfig = repo.config.stores;
109
+ if (bundle.stores.length > 0) {
110
+ const storeConfig = bundle.config.stores;
98
111
  const backend = storeConfig?.backend ?? 'pglite';
99
112
  if (backend === 'postgres' && storeConfig?.postgresUrl) {
100
113
  const connUrl = storeConfig.postgresUrl.startsWith('env:')
101
114
  ? process.env[storeConfig.postgresUrl.slice(4)] ?? ''
102
115
  : storeConfig.postgresUrl;
103
116
  if (!connUrl) {
104
- log.error(`Postgres URL not set (${storeConfig.postgresUrl})`, 'dev');
117
+ log.error('store_postgres_url_missing', { configured: storeConfig.postgresUrl });
105
118
  }
106
119
  else {
107
120
  try {
@@ -109,95 +122,150 @@ export async function createLocalServer(config) {
109
122
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dynamic import for optional postgres backend
110
123
  const mod = await import(pgModPath).catch(() => null);
111
124
  if (mod?.createPostgresStoreBackend) {
112
- storeBackend = await mod.createPostgresStoreBackend(repo.stores, connUrl);
125
+ storeBackend = await mod.createPostgresStoreBackend(bundle.stores, connUrl);
113
126
  storeBackendType = 'postgres';
114
- log.info(`Store backend ready (postgres, ${String(repo.stores.length)} stores)`, 'dev');
127
+ log.info('store_backend_ready', { type: 'postgres', storeCount: bundle.stores.length });
115
128
  }
116
129
  else {
117
- log.error('Postgres backend not available — install @amodalai/store-postgres', 'dev');
130
+ log.error('store_postgres_unavailable', { hint: 'install @amodalai/store-postgres' });
118
131
  }
119
132
  }
120
133
  catch (err) {
121
- log.error(`Postgres store backend failed: ${err instanceof Error ? err.message : String(err)}`, 'dev');
122
- log.info('Falling back to PGLite', 'dev');
134
+ log.error('store_postgres_failed', { error: err instanceof Error ? err.message : String(err) });
135
+ log.info('store_fallback_pglite', {});
123
136
  }
124
137
  }
125
138
  }
126
139
  // Default: PGLite
127
140
  if (!storeBackend) {
128
141
  const dataDir = storeConfig?.dataDir ?? `${config.repoPath}/.amodal/store-data`;
129
- // Check for lock file — another instance may be using this data dir
130
- const lockPath = `${dataDir}/server.lock`;
142
+ // Check for lock file — another instance may be using this data dir.
143
+ // Lock file lives in the PARENT dir (not inside dataDir) so it doesn't
144
+ // conflict with PGLite's own PostgreSQL data files (e.g. postmaster.pid).
145
+ const lockPath = `${dataDir}.lock`;
131
146
  try {
132
147
  const { readFileSync, writeFileSync, mkdirSync, unlinkSync } = await import('node:fs');
133
- mkdirSync(dataDir, { recursive: true });
148
+ const path = await import('node:path');
149
+ mkdirSync(path.dirname(dataDir), { recursive: true });
134
150
  try {
135
151
  const existing = readFileSync(lockPath, 'utf-8').trim();
136
152
  try {
137
153
  process.kill(Number(existing), 0);
138
- log.warn(`Another amodal instance (PID ${existing}) may be using this store directory. PGLite does not support concurrent access.`, 'dev');
154
+ log.warn('store_lock_conflict', { pid: existing });
139
155
  }
140
- catch { /* PID not running, stale lock */ }
156
+ catch { /* PID not running stale lock, safe to overwrite */ }
141
157
  }
142
- catch { /* no lock file */ }
158
+ catch { /* No lock file exists — first run */ }
143
159
  writeFileSync(lockPath, String(process.pid));
144
160
  const lockCleanup = lockPath;
145
161
  process.on('exit', () => { try {
146
162
  unlinkSync(lockCleanup);
147
163
  }
148
- catch { /* */ } });
164
+ catch { /* exit handler — can't log */ } });
165
+ }
166
+ catch (err) {
167
+ log.warn('store_lock_setup_failed', { dataDir, error: err instanceof Error ? err.message : String(err) });
149
168
  }
150
- catch { /* lock file handling failed, proceed anyway */ }
151
169
  try {
152
- storeBackend = await createPGLiteStoreBackend(repo.stores, dataDir);
170
+ storeBackend = await createPGLiteStoreBackend(bundle.stores, dataDir);
153
171
  storeBackendType = 'pglite';
154
- log.info(`Store backend ready (pglite, ${String(repo.stores.length)} stores, dir: ${dataDir})`, 'dev');
172
+ log.info('store_backend_ready', { type: 'pglite', storeCount: bundle.stores.length, dataDir });
155
173
  }
156
174
  catch (err) {
157
- log.error(`Store backend failed to initialize: ${err instanceof Error ? err.message : String(err)}`, 'dev');
158
- log.error(`Try deleting ${dataDir} and restarting`, 'dev');
175
+ const errMsg = err instanceof Error ? err.message : (typeof err === 'object' ? JSON.stringify(err) : String(err));
176
+ log.error('store_backend_init_failed', { error: errMsg, dataDir });
159
177
  }
160
178
  }
161
179
  }
162
- // Session persistence — created before SessionManager so hydration works
163
- const sessionStore = new SessionStore(config.repoPath);
164
- const sessionManager = new SessionManager({
165
- baseParams: {
166
- sessionId: 'local-init',
167
- interactive: false,
168
- noBrowser: true,
169
- debugMode: process.env['DEBUG'] === 'true',
170
- cwd: config.repoPath,
171
- targetDir: config.repoPath,
172
- },
180
+ // -------------------------------------------------------------------------
181
+ // Session manager (new standalone stack)
182
+ // -------------------------------------------------------------------------
183
+ const sessionLogger = createLogger({ component: 'session-manager' });
184
+ const sessionStore = new PGLiteSessionStore({ logger: sessionLogger });
185
+ await sessionStore.initialize();
186
+ const sessionManager = new StandaloneSessionManager({
187
+ logger: sessionLogger,
188
+ store: sessionStore,
173
189
  ttlMs: config.sessionTtlMs,
174
- bundle: repo,
175
- shellExecutor,
176
- storeBackend,
177
- sessionStore: {
178
- async getSession(sessionId) {
179
- const persisted = sessionStore.load(sessionId);
180
- if (!persisted || !persisted.conversationHistory.length)
181
- return null;
182
- return {
183
- id: persisted.id,
184
- app_id: persisted.appId,
185
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Persisted data
186
- messages: persisted.conversationHistory,
187
- status: 'completed',
188
- };
189
- },
190
- },
191
190
  });
192
- const runner = new ProactiveRunner(repo, {
191
+ sessionManager.start();
192
+ // Legacy session store for UI history (file-based)
193
+ const legacySessionStore = new SessionStore(config.repoPath);
194
+ // -------------------------------------------------------------------------
195
+ // MCP connections (shared across sessions)
196
+ // -------------------------------------------------------------------------
197
+ let mcpManager = null;
198
+ {
199
+ const { McpManager } = await import('@amodalai/core');
200
+ const mcpConfigs = buildMcpConfigs(bundle);
201
+ if (Object.keys(mcpConfigs).length > 0) {
202
+ const manager = new McpManager();
203
+ try {
204
+ await manager.startServers(mcpConfigs);
205
+ if (manager.connectedCount > 0) {
206
+ mcpManager = manager;
207
+ const tools = manager.getDiscoveredTools();
208
+ log.info('mcp_initialized', { servers: manager.connectedCount, tools: tools.length });
209
+ }
210
+ }
211
+ catch (err) {
212
+ log.error('mcp_init_failed', { error: err instanceof Error ? err.message : String(err) });
213
+ }
214
+ }
215
+ }
216
+ // -------------------------------------------------------------------------
217
+ // Shared resources for route handlers
218
+ // -------------------------------------------------------------------------
219
+ const shared = {
220
+ storeBackend: storeBackend ?? null,
221
+ mcpManager,
222
+ logger: log,
223
+ toolExecutor,
224
+ };
225
+ // Helper: get current bundle (updated by config watcher)
226
+ const getBundle = () => bundle;
227
+ // Helper: create automation session components
228
+ const createAutomationSessionComponents = () => {
229
+ const components = buildSessionComponents({
230
+ bundle,
231
+ storeBackend: storeBackend ?? null,
232
+ mcpManager,
233
+ logger: log,
234
+ toolExecutor,
235
+ sessionType: 'automation',
236
+ });
237
+ const session = sessionManager.create({
238
+ tenantId: 'local',
239
+ userId: 'automation',
240
+ provider: components.provider,
241
+ toolRegistry: components.toolRegistry,
242
+ permissionChecker: components.permissionChecker,
243
+ systemPrompt: components.systemPrompt,
244
+ userRoles: components.userRoles,
245
+ toolContextFactory: components.toolContextFactory,
246
+ appId: LOCAL_APP_ID,
247
+ });
248
+ return { session, toolContextFactory: components.toolContextFactory };
249
+ };
250
+ // -------------------------------------------------------------------------
251
+ // Proactive runner
252
+ // -------------------------------------------------------------------------
253
+ const runner = new ProactiveRunner(bundle, {
254
+ sessionManager,
255
+ createSessionComponents: createAutomationSessionComponents,
256
+ logger: log,
193
257
  webhookSecret: config.webhookSecret,
194
- createSession: async () => sessionManager.create(LOCAL_APP_ID),
195
- destroySession: async (id) => sessionManager.destroy(id),
196
- onSessionComplete: (session, automationName) => {
197
- sessionStore.save(session, automationName);
258
+ onSessionComplete: (session) => {
259
+ void sessionManager.persist(session);
198
260
  },
199
261
  });
262
+ // -------------------------------------------------------------------------
263
+ // Config watcher (hot reload)
264
+ // -------------------------------------------------------------------------
200
265
  let watcher = null;
266
+ // -------------------------------------------------------------------------
267
+ // Express app
268
+ // -------------------------------------------------------------------------
201
269
  const app = express();
202
270
  // CORS
203
271
  const corsOrigin = config.corsOrigin ?? '*';
@@ -227,9 +295,9 @@ export async function createLocalServer(config) {
227
295
  app.post('/auth/token', (_req, res) => {
228
296
  res.json({ token: '', expires_at: null });
229
297
  });
230
- // Unified config endpoint — same path as hosted, different response
298
+ // Unified config endpoint
231
299
  app.get('/api/config', (_req, res) => {
232
- const bundleData = sessionManager.getBundle();
300
+ const bundleData = getBundle();
233
301
  const cfg = bundleData.config;
234
302
  // Collect all env:* references from connection specs
235
303
  const envRefs = [];
@@ -241,11 +309,8 @@ export async function createLocalServer(config) {
241
309
  }
242
310
  }
243
311
  res.json({
244
- // Common fields (used by useHostedConfig)
245
312
  appId: LOCAL_APP_ID,
246
313
  appName: cfg?.name ?? '',
247
- // No authMode — signals to the SPA that no auth is needed
248
- // Local dev fields (used by config pages)
249
314
  name: cfg?.name ?? '',
250
315
  version: cfg?.version ?? '',
251
316
  description: cfg?.description ?? '',
@@ -262,43 +327,38 @@ export async function createLocalServer(config) {
262
327
  // Resolve resume session ID
263
328
  let resumeSessionId = config.resumeSessionId;
264
329
  if (resumeSessionId === 'latest') {
265
- resumeSessionId = sessionStore.latest() ?? undefined;
330
+ resumeSessionId = legacySessionStore.latest() ?? undefined;
266
331
  }
267
332
  if (resumeSessionId) {
268
- log.debug(`Resume session: ${resumeSessionId}`, 'dev');
333
+ log.debug('resume_session', { sessionId: resumeSessionId });
269
334
  }
270
335
  // Client config — tells the web UI which session to resume
271
336
  app.get('/config', (_req, res) => {
272
337
  res.json({ resumeSessionId: resumeSessionId ?? null });
273
338
  });
274
- // Sessions endpoints
339
+ // Sessions endpoints (legacy file-based store for UI history)
275
340
  app.get('/sessions', (req, res) => {
276
341
  const automationFilter = typeof req.query?.['automation'] === 'string' ? String(req.query['automation']) : undefined;
277
- const all = sessionStore.list();
278
- // Filter out eval and admin sessions from chat history
342
+ const all = legacySessionStore.list();
279
343
  const visible = all.filter((s) => s.appId !== 'eval-runner' && s.appId !== 'admin');
280
344
  const filtered = automationFilter ? visible.filter((s) => s.automationName === automationFilter) : visible;
281
345
  res.json({ sessions: filtered });
282
346
  });
283
347
  app.get('/session/:id', (req, res) => {
284
- const persisted = sessionStore.load(req.params['id'] ?? '');
348
+ const persisted = legacySessionStore.load(req.params['id'] ?? '');
285
349
  if (!persisted) {
286
350
  res.status(404).json({ error: 'Session not found' });
287
351
  return;
288
352
  }
289
- // Convert persisted messages to the format the UI expects.
290
- // Supports both SessionMessage format ({type, text}) and legacy LLMMessage ({role, content}).
291
353
  const messages = persisted.conversationHistory.map((msg) => {
292
354
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Persisted message
293
355
  const m = msg;
294
- // SessionMessage format (new): {type: 'user'|'assistant_text'|'error', text, toolCalls?, ...}
295
356
  if (m['type'] === 'user') {
296
357
  return { role: 'user', text: String(m['text'] ?? '') };
297
358
  }
298
359
  if (m['type'] === 'assistant_text') {
299
360
  return { role: 'assistant', text: String(m['text'] ?? ''), toolCalls: m['toolCalls'] };
300
361
  }
301
- // Legacy LLMMessage format: {role: 'user'|'assistant', content: string|Block[]}
302
362
  if (m['role'] === 'user') {
303
363
  return { role: 'user', text: typeof m['content'] === 'string' ? m['content'] : '' };
304
364
  }
@@ -321,23 +381,17 @@ export async function createLocalServer(config) {
321
381
  }
322
382
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- validated above
323
383
  const title = body['title'];
324
- const updated = sessionStore.updateTitle(sessionId, title);
384
+ const updated = legacySessionStore.updateTitle(sessionId, title);
325
385
  if (!updated) {
326
386
  res.status(404).json({ error: 'Session not found' });
327
387
  return;
328
388
  }
329
- // Also update in-memory session if active
330
- const activeSession = sessionManager.get(sessionId);
331
- if (activeSession) {
332
- activeSession.title = title;
333
- }
334
389
  res.json({ ok: true });
335
390
  });
336
391
  app.delete('/session/:id', (req, res) => {
337
392
  const sessionId = req.params['id'] ?? '';
338
- // Destroy in-memory session if active
339
393
  void sessionManager.destroy(sessionId);
340
- const deleted = sessionStore.delete(sessionId);
394
+ const deleted = legacySessionStore.delete(sessionId);
341
395
  if (!deleted) {
342
396
  res.status(404).json({ error: 'Session not found' });
343
397
  return;
@@ -348,29 +402,69 @@ export async function createLocalServer(config) {
348
402
  app.use(createFilesRouter({ repoPath: config.repoPath }));
349
403
  // Evals
350
404
  const evalStore = new EvalStore(config.repoPath);
351
- app.use(createEvalRouter({ sessionManager, evalStore, repoPath: config.repoPath, getPort: () => config.port }));
405
+ app.use(createEvalRouter({ getBundle, evalStore, repoPath: config.repoPath, getPort: () => config.port }));
352
406
  // Feedback
353
407
  const feedbackStore = new FeedbackStore(config.repoPath);
354
408
  app.use(createFeedbackRouter({ feedbackStore }));
355
- // Routes
409
+ // Chat routes (new stack)
356
410
  app.use(createChatStreamRouter({
357
411
  sessionManager,
412
+ bundleResolver: { staticBundle: bundle },
413
+ shared,
358
414
  createStreamHooks: () => ({
359
415
  onSessionPersist: (sessionId) => {
360
416
  const session = sessionManager.get(sessionId);
361
- if (session)
362
- sessionStore.save(session);
417
+ if (session) {
418
+ void sessionManager.persist(session);
419
+ // Also mirror to the legacy file-based store so the /sessions
420
+ // endpoints (read by the UI history panel) see chat sessions.
421
+ // Without this, chat sessions only land in PGLite and the UI
422
+ // only sees automation/task sessions. PGLite remains the source
423
+ // of truth — a mirror failure is logged and swallowed (the hook
424
+ // is invoked from fireDrainHooks after the response has drained,
425
+ // so throwing here would break the route handler).
426
+ try {
427
+ legacySessionStore.save({
428
+ id: session.id,
429
+ appId: session.appId,
430
+ title: session.metadata.title,
431
+ messages: session.messages,
432
+ createdAt: session.createdAt,
433
+ lastAccessedAt: session.lastAccessedAt,
434
+ });
435
+ }
436
+ catch (err) {
437
+ log.warn('legacy_session_mirror_failed', {
438
+ sessionId: session.id,
439
+ error: err instanceof Error ? err.message : String(err),
440
+ });
441
+ }
442
+ }
363
443
  },
364
444
  }),
365
445
  }));
366
- app.use(createTaskRouter({ sessionManager }));
367
- app.use(createAdminChatRouter({ sessionManager, getPort: () => config.port }));
368
- app.use(createInspectRouter({ sessionManager, repoPath: config.repoPath }));
446
+ app.use(createChatRouter({
447
+ sessionManager,
448
+ bundleResolver: { staticBundle: bundle },
449
+ shared,
450
+ }));
451
+ // Task runner
452
+ app.use(createTaskRouter({ sessionManager, createTaskSession: createAutomationSessionComponents }));
453
+ // Admin chat (new stack)
454
+ app.use(createAdminChatRouter({
455
+ sessionManager,
456
+ shared,
457
+ getBundle,
458
+ getPort: () => config.port,
459
+ }));
460
+ // Inspect
461
+ app.use(createInspectRouter({ getBundle, repoPath: config.repoPath }));
462
+ // Automations
369
463
  app.use(createAutomationRouter({ runner }));
370
464
  app.use(createWebhookRouter({ runner, webhookSecret: config.webhookSecret }));
371
465
  // Store REST API (if stores are defined)
372
466
  if (storeBackend) {
373
- app.use(createStoresRouter({ repo, storeBackend, appId: LOCAL_APP_ID }));
467
+ app.use(createStoresRouter({ repo: bundle, storeBackend, appId: LOCAL_APP_ID }));
374
468
  }
375
469
  // Build user pages (if pages/ directory exists)
376
470
  let builtPages = [];
@@ -378,14 +472,12 @@ export async function createLocalServer(config) {
378
472
  const result = await buildPages(config.repoPath);
379
473
  builtPages = result.pages;
380
474
  if (builtPages.length > 0) {
381
- log.info(`Built ${String(builtPages.length)} page(s)`, 'dev');
382
- // Serve compiled page bundles
475
+ log.info('pages_built', { count: builtPages.length });
383
476
  app.use('/pages-bundle', express.static(result.outDir));
384
477
  }
385
478
  }
386
479
  catch (err) {
387
- const msg = err instanceof Error ? err.message : String(err);
388
- log.error(`Page build failed: ${msg}`, 'dev');
480
+ log.error('pages_build_failed', { error: err instanceof Error ? err.message : String(err) });
389
481
  }
390
482
  // Pages list endpoint
391
483
  app.get('/api/pages', (_req, res) => {
@@ -398,11 +490,8 @@ export async function createLocalServer(config) {
398
490
  app.use(config.appMiddleware);
399
491
  }
400
492
  else if (config.staticAppDir && existsSync(config.staticAppDir)) {
401
- // Serve pre-built SPA static assets with index.html fallback
402
493
  app.use(express.static(config.staticAppDir));
403
- // SPA fallback — serve index.html for any non-API, non-static route
404
494
  app.use((_req, res, next) => {
405
- // Don't intercept API or inspect routes (already handled above)
406
495
  if (_req.path.startsWith('/api/') || _req.path.startsWith('/inspect/') || _req.method !== 'GET') {
407
496
  next();
408
497
  return;
@@ -427,17 +516,16 @@ export async function createLocalServer(config) {
427
516
  // Start hot reload watcher
428
517
  if (config.hotReload) {
429
518
  watcher = new ConfigWatcher(config.repoPath, (newBundle) => {
430
- sessionManager.updateBundle(newBundle);
519
+ bundle = newBundle;
520
+ // Shared resources and session components will pick up the new
521
+ // bundle on next session creation via getBundle().
522
+ log.info('config_reloaded', { name: newBundle.config.name });
431
523
  });
432
524
  watcher.start();
433
525
  }
434
526
  return new Promise((resolve) => {
435
527
  const httpServer = app.listen(port, host, () => {
436
- log.info(`Repo server listening on ${host}:${port}`);
437
- log.info(`Repo: ${config.repoPath}`);
438
- if (config.hotReload) {
439
- log.info('Hot reload enabled');
440
- }
528
+ log.info('server_started', { host, port, repoPath: config.repoPath, hotReload: !!config.hotReload });
441
529
  resolve(httpServer);
442
530
  });
443
531
  server = httpServer;
@@ -462,10 +550,13 @@ export async function createLocalServer(config) {
462
550
  server = null;
463
551
  }
464
552
  await sessionManager.shutdown();
553
+ if (mcpManager) {
554
+ await mcpManager.shutdown();
555
+ }
465
556
  if (storeBackend) {
466
557
  await storeBackend.close();
467
558
  }
468
- log.info('Repo server stopped');
559
+ log.info('server_stopped', {});
469
560
  },
470
561
  };
471
562
  }