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