@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.
- package/dist/src/__fixtures__/README.md +84 -0
- package/dist/src/__fixtures__/smoke-agent/amodal.json +11 -0
- package/dist/src/__fixtures__/smoke-agent/automations/test-auto.md +5 -0
- package/dist/src/__fixtures__/smoke-agent/connections/mock-api/access.json +11 -0
- package/dist/src/__fixtures__/smoke-agent/connections/mock-api/spec.json +4 -0
- package/dist/src/__fixtures__/smoke-agent/connections/mock-api/surface.md +9 -0
- package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/access.json +3 -0
- package/dist/src/__fixtures__/smoke-agent/connections/mock-mcp/spec.json +8 -0
- package/dist/src/__fixtures__/smoke-agent/evals/basic-eval.md +12 -0
- package/dist/src/__fixtures__/smoke-agent/knowledge/test-knowledge.md +3 -0
- package/dist/src/__fixtures__/smoke-agent/skills/test-skill/SKILL.md +11 -0
- package/dist/src/__fixtures__/smoke-agent/stores/test-items.json +11 -0
- package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.d.ts +18 -0
- package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.js +22 -0
- package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/handler.js.map +1 -0
- package/dist/src/__fixtures__/smoke-agent/tools/echo_tool/tool.json +17 -0
- package/dist/src/__fixtures__/smoke.test.js +718 -0
- package/dist/src/__fixtures__/smoke.test.js.map +1 -0
- package/dist/src/{agent/stores-e2e.test.d.ts → __tests__/providers.test.d.ts} +1 -1
- package/dist/src/__tests__/providers.test.js +209 -0
- package/dist/src/__tests__/providers.test.js.map +1 -0
- package/dist/src/__tests__/test-providers.d.ts +40 -0
- package/dist/src/__tests__/test-providers.js +61 -0
- package/dist/src/__tests__/test-providers.js.map +1 -0
- package/dist/src/agent/agent-types.d.ts +2 -2
- package/dist/src/agent/config-watcher.test.js +18 -14
- package/dist/src/agent/config-watcher.test.js.map +1 -1
- package/dist/src/agent/local-server.d.ts +3 -3
- package/dist/src/agent/local-server.js +213 -122
- package/dist/src/agent/local-server.js.map +1 -1
- package/dist/src/agent/local-server.test.js +17 -13
- package/dist/src/agent/local-server.test.js.map +1 -1
- package/dist/src/agent/loop-types.d.ts +175 -0
- package/dist/src/agent/loop-types.js +20 -0
- package/dist/src/agent/loop-types.js.map +1 -0
- package/dist/src/agent/loop.d.ts +31 -0
- package/dist/src/agent/loop.js +139 -0
- package/dist/src/agent/loop.js.map +1 -0
- package/dist/src/agent/{tool-context-builder.test.d.ts → loop.test.d.ts} +1 -1
- package/dist/src/agent/loop.test.js +1030 -0
- package/dist/src/agent/loop.test.js.map +1 -0
- package/dist/src/agent/mcp-config.d.ts +28 -0
- package/dist/src/agent/mcp-config.js +57 -0
- package/dist/src/agent/mcp-config.js.map +1 -0
- package/dist/src/agent/page-builder.js +6 -1
- package/dist/src/agent/page-builder.js.map +1 -1
- package/dist/src/agent/proactive/proactive-runner.d.ts +24 -8
- package/dist/src/agent/proactive/proactive-runner.js +36 -33
- package/dist/src/agent/proactive/proactive-runner.js.map +1 -1
- package/dist/src/agent/proactive/proactive-runner.test.d.ts +1 -1
- package/dist/src/agent/proactive/proactive-runner.test.js +75 -87
- package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -1
- package/dist/src/agent/routes/admin-chat.d.ts +15 -3
- package/dist/src/agent/routes/admin-chat.js +63 -17
- package/dist/src/agent/routes/admin-chat.js.map +1 -1
- package/dist/src/agent/routes/automations.js +5 -4
- package/dist/src/agent/routes/automations.js.map +1 -1
- package/dist/src/agent/routes/evals.d.ts +3 -2
- package/dist/src/agent/routes/evals.js +25 -11
- package/dist/src/agent/routes/evals.js.map +1 -1
- package/dist/src/agent/routes/files.js +7 -6
- package/dist/src/agent/routes/files.js.map +1 -1
- package/dist/src/agent/routes/inspect.d.ts +6 -2
- package/dist/src/agent/routes/inspect.js +32 -15
- package/dist/src/agent/routes/inspect.js.map +1 -1
- package/dist/src/agent/routes/inspect.test.js +18 -42
- package/dist/src/agent/routes/inspect.test.js.map +1 -1
- package/dist/src/agent/routes/stores.js +9 -8
- package/dist/src/agent/routes/stores.js.map +1 -1
- package/dist/src/agent/routes/task.d.ts +15 -3
- package/dist/src/agent/routes/task.js +16 -6
- package/dist/src/agent/routes/task.js.map +1 -1
- package/dist/src/agent/routes/task.test.d.ts +1 -1
- package/dist/src/agent/routes/task.test.js +70 -53
- package/dist/src/agent/routes/task.test.js.map +1 -1
- package/dist/src/agent/routes/webhooks.js +12 -2
- package/dist/src/agent/routes/webhooks.js.map +1 -1
- package/dist/src/agent/session-store.d.ts +11 -2
- package/dist/src/agent/session-store.js +1 -1
- package/dist/src/agent/session-store.js.map +1 -1
- package/dist/src/agent/snapshot-server.d.ts +2 -22
- package/dist/src/agent/snapshot-server.js +50 -27
- package/dist/src/agent/snapshot-server.js.map +1 -1
- package/dist/src/agent/states/compacting.d.ts +14 -0
- package/dist/src/agent/states/compacting.js +258 -0
- package/dist/src/agent/states/compacting.js.map +1 -0
- package/dist/src/agent/states/confirming.d.ts +10 -0
- package/dist/src/agent/states/confirming.js +76 -0
- package/dist/src/agent/states/confirming.js.map +1 -0
- package/dist/src/agent/states/dispatching.d.ts +18 -0
- package/dist/src/agent/states/dispatching.js +241 -0
- package/dist/src/agent/states/dispatching.js.map +1 -0
- package/dist/src/agent/states/executing.d.ts +21 -0
- package/dist/src/agent/states/executing.js +308 -0
- package/dist/src/agent/states/executing.js.map +1 -0
- package/dist/src/agent/states/streaming.d.ts +10 -0
- package/dist/src/agent/states/streaming.js +155 -0
- package/dist/src/agent/states/streaming.js.map +1 -0
- package/dist/src/agent/states/thinking.d.ts +13 -0
- package/dist/src/agent/states/thinking.js +233 -0
- package/dist/src/agent/states/thinking.js.map +1 -0
- package/dist/src/agent/token-estimate.d.ts +17 -0
- package/dist/src/agent/token-estimate.js +13 -0
- package/dist/src/agent/token-estimate.js.map +1 -0
- package/dist/src/agent/tool-executor-local.js +9 -18
- package/dist/src/agent/tool-executor-local.js.map +1 -1
- package/dist/src/agent/tool-executor-local.test.js +3 -5
- package/dist/src/agent/tool-executor-local.test.js.map +1 -1
- package/dist/src/api/create-agent.d.ts +15 -0
- package/dist/src/api/create-agent.js +137 -0
- package/dist/src/api/create-agent.js.map +1 -0
- package/dist/src/api/types.d.ts +68 -0
- package/dist/src/api/types.js +7 -0
- package/dist/src/api/types.js.map +1 -0
- package/dist/src/config.d.ts +96 -0
- package/dist/src/config.js +221 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/{agent/custom-tools-e2e.test.d.ts → config.test.d.ts} +1 -1
- package/dist/src/config.test.js +235 -0
- package/dist/src/config.test.js.map +1 -0
- package/dist/src/context/compiler.d.ts +13 -0
- package/dist/src/context/compiler.js +358 -0
- package/dist/src/context/compiler.js.map +1 -0
- package/dist/src/context/compiler.test.d.ts +6 -0
- package/dist/src/context/compiler.test.js +532 -0
- package/dist/src/context/compiler.test.js.map +1 -0
- package/dist/src/context/types.d.ts +110 -0
- package/dist/src/context/types.js +7 -0
- package/dist/src/context/types.js.map +1 -0
- package/dist/src/errors.d.ts +145 -0
- package/dist/src/errors.js +218 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/errors.test.d.ts +6 -0
- package/dist/src/errors.test.js +203 -0
- package/dist/src/errors.test.js.map +1 -0
- package/dist/src/index.d.ts +38 -6
- package/dist/src/index.js +40 -22
- package/dist/src/index.js.map +1 -1
- package/dist/src/logger.d.ts +15 -31
- package/dist/src/logger.js +19 -78
- package/dist/src/logger.js.map +1 -1
- package/dist/src/logger.test.d.ts +6 -0
- package/dist/src/logger.test.js +198 -0
- package/dist/src/logger.test.js.map +1 -0
- package/dist/src/providers/create-provider.d.ts +23 -0
- package/dist/src/providers/create-provider.js +185 -0
- package/dist/src/providers/create-provider.js.map +1 -0
- package/dist/src/providers/create-provider.test.d.ts +6 -0
- package/dist/src/providers/create-provider.test.js +95 -0
- package/dist/src/providers/create-provider.test.js.map +1 -0
- package/dist/src/providers/failover.d.ts +38 -0
- package/dist/src/providers/failover.js +147 -0
- package/dist/src/providers/failover.js.map +1 -0
- package/dist/src/providers/failover.test.d.ts +6 -0
- package/dist/src/providers/failover.test.js +169 -0
- package/dist/src/providers/failover.test.js.map +1 -0
- package/dist/src/providers/types.d.ts +110 -0
- package/dist/src/providers/types.js +7 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/routes/ai-stream.d.ts +13 -10
- package/dist/src/routes/ai-stream.js +76 -38
- package/dist/src/routes/ai-stream.js.map +1 -1
- package/dist/src/routes/chat-new.test.d.ts +6 -0
- package/dist/src/routes/chat-new.test.js +107 -0
- package/dist/src/routes/chat-new.test.js.map +1 -0
- package/dist/src/routes/chat-stream-new.test.d.ts +6 -0
- package/dist/src/routes/chat-stream-new.test.js +135 -0
- package/dist/src/routes/chat-stream-new.test.js.map +1 -0
- package/dist/src/routes/chat-stream.d.ts +14 -4
- package/dist/src/routes/chat-stream.js +47 -27
- package/dist/src/routes/chat-stream.js.map +1 -1
- package/dist/src/routes/chat.d.ts +13 -4
- package/dist/src/routes/chat.js +60 -22
- package/dist/src/routes/chat.js.map +1 -1
- package/dist/src/routes/health.d.ts +3 -2
- package/dist/src/routes/health.js.map +1 -1
- package/dist/src/routes/route-helpers.d.ts +50 -0
- package/dist/src/routes/route-helpers.js +80 -0
- package/dist/src/routes/route-helpers.js.map +1 -0
- package/dist/src/routes/session-resolver.d.ts +72 -0
- package/dist/src/routes/session-resolver.js +123 -0
- package/dist/src/routes/session-resolver.js.map +1 -0
- package/dist/src/routes/session-resolver.test.d.ts +6 -0
- package/dist/src/routes/session-resolver.test.js +206 -0
- package/dist/src/routes/session-resolver.test.js.map +1 -0
- package/dist/src/routes/webhooks.d.ts +3 -1
- package/dist/src/routes/webhooks.js +12 -2
- package/dist/src/routes/webhooks.js.map +1 -1
- package/dist/src/security/permission-checker.d.ts +80 -0
- package/dist/src/security/permission-checker.js +75 -0
- package/dist/src/security/permission-checker.js.map +1 -0
- package/dist/src/security/permission-checker.test.d.ts +6 -0
- package/dist/src/security/permission-checker.test.js +208 -0
- package/dist/src/security/permission-checker.test.js.map +1 -0
- package/dist/src/server.d.ts +12 -11
- package/dist/src/server.js +44 -46
- package/dist/src/server.js.map +1 -1
- package/dist/src/server.test.d.ts +1 -1
- package/dist/src/server.test.js +6 -140
- package/dist/src/server.test.js.map +1 -1
- package/dist/src/session/manager.d.ts +98 -0
- package/dist/src/session/manager.js +364 -0
- package/dist/src/session/manager.js.map +1 -0
- package/dist/src/session/manager.test.d.ts +6 -0
- package/dist/src/session/manager.test.js +315 -0
- package/dist/src/session/manager.test.js.map +1 -0
- package/dist/src/session/session-builder.d.ts +71 -0
- package/dist/src/session/session-builder.js +364 -0
- package/dist/src/session/session-builder.js.map +1 -0
- package/dist/src/session/session-builder.test.d.ts +6 -0
- package/dist/src/session/session-builder.test.js +352 -0
- package/dist/src/session/session-builder.test.js.map +1 -0
- package/dist/src/session/store.d.ts +57 -0
- package/dist/src/session/store.js +167 -0
- package/dist/src/session/store.js.map +1 -0
- package/dist/src/session/store.test.d.ts +6 -0
- package/dist/src/session/store.test.js +145 -0
- package/dist/src/session/store.test.js.map +1 -0
- package/dist/src/session/stream-hooks.d.ts +39 -0
- package/dist/src/session/stream-hooks.js +7 -0
- package/dist/src/session/stream-hooks.js.map +1 -0
- package/dist/src/session/tool-context-factory.d.ts +60 -0
- package/dist/src/session/tool-context-factory.js +190 -0
- package/dist/src/session/tool-context-factory.js.map +1 -0
- package/dist/src/session/tool-context-factory.test.d.ts +6 -0
- package/dist/src/session/tool-context-factory.test.js +287 -0
- package/dist/src/session/tool-context-factory.test.js.map +1 -0
- package/dist/src/session/types.d.ts +188 -0
- package/dist/src/session/types.js +7 -0
- package/dist/src/session/types.js.map +1 -0
- package/dist/src/stores/drizzle-store-backend.d.ts +49 -0
- package/dist/src/stores/drizzle-store-backend.js +306 -0
- package/dist/src/stores/drizzle-store-backend.js.map +1 -0
- package/dist/src/stores/drizzle-store-backend.test.d.ts +6 -0
- package/dist/src/stores/drizzle-store-backend.test.js +215 -0
- package/dist/src/stores/drizzle-store-backend.test.js.map +1 -0
- package/dist/src/stores/index.d.ts +4 -0
- package/dist/src/stores/index.js +2 -0
- package/dist/src/stores/index.js.map +1 -1
- package/dist/src/stores/pglite-store-backend.d.ts +16 -19
- package/dist/src/stores/pglite-store-backend.js +85 -233
- package/dist/src/stores/pglite-store-backend.js.map +1 -1
- package/dist/src/stores/postgres-store-backend.d.ts +30 -0
- package/dist/src/stores/postgres-store-backend.js +100 -0
- package/dist/src/stores/postgres-store-backend.js.map +1 -0
- package/dist/src/stores/schema.d.ts +491 -0
- package/dist/src/stores/schema.js +57 -0
- package/dist/src/stores/schema.js.map +1 -0
- package/dist/src/tools/admin-file-tools.d.ts +13 -0
- package/dist/src/tools/admin-file-tools.js +200 -0
- package/dist/src/tools/admin-file-tools.js.map +1 -0
- package/dist/src/tools/admin-file-tools.test.d.ts +6 -0
- package/dist/src/tools/admin-file-tools.test.js +152 -0
- package/dist/src/tools/admin-file-tools.test.js.map +1 -0
- package/dist/src/tools/custom-tool-adapter.d.ts +41 -0
- package/dist/src/tools/custom-tool-adapter.js +190 -0
- package/dist/src/tools/custom-tool-adapter.js.map +1 -0
- package/dist/src/tools/custom-tool-adapter.test.d.ts +6 -0
- package/dist/src/tools/custom-tool-adapter.test.js +244 -0
- package/dist/src/tools/custom-tool-adapter.test.js.map +1 -0
- package/dist/src/tools/dispatch-tool.d.ts +52 -0
- package/dist/src/tools/dispatch-tool.js +71 -0
- package/dist/src/tools/dispatch-tool.js.map +1 -0
- package/dist/src/tools/dispatch-tool.test.d.ts +6 -0
- package/dist/src/tools/dispatch-tool.test.js +75 -0
- package/dist/src/tools/dispatch-tool.test.js.map +1 -0
- package/dist/src/tools/mcp-tool-adapter.d.ts +18 -0
- package/dist/src/tools/mcp-tool-adapter.js +135 -0
- package/dist/src/tools/mcp-tool-adapter.js.map +1 -0
- package/dist/src/tools/mcp-tool-adapter.test.d.ts +6 -0
- package/dist/src/tools/mcp-tool-adapter.test.js +227 -0
- package/dist/src/tools/mcp-tool-adapter.test.js.map +1 -0
- package/dist/src/tools/registry.d.ts +25 -0
- package/dist/src/tools/registry.js +72 -0
- package/dist/src/tools/registry.js.map +1 -0
- package/dist/src/tools/registry.test.d.ts +6 -0
- package/dist/src/tools/registry.test.js +121 -0
- package/dist/src/tools/registry.test.js.map +1 -0
- package/dist/src/tools/request-tool.d.ts +42 -0
- package/dist/src/tools/request-tool.js +190 -0
- package/dist/src/tools/request-tool.js.map +1 -0
- package/dist/src/tools/request-tool.test.d.ts +6 -0
- package/dist/src/tools/request-tool.test.js +254 -0
- package/dist/src/tools/request-tool.test.js.map +1 -0
- package/dist/src/tools/store-tools.d.ts +29 -0
- package/dist/src/tools/store-tools.js +224 -0
- package/dist/src/tools/store-tools.js.map +1 -0
- package/dist/src/tools/store-tools.test.d.ts +6 -0
- package/dist/src/tools/store-tools.test.js +216 -0
- package/dist/src/tools/store-tools.test.js.map +1 -0
- package/dist/src/tools/types.d.ts +111 -0
- package/dist/src/tools/types.js +7 -0
- package/dist/src/tools/types.js.map +1 -0
- package/dist/src/types.d.ts +20 -12
- package/dist/src/types.js +3 -2
- package/dist/src/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -3
- package/dist/src/agent/agent-runner.d.ts +0 -33
- package/dist/src/agent/agent-runner.js +0 -1040
- package/dist/src/agent/agent-runner.js.map +0 -1
- package/dist/src/agent/custom-tools-e2e.test.js +0 -566
- package/dist/src/agent/custom-tools-e2e.test.js.map +0 -1
- package/dist/src/agent/request-helper.d.ts +0 -16
- package/dist/src/agent/request-helper.js +0 -96
- package/dist/src/agent/request-helper.js.map +0 -1
- package/dist/src/agent/stores-e2e.test.js +0 -433
- package/dist/src/agent/stores-e2e.test.js.map +0 -1
- package/dist/src/agent/tool-context-builder.d.ts +0 -11
- package/dist/src/agent/tool-context-builder.js +0 -102
- package/dist/src/agent/tool-context-builder.js.map +0 -1
- package/dist/src/agent/tool-context-builder.test.js +0 -152
- package/dist/src/agent/tool-context-builder.test.js.map +0 -1
- package/dist/src/agent/write-repo-file.test.js +0 -270
- package/dist/src/agent/write-repo-file.test.js.map +0 -1
- package/dist/src/cron/heartbeat-runner.d.ts +0 -21
- package/dist/src/cron/heartbeat-runner.js +0 -79
- package/dist/src/cron/heartbeat-runner.js.map +0 -1
- package/dist/src/cron/heartbeat-runner.test.d.ts +0 -6
- package/dist/src/cron/heartbeat-runner.test.js +0 -120
- package/dist/src/cron/heartbeat-runner.test.js.map +0 -1
- package/dist/src/cron/heartbeat-scheduler.d.ts +0 -26
- package/dist/src/cron/heartbeat-scheduler.js +0 -55
- package/dist/src/cron/heartbeat-scheduler.js.map +0 -1
- package/dist/src/cron/heartbeat-scheduler.test.d.ts +0 -6
- package/dist/src/cron/heartbeat-scheduler.test.js +0 -61
- package/dist/src/cron/heartbeat-scheduler.test.js.map +0 -1
- package/dist/src/routes/ai-stream.test.d.ts +0 -6
- package/dist/src/routes/ai-stream.test.js +0 -586
- package/dist/src/routes/ai-stream.test.js.map +0 -1
- package/dist/src/routes/ask-user-response.d.ts +0 -30
- package/dist/src/routes/ask-user-response.js +0 -61
- package/dist/src/routes/ask-user-response.js.map +0 -1
- package/dist/src/routes/ask-user-response.test.d.ts +0 -6
- package/dist/src/routes/ask-user-response.test.js +0 -88
- package/dist/src/routes/ask-user-response.test.js.map +0 -1
- package/dist/src/routes/chat-stream.test.d.ts +0 -6
- package/dist/src/routes/chat-stream.test.js +0 -155
- package/dist/src/routes/chat-stream.test.js.map +0 -1
- package/dist/src/routes/chat.test.d.ts +0 -6
- package/dist/src/routes/chat.test.js +0 -99
- package/dist/src/routes/chat.test.js.map +0 -1
- package/dist/src/routes/widget-actions.d.ts +0 -49
- package/dist/src/routes/widget-actions.js +0 -78
- package/dist/src/routes/widget-actions.js.map +0 -1
- package/dist/src/session/custom-tool-adapter.d.ts +0 -74
- package/dist/src/session/custom-tool-adapter.js +0 -180
- package/dist/src/session/custom-tool-adapter.js.map +0 -1
- package/dist/src/session/history-converter.d.ts +0 -21
- package/dist/src/session/history-converter.js +0 -59
- package/dist/src/session/history-converter.js.map +0 -1
- package/dist/src/session/history-converter.test.d.ts +0 -6
- package/dist/src/session/history-converter.test.js +0 -130
- package/dist/src/session/history-converter.test.js.map +0 -1
- package/dist/src/session/session-manager.d.ts +0 -219
- package/dist/src/session/session-manager.js +0 -915
- package/dist/src/session/session-manager.js.map +0 -1
- package/dist/src/session/session-manager.test.d.ts +0 -6
- package/dist/src/session/session-manager.test.js +0 -451
- package/dist/src/session/session-manager.test.js.map +0 -1
- package/dist/src/session/session-runner.d.ts +0 -45
- package/dist/src/session/session-runner.js +0 -719
- package/dist/src/session/session-runner.js.map +0 -1
- package/dist/src/session/session-runner.test.d.ts +0 -6
- package/dist/src/session/session-runner.test.js +0 -830
- package/dist/src/session/session-runner.test.js.map +0 -1
- /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
|
|
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 {
|
|
11
|
-
import {
|
|
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/
|
|
34
|
-
{ provider: 'openai', envVar: 'OPENAI_API_KEY', url: 'https://api.openai.com/v1/models
|
|
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
|
-
* `
|
|
70
|
-
*
|
|
80
|
+
* `StandaloneSessionManager`, mounts all routes, and optionally watches
|
|
81
|
+
* for config changes (hot reload).
|
|
71
82
|
*/
|
|
72
83
|
export async function createLocalServer(config) {
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
+
log.warn('provider_key_invalid', { provider: f.provider, error: f.error });
|
|
87
98
|
}
|
|
88
|
-
}).catch(() => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
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 (
|
|
97
|
-
const storeConfig =
|
|
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(
|
|
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(
|
|
125
|
+
storeBackend = await mod.createPostgresStoreBackend(bundle.stores, connUrl);
|
|
113
126
|
storeBackendType = 'postgres';
|
|
114
|
-
log.info(
|
|
127
|
+
log.info('store_backend_ready', { type: 'postgres', storeCount: bundle.stores.length });
|
|
115
128
|
}
|
|
116
129
|
else {
|
|
117
|
-
log.error('
|
|
130
|
+
log.error('store_postgres_unavailable', { hint: 'install @amodalai/store-postgres' });
|
|
118
131
|
}
|
|
119
132
|
}
|
|
120
133
|
catch (err) {
|
|
121
|
-
log.error(
|
|
122
|
-
log.info('
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
154
|
+
log.warn('store_lock_conflict', { pid: existing });
|
|
139
155
|
}
|
|
140
|
-
catch { /* PID not running
|
|
156
|
+
catch { /* PID not running — stale lock, safe to overwrite */ }
|
|
141
157
|
}
|
|
142
|
-
catch { /*
|
|
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(
|
|
170
|
+
storeBackend = await createPGLiteStoreBackend(bundle.stores, dataDir);
|
|
153
171
|
storeBackendType = 'pglite';
|
|
154
|
-
log.info(
|
|
172
|
+
log.info('store_backend_ready', { type: 'pglite', storeCount: bundle.stores.length, dataDir });
|
|
155
173
|
}
|
|
156
174
|
catch (err) {
|
|
157
|
-
|
|
158
|
-
log.error(
|
|
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
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
195
|
-
|
|
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
|
|
298
|
+
// Unified config endpoint
|
|
231
299
|
app.get('/api/config', (_req, res) => {
|
|
232
|
-
const bundleData =
|
|
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 =
|
|
330
|
+
resumeSessionId = legacySessionStore.latest() ?? undefined;
|
|
266
331
|
}
|
|
267
332
|
if (resumeSessionId) {
|
|
268
|
-
log.debug(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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({
|
|
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
|
-
//
|
|
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
|
-
|
|
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(
|
|
367
|
-
|
|
368
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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('
|
|
559
|
+
log.info('server_stopped', {});
|
|
469
560
|
},
|
|
470
561
|
};
|
|
471
562
|
}
|