@amodalai/runtime 0.1.26 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/__fixtures__/README.md +88 -0
- package/dist/src/__fixtures__/e2e.test.js +211 -0
- package/dist/src/__fixtures__/e2e.test.js.map +1 -0
- package/dist/src/__fixtures__/smoke-agent/amodal.json +11 -0
- package/dist/src/__fixtures__/smoke-agent/automations/delivery-callback-test.json +9 -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 +1404 -0
- package/dist/src/__fixtures__/smoke.test.js.map +1 -0
- package/dist/src/__fixtures__/test-env.d.ts +27 -0
- package/dist/src/__fixtures__/test-env.js +64 -0
- package/dist/src/__fixtures__/test-env.js.map +1 -0
- package/dist/src/__fixtures__/test-helpers.d.ts +30 -0
- package/dist/src/__fixtures__/test-helpers.js +120 -0
- package/dist/src/__fixtures__/test-helpers.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 +22 -0
- package/dist/src/agent/agent-types.js.map +1 -1
- package/dist/src/agent/automation-bridge.d.ts +9 -0
- package/dist/src/agent/automation-bridge.js +26 -0
- package/dist/src/agent/automation-bridge.js.map +1 -1
- package/dist/src/agent/automation-bridge.test.js +63 -0
- package/dist/src/agent/automation-bridge.test.js.map +1 -1
- package/dist/src/agent/local-server.d.ts +1 -8
- package/dist/src/agent/local-server.js +398 -163
- package/dist/src/agent/local-server.js.map +1 -1
- package/dist/src/agent/local-server.test.js +14 -8
- package/dist/src/agent/local-server.test.js.map +1 -1
- package/dist/src/agent/loop-types.d.ts +254 -0
- package/dist/src/agent/loop-types.js +24 -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 +152 -0
- package/dist/src/agent/loop.js.map +1 -0
- package/dist/src/agent/loop.test.js +1594 -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/delivery-router.d.ts +68 -0
- package/dist/src/agent/proactive/delivery-router.js +337 -0
- package/dist/src/agent/proactive/delivery-router.js.map +1 -0
- package/dist/src/agent/{stores-e2e.test.d.ts → proactive/delivery-router.test.d.ts} +1 -1
- package/dist/src/agent/proactive/delivery-router.test.js +455 -0
- package/dist/src/agent/proactive/delivery-router.test.js.map +1 -0
- package/dist/src/agent/proactive/proactive-runner.d.ts +46 -8
- package/dist/src/agent/proactive/proactive-runner.js +67 -37
- 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 +73 -87
- package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -1
- package/dist/src/agent/routes/admin-chat-abort.test.d.ts +6 -0
- package/dist/src/agent/routes/admin-chat-abort.test.js +206 -0
- package/dist/src/agent/routes/admin-chat-abort.test.js.map +1 -0
- package/dist/src/agent/routes/admin-chat.d.ts +15 -3
- package/dist/src/agent/routes/admin-chat.js +61 -18
- package/dist/src/agent/routes/admin-chat.js.map +1 -1
- package/dist/src/agent/routes/automations.js +5 -6
- 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 -12
- package/dist/src/agent/routes/evals.js.map +1 -1
- package/dist/src/agent/routes/files.js +7 -9
- 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 +31 -17
- 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 -12
- 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 -7
- 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 +68 -53
- package/dist/src/agent/routes/task.test.js.map +1 -1
- package/dist/src/agent/routes/webhooks.js +12 -3
- package/dist/src/agent/routes/webhooks.js.map +1 -1
- package/dist/src/agent/snapshot-server.d.ts +2 -22
- package/dist/src/agent/snapshot-server.js +48 -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 +260 -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 +79 -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 +285 -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 +452 -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 +169 -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 +450 -0
- package/dist/src/agent/states/thinking.js.map +1 -0
- package/dist/src/agent/token-estimate.d.ts +31 -0
- package/dist/src/agent/token-estimate.js +34 -0
- package/dist/src/agent/token-estimate.js.map +1 -0
- package/dist/src/agent/token-estimate.test.d.ts +6 -0
- package/dist/src/agent/token-estimate.test.js +44 -0
- package/dist/src/agent/token-estimate.test.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 +134 -0
- package/dist/src/api/create-agent.js.map +1 -0
- package/dist/src/api/types.d.ts +66 -0
- package/dist/src/api/types.js +7 -0
- package/dist/src/api/types.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/env-ref.d.ts +13 -0
- package/dist/src/env-ref.js +31 -0
- package/dist/src/env-ref.js.map +1 -0
- package/dist/src/env-ref.test.d.ts +6 -0
- package/dist/src/env-ref.test.js +34 -0
- package/dist/src/env-ref.test.js.map +1 -0
- package/dist/src/errors.d.ts +15 -0
- package/dist/src/errors.js +22 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/errors.test.js +2 -2
- package/dist/src/errors.test.js.map +1 -1
- package/dist/src/events/event-bus.d.ts +54 -0
- package/dist/src/events/event-bus.js +84 -0
- package/dist/src/events/event-bus.js.map +1 -0
- package/dist/src/events/event-bus.test.d.ts +6 -0
- package/dist/src/events/event-bus.test.js +112 -0
- package/dist/src/events/event-bus.test.js.map +1 -0
- package/dist/src/events/events-route.d.ts +36 -0
- package/dist/src/events/events-route.js +80 -0
- package/dist/src/events/events-route.js.map +1 -0
- package/dist/src/events/events-route.test.d.ts +6 -0
- package/dist/src/events/events-route.test.js +134 -0
- package/dist/src/events/events-route.test.js.map +1 -0
- package/dist/src/events/store-event-wrapper.d.ts +19 -0
- package/dist/src/events/store-event-wrapper.js +57 -0
- package/dist/src/events/store-event-wrapper.js.map +1 -0
- package/dist/src/events/store-event-wrapper.test.d.ts +6 -0
- package/dist/src/events/store-event-wrapper.test.js +91 -0
- package/dist/src/events/store-event-wrapper.test.js.map +1 -0
- package/dist/src/index.d.ts +33 -6
- package/dist/src/index.js +35 -21
- package/dist/src/index.js.map +1 -1
- package/dist/src/middleware/auth.d.ts +0 -2
- package/dist/src/middleware/auth.js.map +1 -1
- 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/search-provider.d.ts +64 -0
- package/dist/src/providers/search-provider.js +174 -0
- package/dist/src/providers/search-provider.js.map +1 -0
- package/dist/src/providers/types.d.ts +118 -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 +28 -10
- package/dist/src/routes/ai-stream.js +85 -41
- 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 +20 -4
- package/dist/src/routes/chat-stream.js +49 -29
- package/dist/src/routes/chat-stream.js.map +1 -1
- package/dist/src/routes/chat.d.ts +19 -4
- package/dist/src/routes/chat.js +62 -23
- 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 +77 -0
- package/dist/src/routes/session-resolver.js +109 -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 +207 -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 -4
- 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 +18 -11
- package/dist/src/server.js +46 -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 -144
- package/dist/src/server.test.js.map +1 -1
- package/dist/src/session/drizzle-session-store.d.ts +56 -0
- package/dist/src/session/drizzle-session-store.js +203 -0
- package/dist/src/session/drizzle-session-store.js.map +1 -0
- package/dist/src/session/manager.d.ts +101 -0
- package/dist/src/session/manager.js +394 -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 +309 -0
- package/dist/src/session/manager.test.js.map +1 -0
- package/dist/src/session/pglite-session-store.d.ts +23 -0
- package/dist/src/session/pglite-session-store.js +70 -0
- package/dist/src/session/pglite-session-store.js.map +1 -0
- package/dist/src/session/postgres-session-store.d.ts +44 -0
- package/dist/src/session/postgres-session-store.js +138 -0
- package/dist/src/session/postgres-session-store.js.map +1 -0
- package/dist/src/session/session-builder.d.ts +69 -0
- package/dist/src/session/session-builder.js +384 -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 +350 -0
- package/dist/src/session/session-builder.test.js.map +1 -0
- package/dist/src/session/session-store-selector.d.ts +49 -0
- package/dist/src/session/session-store-selector.js +60 -0
- package/dist/src/session/session-store-selector.js.map +1 -0
- package/dist/src/session/session-store-selector.test.d.ts +6 -0
- package/dist/src/session/session-store-selector.test.js +79 -0
- package/dist/src/session/session-store-selector.test.js.map +1 -0
- package/dist/src/session/store.d.ts +171 -0
- package/dist/src/session/store.js +155 -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 +423 -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 +61 -0
- package/dist/src/session/tool-context-factory.js +189 -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 +284 -0
- package/dist/src/session/tool-context-factory.test.js.map +1 -0
- package/dist/src/session/types.d.ts +195 -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 -239
- 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 +457 -0
- package/dist/src/stores/schema.js +59 -0
- package/dist/src/stores/schema.js.map +1 -0
- package/dist/src/tools/admin-file-tools.d.ts +42 -0
- package/dist/src/tools/admin-file-tools.js +714 -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 +521 -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 +243 -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/fetch-url-tool.d.ts +23 -0
- package/dist/src/tools/fetch-url-tool.js +333 -0
- package/dist/src/tools/fetch-url-tool.js.map +1 -0
- package/dist/src/tools/fetch-url-tool.test.d.ts +6 -0
- package/dist/src/tools/fetch-url-tool.test.js +228 -0
- package/dist/src/tools/fetch-url-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 +226 -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 +120 -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 +253 -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 +215 -0
- package/dist/src/tools/store-tools.test.js.map +1 -0
- package/dist/src/tools/types.d.ts +129 -0
- package/dist/src/tools/types.js +7 -0
- package/dist/src/tools/types.js.map +1 -0
- package/dist/src/tools/web-search-tool.d.ts +31 -0
- package/dist/src/tools/web-search-tool.js +170 -0
- package/dist/src/tools/web-search-tool.js.map +1 -0
- package/dist/src/tools/web-search-tool.test.d.ts +6 -0
- package/dist/src/tools/web-search-tool.test.js +153 -0
- package/dist/src/tools/web-search-tool.test.js.map +1 -0
- package/dist/src/tools/web-tools-shared.d.ts +21 -0
- package/dist/src/tools/web-tools-shared.js +32 -0
- package/dist/src/tools/web-tools-shared.js.map +1 -0
- package/dist/src/types.d.ts +40 -12
- package/dist/src/types.js +16 -2
- package/dist/src/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -4
- package/dist/src/__tests__/sse-contract.test.js +0 -464
- package/dist/src/__tests__/sse-contract.test.js.map +0 -1
- package/dist/src/__tests__/tools.test.js +0 -583
- package/dist/src/__tests__/tools.test.js.map +0 -1
- 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.d.ts +0 -6
- 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/session-store.d.ts +0 -62
- package/dist/src/agent/session-store.js +0 -151
- package/dist/src/agent/session-store.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.d.ts +0 -6
- 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/admin-file-tools.d.ts +0 -136
- package/dist/src/session/admin-file-tools.js +0 -240
- package/dist/src/session/admin-file-tools.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 -455
- 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 -834
- package/dist/src/session/session-runner.test.js.map +0 -1
- /package/dist/src/{__tests__/sse-contract.test.d.ts → __fixtures__/e2e.test.d.ts} +0 -0
- /package/dist/src/{__tests__/tools.test.d.ts → __fixtures__/smoke.test.d.ts} +0 -0
- /package/dist/src/agent/{write-repo-file.test.d.ts → loop.test.d.ts} +0 -0
|
@@ -1,17 +1,35 @@
|
|
|
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 { selectSessionStore } from '../session/session-store-selector.js';
|
|
22
|
+
import { resolveEnvRef } from '../env-ref.js';
|
|
23
|
+
import { buildSessionComponents } from '../session/session-builder.js';
|
|
24
|
+
import { LocalToolExecutor } from './tool-executor-local.js';
|
|
25
|
+
import { buildMcpConfigs } from './mcp-config.js';
|
|
12
26
|
import { ConfigWatcher } from './config-watcher.js';
|
|
27
|
+
import { RuntimeEventBus } from '../events/event-bus.js';
|
|
28
|
+
import { createEventsRouter } from '../events/events-route.js';
|
|
29
|
+
import { wrapStoreBackendWithEvents } from '../events/store-event-wrapper.js';
|
|
13
30
|
import { ProactiveRunner } from './proactive/proactive-runner.js';
|
|
14
31
|
import { createChatStreamRouter } from '../routes/chat-stream.js';
|
|
32
|
+
import { createChatRouter } from '../routes/chat.js';
|
|
15
33
|
import { createAdminChatRouter } from './routes/admin-chat.js';
|
|
16
34
|
import { createTaskRouter } from './routes/task.js';
|
|
17
35
|
import { createInspectRouter } from './routes/inspect.js';
|
|
@@ -23,20 +41,21 @@ import { createStoresRouter } from './routes/stores.js';
|
|
|
23
41
|
import { createFilesRouter } from './routes/files.js';
|
|
24
42
|
import { createEvalRouter } from './routes/evals.js';
|
|
25
43
|
import { errorHandler } from '../middleware/error-handler.js';
|
|
44
|
+
import { asyncHandler } from '../routes/route-helpers.js';
|
|
26
45
|
import { createPGLiteStoreBackend } from '../stores/pglite-store-backend.js';
|
|
27
|
-
import { SessionStore } from './session-store.js';
|
|
28
46
|
import { EvalStore } from './eval-store.js';
|
|
29
47
|
import { buildPages } from './page-builder.js';
|
|
30
48
|
import { LOCAL_APP_ID } from '../constants.js';
|
|
31
|
-
import { log } from '../logger.js';
|
|
49
|
+
import { log, createLogger } from '../logger.js';
|
|
50
|
+
// Each check must use an endpoint that returns 200 on a valid key and
|
|
51
|
+
// a distinct auth-failure status (typically 401) on a bad key. Do NOT
|
|
52
|
+
// use endpoints with method guards that might return 405 before the
|
|
53
|
+
// auth check — `GET /v1/messages` on Anthropic does exactly that, and
|
|
54
|
+
// makes every key (good or bad) look invalid because Anthropic returns
|
|
55
|
+
// 405 for wrong-method regardless of whether the x-api-key is real.
|
|
32
56
|
const PROVIDER_CHECKS = [
|
|
33
|
-
{ provider: 'anthropic', envVar: 'ANTHROPIC_API_KEY', url: 'https://api.anthropic.com/v1/models', authHeader: (
|
|
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}` }) },
|
|
57
|
+
{ provider: 'anthropic', envVar: 'ANTHROPIC_API_KEY', url: 'https://api.anthropic.com/v1/models', authHeader: (key) => ({ 'x-api-key': key, 'anthropic-version': '2023-06-01' }) },
|
|
58
|
+
{ provider: 'openai', envVar: 'OPENAI_API_KEY', url: 'https://api.openai.com/v1/models', authHeader: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
40
59
|
];
|
|
41
60
|
async function checkProviders() {
|
|
42
61
|
const results = await Promise.allSettled(PROVIDER_CHECKS.map(async (check) => {
|
|
@@ -45,7 +64,6 @@ async function checkProviders() {
|
|
|
45
64
|
return { provider: check.provider, envVar: check.envVar, keySet: false, verified: false };
|
|
46
65
|
}
|
|
47
66
|
try {
|
|
48
|
-
// globalThis.fetch is available in Node 18+
|
|
49
67
|
const res = await globalThis.fetch(check.url, {
|
|
50
68
|
method: 'GET',
|
|
51
69
|
headers: check.authHeader(key),
|
|
@@ -62,46 +80,75 @@ async function checkProviders() {
|
|
|
62
80
|
}));
|
|
63
81
|
return results.map((r) => r.status === 'fulfilled' ? r.value : { provider: 'unknown', envVar: '', keySet: false, verified: false });
|
|
64
82
|
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Local server
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
65
86
|
/**
|
|
66
87
|
* Creates an Express server for repo-based agent mode.
|
|
67
88
|
*
|
|
68
89
|
* Loads the `.amodal/` config from `config.repoPath`, creates a
|
|
69
|
-
* `
|
|
70
|
-
*
|
|
90
|
+
* `StandaloneSessionManager`, mounts all routes, and optionally watches
|
|
91
|
+
* for config changes (hot reload).
|
|
71
92
|
*/
|
|
93
|
+
/**
|
|
94
|
+
* Install a process-level unhandledRejection listener that logs instead
|
|
95
|
+
* of crashing. An escaped rejection is always a bug — we want loud logs,
|
|
96
|
+
* not silent outages. The previous behavior (default Node: print + crash)
|
|
97
|
+
* turned small bugs (one leaked promise) into whole-server downtime for
|
|
98
|
+
* every active session. Logging + continuing preserves service for all
|
|
99
|
+
* other sessions while still surfacing the issue to operators.
|
|
100
|
+
*
|
|
101
|
+
* Idempotent: only installs once per process (the local-server can be
|
|
102
|
+
* created and torn down repeatedly during tests).
|
|
103
|
+
*/
|
|
104
|
+
let unhandledRejectionListenerInstalled = false;
|
|
105
|
+
function installUnhandledRejectionLogger() {
|
|
106
|
+
if (unhandledRejectionListenerInstalled)
|
|
107
|
+
return;
|
|
108
|
+
unhandledRejectionListenerInstalled = true;
|
|
109
|
+
process.on('unhandledRejection', (reason) => {
|
|
110
|
+
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
111
|
+
log.error('unhandled_rejection', {
|
|
112
|
+
name: err.name,
|
|
113
|
+
message: err.message,
|
|
114
|
+
stack: err.stack,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
72
118
|
export async function createLocalServer(config) {
|
|
73
|
-
|
|
119
|
+
installUnhandledRejectionLogger();
|
|
120
|
+
let bundle = await loadRepo({ localPath: config.repoPath });
|
|
74
121
|
// Check provider API keys in the background at startup
|
|
75
122
|
let providerStatuses = PROVIDER_CHECKS.map((c) => ({
|
|
76
123
|
provider: c.provider, envVar: c.envVar, keySet: !!process.env[c.envVar], verified: false,
|
|
77
124
|
}));
|
|
78
|
-
checkProviders().then((results) => {
|
|
125
|
+
void checkProviders().then((results) => {
|
|
79
126
|
providerStatuses = results;
|
|
80
127
|
const verified = results.filter((r) => r.verified).map((r) => r.provider);
|
|
81
128
|
if (verified.length > 0) {
|
|
82
|
-
|
|
129
|
+
log.info('provider_keys_verified', { providers: verified });
|
|
83
130
|
}
|
|
84
131
|
const failed = results.filter((r) => r.keySet && !r.verified);
|
|
85
132
|
for (const f of failed) {
|
|
86
|
-
|
|
133
|
+
log.warn('provider_key_invalid', { provider: f.provider, error: f.error });
|
|
87
134
|
}
|
|
88
|
-
}).catch(() => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
135
|
+
}).catch((err) => {
|
|
136
|
+
log.error('provider_check_failed', { error: err instanceof Error ? err.message : String(err) });
|
|
137
|
+
});
|
|
138
|
+
// Create custom tool executor
|
|
139
|
+
const toolExecutor = bundle.tools.length > 0 ? new LocalToolExecutor() : undefined;
|
|
140
|
+
// -------------------------------------------------------------------------
|
|
141
|
+
// Store backend
|
|
142
|
+
// -------------------------------------------------------------------------
|
|
94
143
|
let storeBackend;
|
|
95
144
|
let storeBackendType = 'none';
|
|
96
|
-
if (
|
|
97
|
-
const storeConfig =
|
|
145
|
+
if (bundle.stores.length > 0) {
|
|
146
|
+
const storeConfig = bundle.config.stores;
|
|
98
147
|
const backend = storeConfig?.backend ?? 'pglite';
|
|
99
148
|
if (backend === 'postgres' && storeConfig?.postgresUrl) {
|
|
100
|
-
const connUrl = storeConfig.postgresUrl
|
|
101
|
-
? process.env[storeConfig.postgresUrl.slice(4)] ?? ''
|
|
102
|
-
: storeConfig.postgresUrl;
|
|
149
|
+
const connUrl = resolveEnvRef(storeConfig.postgresUrl) ?? '';
|
|
103
150
|
if (!connUrl) {
|
|
104
|
-
log.error(
|
|
151
|
+
log.error('store_postgres_url_missing', { configured: storeConfig.postgresUrl });
|
|
105
152
|
}
|
|
106
153
|
else {
|
|
107
154
|
try {
|
|
@@ -109,95 +156,176 @@ export async function createLocalServer(config) {
|
|
|
109
156
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dynamic import for optional postgres backend
|
|
110
157
|
const mod = await import(pgModPath).catch(() => null);
|
|
111
158
|
if (mod?.createPostgresStoreBackend) {
|
|
112
|
-
storeBackend = await mod.createPostgresStoreBackend(
|
|
159
|
+
storeBackend = await mod.createPostgresStoreBackend(bundle.stores, connUrl);
|
|
113
160
|
storeBackendType = 'postgres';
|
|
114
|
-
log.info(
|
|
161
|
+
log.info('store_backend_ready', { type: 'postgres', storeCount: bundle.stores.length });
|
|
115
162
|
}
|
|
116
163
|
else {
|
|
117
|
-
log.error('
|
|
164
|
+
log.error('store_postgres_unavailable', { hint: 'install @amodalai/store-postgres' });
|
|
118
165
|
}
|
|
119
166
|
}
|
|
120
167
|
catch (err) {
|
|
121
|
-
log.error(
|
|
122
|
-
log.info('
|
|
168
|
+
log.error('store_postgres_failed', { error: err instanceof Error ? err.message : String(err) });
|
|
169
|
+
log.info('store_fallback_pglite', {});
|
|
123
170
|
}
|
|
124
171
|
}
|
|
125
172
|
}
|
|
126
173
|
// Default: PGLite
|
|
127
174
|
if (!storeBackend) {
|
|
128
175
|
const dataDir = storeConfig?.dataDir ?? `${config.repoPath}/.amodal/store-data`;
|
|
129
|
-
// Check for lock file — another instance may be using this data dir
|
|
130
|
-
|
|
176
|
+
// Check for lock file — another instance may be using this data dir.
|
|
177
|
+
// Lock file lives in the PARENT dir (not inside dataDir) so it doesn't
|
|
178
|
+
// conflict with PGLite's own PostgreSQL data files (e.g. postmaster.pid).
|
|
179
|
+
const lockPath = `${dataDir}.lock`;
|
|
131
180
|
try {
|
|
132
181
|
const { readFileSync, writeFileSync, mkdirSync, unlinkSync } = await import('node:fs');
|
|
133
|
-
|
|
182
|
+
const path = await import('node:path');
|
|
183
|
+
mkdirSync(path.dirname(dataDir), { recursive: true });
|
|
134
184
|
try {
|
|
135
185
|
const existing = readFileSync(lockPath, 'utf-8').trim();
|
|
136
186
|
try {
|
|
137
187
|
process.kill(Number(existing), 0);
|
|
138
|
-
log.warn(
|
|
188
|
+
log.warn('store_lock_conflict', { pid: existing });
|
|
139
189
|
}
|
|
140
|
-
catch { /* PID not running
|
|
190
|
+
catch { /* PID not running — stale lock, safe to overwrite */ }
|
|
141
191
|
}
|
|
142
|
-
catch { /*
|
|
192
|
+
catch { /* No lock file exists — first run */ }
|
|
143
193
|
writeFileSync(lockPath, String(process.pid));
|
|
144
194
|
const lockCleanup = lockPath;
|
|
145
195
|
process.on('exit', () => { try {
|
|
146
196
|
unlinkSync(lockCleanup);
|
|
147
197
|
}
|
|
148
|
-
catch { /* */ } });
|
|
198
|
+
catch { /* exit handler — can't log */ } });
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
log.warn('store_lock_setup_failed', { dataDir, error: err instanceof Error ? err.message : String(err) });
|
|
149
202
|
}
|
|
150
|
-
catch { /* lock file handling failed, proceed anyway */ }
|
|
151
203
|
try {
|
|
152
|
-
storeBackend = await createPGLiteStoreBackend(
|
|
204
|
+
storeBackend = await createPGLiteStoreBackend(bundle.stores, dataDir);
|
|
153
205
|
storeBackendType = 'pglite';
|
|
154
|
-
log.info(
|
|
206
|
+
log.info('store_backend_ready', { type: 'pglite', storeCount: bundle.stores.length, dataDir });
|
|
155
207
|
}
|
|
156
208
|
catch (err) {
|
|
157
|
-
|
|
158
|
-
log.error(
|
|
209
|
+
const errMsg = err instanceof Error ? err.message : (typeof err === 'object' ? JSON.stringify(err) : String(err));
|
|
210
|
+
log.error('store_backend_init_failed', { error: errMsg, dataDir });
|
|
159
211
|
}
|
|
160
212
|
}
|
|
161
213
|
}
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
214
|
+
// -------------------------------------------------------------------------
|
|
215
|
+
// Runtime event bus (powers /api/events SSE for live UI updates)
|
|
216
|
+
// -------------------------------------------------------------------------
|
|
217
|
+
const eventBus = new RuntimeEventBus({
|
|
218
|
+
onListenerError: (err, event) => {
|
|
219
|
+
log.warn('event_bus_listener_error', {
|
|
220
|
+
seq: event.seq,
|
|
221
|
+
type: event.type,
|
|
222
|
+
error: err instanceof Error ? err.message : String(err),
|
|
223
|
+
});
|
|
172
224
|
},
|
|
225
|
+
});
|
|
226
|
+
// Wrap the store backend so every write emits store_updated events.
|
|
227
|
+
// Covers every write path through one seam: tools, REST routes, admin
|
|
228
|
+
// file tools, task execution — they all go through this backend.
|
|
229
|
+
if (storeBackend) {
|
|
230
|
+
storeBackend = wrapStoreBackendWithEvents(storeBackend, eventBus);
|
|
231
|
+
}
|
|
232
|
+
// -------------------------------------------------------------------------
|
|
233
|
+
// Session manager (new standalone stack)
|
|
234
|
+
// -------------------------------------------------------------------------
|
|
235
|
+
const sessionLogger = createLogger({ component: 'session-manager' });
|
|
236
|
+
const sessionDataDir = `${config.repoPath}/.amodal/session-data`;
|
|
237
|
+
const sessionStore = await selectSessionStore({
|
|
238
|
+
backend: bundle.config.stores?.backend,
|
|
239
|
+
postgresUrl: resolveEnvRef(bundle.config.stores?.postgresUrl),
|
|
240
|
+
logger: sessionLogger,
|
|
241
|
+
dataDir: sessionDataDir,
|
|
242
|
+
});
|
|
243
|
+
const sessionManager = new StandaloneSessionManager({
|
|
244
|
+
logger: sessionLogger,
|
|
245
|
+
store: sessionStore,
|
|
173
246
|
ttlMs: config.sessionTtlMs,
|
|
174
|
-
|
|
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
|
-
},
|
|
247
|
+
eventBus,
|
|
191
248
|
});
|
|
192
|
-
|
|
249
|
+
sessionManager.start();
|
|
250
|
+
// -------------------------------------------------------------------------
|
|
251
|
+
// MCP connections (shared across sessions)
|
|
252
|
+
// -------------------------------------------------------------------------
|
|
253
|
+
let mcpManager = null;
|
|
254
|
+
{
|
|
255
|
+
const { McpManager } = await import('@amodalai/core');
|
|
256
|
+
const mcpConfigs = buildMcpConfigs(bundle);
|
|
257
|
+
if (Object.keys(mcpConfigs).length > 0) {
|
|
258
|
+
const manager = new McpManager();
|
|
259
|
+
try {
|
|
260
|
+
await manager.startServers(mcpConfigs);
|
|
261
|
+
if (manager.connectedCount > 0) {
|
|
262
|
+
mcpManager = manager;
|
|
263
|
+
const tools = manager.getDiscoveredTools();
|
|
264
|
+
log.info('mcp_initialized', { servers: manager.connectedCount, tools: tools.length });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
log.error('mcp_init_failed', { error: err instanceof Error ? err.message : String(err) });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// -------------------------------------------------------------------------
|
|
273
|
+
// Shared resources for route handlers
|
|
274
|
+
// -------------------------------------------------------------------------
|
|
275
|
+
const shared = {
|
|
276
|
+
storeBackend: storeBackend ?? null,
|
|
277
|
+
mcpManager,
|
|
278
|
+
logger: log,
|
|
279
|
+
toolExecutor,
|
|
280
|
+
};
|
|
281
|
+
// Helper: get current bundle (updated by config watcher)
|
|
282
|
+
const getBundle = () => bundle;
|
|
283
|
+
// Helper: create automation session components
|
|
284
|
+
const createAutomationSessionComponents = () => {
|
|
285
|
+
const components = buildSessionComponents({
|
|
286
|
+
bundle,
|
|
287
|
+
storeBackend: storeBackend ?? null,
|
|
288
|
+
mcpManager,
|
|
289
|
+
logger: log,
|
|
290
|
+
toolExecutor,
|
|
291
|
+
sessionType: 'automation',
|
|
292
|
+
});
|
|
293
|
+
const session = sessionManager.create({
|
|
294
|
+
provider: components.provider,
|
|
295
|
+
toolRegistry: components.toolRegistry,
|
|
296
|
+
permissionChecker: components.permissionChecker,
|
|
297
|
+
systemPrompt: components.systemPrompt,
|
|
298
|
+
userRoles: components.userRoles,
|
|
299
|
+
toolContextFactory: components.toolContextFactory,
|
|
300
|
+
appId: LOCAL_APP_ID,
|
|
301
|
+
});
|
|
302
|
+
return { session, toolContextFactory: components.toolContextFactory };
|
|
303
|
+
};
|
|
304
|
+
// -------------------------------------------------------------------------
|
|
305
|
+
// Proactive runner
|
|
306
|
+
// -------------------------------------------------------------------------
|
|
307
|
+
const runner = new ProactiveRunner(bundle, {
|
|
308
|
+
sessionManager,
|
|
309
|
+
createSessionComponents: createAutomationSessionComponents,
|
|
310
|
+
logger: log,
|
|
193
311
|
webhookSecret: config.webhookSecret,
|
|
194
|
-
|
|
195
|
-
destroySession: async (id) => sessionManager.destroy(id),
|
|
312
|
+
summarizeToolResult: config.summarizeToolResult,
|
|
196
313
|
onSessionComplete: (session, automationName) => {
|
|
197
|
-
|
|
314
|
+
// Tag the automation name onto metadata so the UI can filter
|
|
315
|
+
// sessions by automation via /sessions?automation=<name>.
|
|
316
|
+
session.metadata.automationName = automationName;
|
|
317
|
+
void sessionManager.persist(session);
|
|
198
318
|
},
|
|
319
|
+
eventBus,
|
|
320
|
+
onAutomationResult: config.onAutomationResult,
|
|
199
321
|
});
|
|
322
|
+
// -------------------------------------------------------------------------
|
|
323
|
+
// Config watcher (hot reload)
|
|
324
|
+
// -------------------------------------------------------------------------
|
|
200
325
|
let watcher = null;
|
|
326
|
+
// -------------------------------------------------------------------------
|
|
327
|
+
// Express app
|
|
328
|
+
// -------------------------------------------------------------------------
|
|
201
329
|
const app = express();
|
|
202
330
|
// CORS
|
|
203
331
|
const corsOrigin = config.corsOrigin ?? '*';
|
|
@@ -227,9 +355,9 @@ export async function createLocalServer(config) {
|
|
|
227
355
|
app.post('/auth/token', (_req, res) => {
|
|
228
356
|
res.json({ token: '', expires_at: null });
|
|
229
357
|
});
|
|
230
|
-
// Unified config endpoint
|
|
358
|
+
// Unified config endpoint
|
|
231
359
|
app.get('/api/config', (_req, res) => {
|
|
232
|
-
const bundleData =
|
|
360
|
+
const bundleData = getBundle();
|
|
233
361
|
const cfg = bundleData.config;
|
|
234
362
|
// Collect all env:* references from connection specs
|
|
235
363
|
const envRefs = [];
|
|
@@ -241,11 +369,8 @@ export async function createLocalServer(config) {
|
|
|
241
369
|
}
|
|
242
370
|
}
|
|
243
371
|
res.json({
|
|
244
|
-
// Common fields (used by useHostedConfig)
|
|
245
372
|
appId: LOCAL_APP_ID,
|
|
246
373
|
appName: cfg?.name ?? '',
|
|
247
|
-
// No authMode — signals to the SPA that no auth is needed
|
|
248
|
-
// Local dev fields (used by config pages)
|
|
249
374
|
name: cfg?.name ?? '',
|
|
250
375
|
version: cfg?.version ?? '',
|
|
251
376
|
description: cfg?.description ?? '',
|
|
@@ -262,57 +387,61 @@ export async function createLocalServer(config) {
|
|
|
262
387
|
// Resolve resume session ID
|
|
263
388
|
let resumeSessionId = config.resumeSessionId;
|
|
264
389
|
if (resumeSessionId === 'latest') {
|
|
265
|
-
|
|
390
|
+
const { sessions: recent } = await sessionStore.list({
|
|
391
|
+
limit: 1,
|
|
392
|
+
filter: { appId: LOCAL_APP_ID },
|
|
393
|
+
});
|
|
394
|
+
resumeSessionId = recent[0]?.id;
|
|
266
395
|
}
|
|
267
396
|
if (resumeSessionId) {
|
|
268
|
-
log.debug(
|
|
397
|
+
log.debug('resume_session', { sessionId: resumeSessionId });
|
|
269
398
|
}
|
|
270
399
|
// Client config — tells the web UI which session to resume
|
|
271
400
|
app.get('/config', (_req, res) => {
|
|
272
401
|
res.json({ resumeSessionId: resumeSessionId ?? null });
|
|
273
402
|
});
|
|
274
|
-
// Sessions endpoints
|
|
275
|
-
|
|
403
|
+
// Sessions endpoints — served directly from the DrizzleSessionStore.
|
|
404
|
+
//
|
|
405
|
+
// Dev-UI consumers (sidebar Recent list, Sessions page, Automation detail
|
|
406
|
+
// page) don't paginate — they render what they get and slice the top N.
|
|
407
|
+
// A 500-session ceiling keeps the response bounded without forcing a
|
|
408
|
+
// cursor API on the client today. If dev sessions regularly exceed this,
|
|
409
|
+
// the store already supports cursor pagination via SessionListOptions.
|
|
410
|
+
const SESSION_LIST_LIMIT = 500;
|
|
411
|
+
app.get('/sessions', asyncHandler(async (req, res) => {
|
|
276
412
|
const automationFilter = typeof req.query?.['automation'] === 'string' ? String(req.query['automation']) : undefined;
|
|
277
|
-
|
|
278
|
-
//
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
413
|
+
// Automation filter uses metadata.automationName; otherwise restrict
|
|
414
|
+
// to chat sessions by metadata.appId (excludes eval-runner / admin).
|
|
415
|
+
const filter = automationFilter
|
|
416
|
+
? { automationName: automationFilter }
|
|
417
|
+
: { appId: LOCAL_APP_ID };
|
|
418
|
+
const { sessions: rows } = await sessionStore.list({ limit: SESSION_LIST_LIMIT, filter });
|
|
419
|
+
const sessions = rows.map((s) => {
|
|
420
|
+
const title = typeof s.metadata['title'] === 'string' ? s.metadata['title'] : undefined;
|
|
421
|
+
const appId = typeof s.metadata['appId'] === 'string' ? s.metadata['appId'] : LOCAL_APP_ID;
|
|
422
|
+
const automationName = typeof s.metadata['automationName'] === 'string' ? s.metadata['automationName'] : undefined;
|
|
423
|
+
return {
|
|
424
|
+
id: s.id,
|
|
425
|
+
appId,
|
|
426
|
+
title,
|
|
427
|
+
summary: title ?? extractFirstUserText(s.messages) ?? 'Untitled',
|
|
428
|
+
createdAt: s.createdAt.getTime(),
|
|
429
|
+
lastAccessedAt: s.updatedAt.getTime(),
|
|
430
|
+
automationName,
|
|
431
|
+
};
|
|
432
|
+
});
|
|
433
|
+
res.json({ sessions });
|
|
434
|
+
}));
|
|
435
|
+
app.get('/session/:id', asyncHandler(async (req, res) => {
|
|
436
|
+
const persisted = await sessionStore.load(req.params['id'] ?? '');
|
|
285
437
|
if (!persisted) {
|
|
286
438
|
res.status(404).json({ error: 'Session not found' });
|
|
287
439
|
return;
|
|
288
440
|
}
|
|
289
|
-
|
|
290
|
-
// Supports both SessionMessage format ({type, text}) and legacy LLMMessage ({role, content}).
|
|
291
|
-
const messages = persisted.conversationHistory.map((msg) => {
|
|
292
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Persisted message
|
|
293
|
-
const m = msg;
|
|
294
|
-
// SessionMessage format (new): {type: 'user'|'assistant_text'|'error', text, toolCalls?, ...}
|
|
295
|
-
if (m['type'] === 'user') {
|
|
296
|
-
return { role: 'user', text: String(m['text'] ?? '') };
|
|
297
|
-
}
|
|
298
|
-
if (m['type'] === 'assistant_text') {
|
|
299
|
-
return { role: 'assistant', text: String(m['text'] ?? ''), toolCalls: m['toolCalls'] };
|
|
300
|
-
}
|
|
301
|
-
// Legacy LLMMessage format: {role: 'user'|'assistant', content: string|Block[]}
|
|
302
|
-
if (m['role'] === 'user') {
|
|
303
|
-
return { role: 'user', text: typeof m['content'] === 'string' ? m['content'] : '' };
|
|
304
|
-
}
|
|
305
|
-
if (m['role'] === 'assistant') {
|
|
306
|
-
const blocks = Array.isArray(m['content']) ? m['content'] : [];
|
|
307
|
-
const isTextBlock = (b) => typeof b === 'object' && b !== null && 'type' in b && b['type'] === 'text' && 'text' in b;
|
|
308
|
-
const text = blocks.filter(isTextBlock).map((b) => b.text).join('');
|
|
309
|
-
return { role: 'assistant', text };
|
|
310
|
-
}
|
|
311
|
-
return { role: String(m['role'] ?? m['type'] ?? 'unknown'), text: String(m['text'] ?? '') };
|
|
312
|
-
});
|
|
441
|
+
const messages = persisted.messages.map(flattenModelMessage).filter((m) => m !== null);
|
|
313
442
|
res.json({ session_id: persisted.id, messages });
|
|
314
|
-
});
|
|
315
|
-
app.patch('/session/:id', express.json(), (req, res) => {
|
|
443
|
+
}));
|
|
444
|
+
app.patch('/session/:id', express.json(), asyncHandler(async (req, res) => {
|
|
316
445
|
const sessionId = req.params['id'] ?? '';
|
|
317
446
|
const body = req.body;
|
|
318
447
|
if (!body || typeof body !== 'object' || !('title' in body) || typeof body['title'] !== 'string') {
|
|
@@ -321,56 +450,85 @@ export async function createLocalServer(config) {
|
|
|
321
450
|
}
|
|
322
451
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- validated above
|
|
323
452
|
const title = body['title'];
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
453
|
+
// Live session: mutate metadata on the shared object and persist so
|
|
454
|
+
// the next /sessions read reflects the new title. A concurrent
|
|
455
|
+
// runMessage may be mid-turn, but JSON.stringify runs atomically in
|
|
456
|
+
// JS's single-threaded event loop — no torn writes. The next
|
|
457
|
+
// end-of-turn persist will overwrite with the completed messages
|
|
458
|
+
// array; metadata.title stays because it's on the live session.
|
|
459
|
+
//
|
|
460
|
+
// Not-live: load → mutate → save. No race possible.
|
|
461
|
+
const live = sessionManager.get(sessionId);
|
|
462
|
+
if (live) {
|
|
463
|
+
live.metadata.title = title;
|
|
464
|
+
await sessionManager.persist(live);
|
|
328
465
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
466
|
+
else {
|
|
467
|
+
const persisted = await sessionStore.load(sessionId);
|
|
468
|
+
if (!persisted) {
|
|
469
|
+
res.status(404).json({ error: 'Session not found' });
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
persisted.metadata.title = title;
|
|
473
|
+
persisted.updatedAt = new Date();
|
|
474
|
+
await sessionStore.save(persisted);
|
|
333
475
|
}
|
|
476
|
+
// Emit session_updated so the sidebar picks up the new title live.
|
|
477
|
+
eventBus.emit({ type: 'session_updated', sessionId, appId: LOCAL_APP_ID, title });
|
|
334
478
|
res.json({ ok: true });
|
|
335
|
-
});
|
|
336
|
-
app.delete('/session/:id', (req, res) => {
|
|
479
|
+
}));
|
|
480
|
+
app.delete('/session/:id', asyncHandler(async (req, res) => {
|
|
337
481
|
const sessionId = req.params['id'] ?? '';
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const deleted = sessionStore.delete(sessionId);
|
|
482
|
+
await sessionManager.destroy(sessionId);
|
|
483
|
+
const deleted = await sessionStore.delete(sessionId);
|
|
341
484
|
if (!deleted) {
|
|
342
485
|
res.status(404).json({ error: 'Session not found' });
|
|
343
486
|
return;
|
|
344
487
|
}
|
|
488
|
+
eventBus.emit({ type: 'session_deleted', sessionId });
|
|
345
489
|
res.json({ ok: true });
|
|
346
|
-
});
|
|
490
|
+
}));
|
|
347
491
|
// File browser/editor
|
|
348
492
|
app.use(createFilesRouter({ repoPath: config.repoPath }));
|
|
493
|
+
// Event bus SSE stream (live UI updates)
|
|
494
|
+
app.use(createEventsRouter({ bus: eventBus, logger: log }));
|
|
349
495
|
// Evals
|
|
350
496
|
const evalStore = new EvalStore(config.repoPath);
|
|
351
|
-
app.use(createEvalRouter({
|
|
497
|
+
app.use(createEvalRouter({ getBundle, evalStore, repoPath: config.repoPath, getPort: () => config.port }));
|
|
352
498
|
// Feedback
|
|
353
499
|
const feedbackStore = new FeedbackStore(config.repoPath);
|
|
354
500
|
app.use(createFeedbackRouter({ feedbackStore }));
|
|
355
|
-
//
|
|
501
|
+
// Chat routes (new stack) — persistence is handled inside runMessage /
|
|
502
|
+
// route-helpers, so no explicit hooks are needed here.
|
|
356
503
|
app.use(createChatStreamRouter({
|
|
357
504
|
sessionManager,
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
505
|
+
bundleResolver: { staticBundle: bundle },
|
|
506
|
+
shared,
|
|
507
|
+
summarizeToolResult: config.summarizeToolResult,
|
|
508
|
+
}));
|
|
509
|
+
app.use(createChatRouter({
|
|
510
|
+
sessionManager,
|
|
511
|
+
bundleResolver: { staticBundle: bundle },
|
|
512
|
+
shared,
|
|
513
|
+
summarizeToolResult: config.summarizeToolResult,
|
|
365
514
|
}));
|
|
366
|
-
|
|
367
|
-
app.use(
|
|
368
|
-
|
|
515
|
+
// Task runner
|
|
516
|
+
app.use(createTaskRouter({ sessionManager, createTaskSession: createAutomationSessionComponents }));
|
|
517
|
+
// Admin chat (new stack)
|
|
518
|
+
app.use(createAdminChatRouter({
|
|
519
|
+
sessionManager,
|
|
520
|
+
shared,
|
|
521
|
+
getBundle,
|
|
522
|
+
getPort: () => config.port,
|
|
523
|
+
}));
|
|
524
|
+
// Inspect
|
|
525
|
+
app.use(createInspectRouter({ getBundle, repoPath: config.repoPath }));
|
|
526
|
+
// Automations
|
|
369
527
|
app.use(createAutomationRouter({ runner }));
|
|
370
528
|
app.use(createWebhookRouter({ runner, webhookSecret: config.webhookSecret }));
|
|
371
529
|
// Store REST API (if stores are defined)
|
|
372
530
|
if (storeBackend) {
|
|
373
|
-
app.use(createStoresRouter({ repo, storeBackend, appId: LOCAL_APP_ID }));
|
|
531
|
+
app.use(createStoresRouter({ repo: bundle, storeBackend, appId: LOCAL_APP_ID }));
|
|
374
532
|
}
|
|
375
533
|
// Build user pages (if pages/ directory exists)
|
|
376
534
|
let builtPages = [];
|
|
@@ -378,14 +536,12 @@ export async function createLocalServer(config) {
|
|
|
378
536
|
const result = await buildPages(config.repoPath);
|
|
379
537
|
builtPages = result.pages;
|
|
380
538
|
if (builtPages.length > 0) {
|
|
381
|
-
log.info(
|
|
382
|
-
// Serve compiled page bundles
|
|
539
|
+
log.info('pages_built', { count: builtPages.length });
|
|
383
540
|
app.use('/pages-bundle', express.static(result.outDir));
|
|
384
541
|
}
|
|
385
542
|
}
|
|
386
543
|
catch (err) {
|
|
387
|
-
|
|
388
|
-
log.error(`Page build failed: ${msg}`, 'dev');
|
|
544
|
+
log.error('pages_build_failed', { error: err instanceof Error ? err.message : String(err) });
|
|
389
545
|
}
|
|
390
546
|
// Pages list endpoint
|
|
391
547
|
app.get('/api/pages', (_req, res) => {
|
|
@@ -398,11 +554,8 @@ export async function createLocalServer(config) {
|
|
|
398
554
|
app.use(config.appMiddleware);
|
|
399
555
|
}
|
|
400
556
|
else if (config.staticAppDir && existsSync(config.staticAppDir)) {
|
|
401
|
-
// Serve pre-built SPA static assets with index.html fallback
|
|
402
557
|
app.use(express.static(config.staticAppDir));
|
|
403
|
-
// SPA fallback — serve index.html for any non-API, non-static route
|
|
404
558
|
app.use((_req, res, next) => {
|
|
405
|
-
// Don't intercept API or inspect routes (already handled above)
|
|
406
559
|
if (_req.path.startsWith('/api/') || _req.path.startsWith('/inspect/') || _req.method !== 'GET') {
|
|
407
560
|
next();
|
|
408
561
|
return;
|
|
@@ -427,17 +580,18 @@ export async function createLocalServer(config) {
|
|
|
427
580
|
// Start hot reload watcher
|
|
428
581
|
if (config.hotReload) {
|
|
429
582
|
watcher = new ConfigWatcher(config.repoPath, (newBundle) => {
|
|
430
|
-
|
|
583
|
+
bundle = newBundle;
|
|
584
|
+
// Shared resources and session components will pick up the new
|
|
585
|
+
// bundle on next session creation via getBundle().
|
|
586
|
+
log.info('config_reloaded', { name: newBundle.config.name });
|
|
587
|
+
eventBus.emit({ type: 'manifest_changed' });
|
|
588
|
+
eventBus.emit({ type: 'files_changed' });
|
|
431
589
|
});
|
|
432
590
|
watcher.start();
|
|
433
591
|
}
|
|
434
592
|
return new Promise((resolve) => {
|
|
435
593
|
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
|
-
}
|
|
594
|
+
log.info('server_started', { host, port, repoPath: config.repoPath, hotReload: !!config.hotReload });
|
|
441
595
|
resolve(httpServer);
|
|
442
596
|
});
|
|
443
597
|
server = httpServer;
|
|
@@ -462,11 +616,92 @@ export async function createLocalServer(config) {
|
|
|
462
616
|
server = null;
|
|
463
617
|
}
|
|
464
618
|
await sessionManager.shutdown();
|
|
619
|
+
if (mcpManager) {
|
|
620
|
+
await mcpManager.shutdown();
|
|
621
|
+
}
|
|
465
622
|
if (storeBackend) {
|
|
466
623
|
await storeBackend.close();
|
|
467
624
|
}
|
|
468
|
-
log.info('
|
|
625
|
+
log.info('server_stopped', {});
|
|
469
626
|
},
|
|
470
627
|
};
|
|
471
628
|
}
|
|
629
|
+
// ---------------------------------------------------------------------------
|
|
630
|
+
// /sessions + /session/:id response helpers
|
|
631
|
+
// ---------------------------------------------------------------------------
|
|
632
|
+
/** Max length of the first-user-message excerpt shown in session lists. */
|
|
633
|
+
const SUMMARY_EXCERPT_MAX = 80;
|
|
634
|
+
function isRecord(x) {
|
|
635
|
+
return typeof x === 'object' && x !== null;
|
|
636
|
+
}
|
|
637
|
+
function isTextPart(part) {
|
|
638
|
+
return isRecord(part) && part['type'] === 'text' && typeof part['text'] === 'string';
|
|
639
|
+
}
|
|
640
|
+
function isToolCallPart(part) {
|
|
641
|
+
return (isRecord(part) &&
|
|
642
|
+
part['type'] === 'tool-call' &&
|
|
643
|
+
typeof part['toolCallId'] === 'string' &&
|
|
644
|
+
typeof part['toolName'] === 'string');
|
|
645
|
+
}
|
|
646
|
+
function getMessageRole(raw) {
|
|
647
|
+
if (!isRecord(raw))
|
|
648
|
+
return null;
|
|
649
|
+
const role = raw['role'];
|
|
650
|
+
return typeof role === 'string' ? role : null;
|
|
651
|
+
}
|
|
652
|
+
function getMessageContent(raw) {
|
|
653
|
+
if (!isRecord(raw))
|
|
654
|
+
return undefined;
|
|
655
|
+
return raw['content'];
|
|
656
|
+
}
|
|
657
|
+
/** Truncate with an ellipsis when the source exceeds the excerpt budget. */
|
|
658
|
+
function excerpt(s) {
|
|
659
|
+
return s.length > SUMMARY_EXCERPT_MAX ? `${s.slice(0, SUMMARY_EXCERPT_MAX)}…` : s;
|
|
660
|
+
}
|
|
661
|
+
/** Extract the first user-message text from a persisted message array for list summaries. */
|
|
662
|
+
function extractFirstUserText(messages) {
|
|
663
|
+
for (const raw of messages) {
|
|
664
|
+
if (getMessageRole(raw) !== 'user')
|
|
665
|
+
continue;
|
|
666
|
+
const content = getMessageContent(raw);
|
|
667
|
+
if (typeof content === 'string')
|
|
668
|
+
return excerpt(content);
|
|
669
|
+
if (Array.isArray(content)) {
|
|
670
|
+
const firstText = content.find(isTextPart);
|
|
671
|
+
if (firstText)
|
|
672
|
+
return excerpt(firstText.text);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return undefined;
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Flatten a persisted `ModelMessage` (ai SDK v6) into the shape the web UI's
|
|
679
|
+
* /session/:id consumer expects: {role, text, toolCalls?}. Returns null for
|
|
680
|
+
* tool-result messages and for assistant turns with no renderable content
|
|
681
|
+
* (the history panel shows conversation + tool-call chips, not raw tool
|
|
682
|
+
* plumbing).
|
|
683
|
+
*/
|
|
684
|
+
function flattenModelMessage(raw) {
|
|
685
|
+
const role = getMessageRole(raw);
|
|
686
|
+
if (role !== 'user' && role !== 'assistant')
|
|
687
|
+
return null;
|
|
688
|
+
const content = getMessageContent(raw);
|
|
689
|
+
if (typeof content === 'string') {
|
|
690
|
+
return { role, text: content };
|
|
691
|
+
}
|
|
692
|
+
if (Array.isArray(content)) {
|
|
693
|
+
const text = content.filter(isTextPart).map((p) => p.text).join('');
|
|
694
|
+
const toolCalls = role === 'assistant'
|
|
695
|
+
? content.filter(isToolCallPart).map((p) => ({
|
|
696
|
+
toolId: p.toolCallId,
|
|
697
|
+
toolName: p.toolName,
|
|
698
|
+
parameters: isRecord(p.input) ? p.input : {},
|
|
699
|
+
}))
|
|
700
|
+
: [];
|
|
701
|
+
if (text.length === 0 && toolCalls.length === 0)
|
|
702
|
+
return null;
|
|
703
|
+
return toolCalls.length > 0 ? { role, text, toolCalls } : { role, text };
|
|
704
|
+
}
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
472
707
|
//# sourceMappingURL=local-server.js.map
|