@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
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { resolveBundle, resolveSession } from './session-resolver.js';
|
|
8
|
+
import { SessionError } from '../errors.js';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Mock buildSessionComponents — we test the resolver, not the builder
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const mockBuildSessionComponents = vi.fn();
|
|
13
|
+
vi.mock('../session/session-builder.js', () => ({
|
|
14
|
+
buildSessionComponents: (...args) => mockBuildSessionComponents(...args),
|
|
15
|
+
}));
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Fixtures
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
function stubBundle(name = 'test-agent') {
|
|
20
|
+
return {
|
|
21
|
+
config: { name, models: { main: { provider: 'test', model: 'test-model' } } },
|
|
22
|
+
connections: new Map(),
|
|
23
|
+
resolvedCredentials: {},
|
|
24
|
+
stores: [],
|
|
25
|
+
skills: [],
|
|
26
|
+
knowledge: [],
|
|
27
|
+
tools: [],
|
|
28
|
+
agents: { main: undefined },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function stubComponents() {
|
|
32
|
+
const factory = vi.fn();
|
|
33
|
+
return {
|
|
34
|
+
provider: { model: 'test-model', provider: 'test' },
|
|
35
|
+
toolRegistry: { size: 0 },
|
|
36
|
+
permissionChecker: { check: () => ({ allowed: true }) },
|
|
37
|
+
systemPrompt: 'test prompt',
|
|
38
|
+
toolContextFactory: factory,
|
|
39
|
+
userRoles: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function stubSession(id, hasFactory = true) {
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
model: 'test-model',
|
|
46
|
+
providerName: 'test',
|
|
47
|
+
toolContextFactory: hasFactory ? vi.fn() : undefined,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function stubSessionManager(overrides = {}) {
|
|
51
|
+
return {
|
|
52
|
+
get: vi.fn(),
|
|
53
|
+
resume: vi.fn(),
|
|
54
|
+
create: vi.fn(),
|
|
55
|
+
...overrides,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function stubShared() {
|
|
59
|
+
return {
|
|
60
|
+
storeBackend: null,
|
|
61
|
+
mcpManager: null,
|
|
62
|
+
logger: { info: vi.fn(), warn: vi.fn(), debug: vi.fn(), error: vi.fn(), fatal: vi.fn(), child: vi.fn() },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// resolveBundle
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
describe('resolveBundle', () => {
|
|
69
|
+
it('returns static bundle when no deployId', async () => {
|
|
70
|
+
const bundle = stubBundle();
|
|
71
|
+
const result = await resolveBundle({ staticBundle: bundle });
|
|
72
|
+
expect(result).toBe(bundle);
|
|
73
|
+
});
|
|
74
|
+
it('returns null when no bundle available', async () => {
|
|
75
|
+
const result = await resolveBundle({});
|
|
76
|
+
expect(result).toBeNull();
|
|
77
|
+
});
|
|
78
|
+
it('calls bundleProvider when deployId is provided', async () => {
|
|
79
|
+
const bundle = stubBundle();
|
|
80
|
+
const provider = vi.fn().mockResolvedValue(bundle);
|
|
81
|
+
const result = await resolveBundle({ bundleProvider: provider }, 'deploy-1', 'token-abc');
|
|
82
|
+
expect(provider).toHaveBeenCalledWith('deploy-1', 'token-abc');
|
|
83
|
+
expect(result).toBe(bundle);
|
|
84
|
+
});
|
|
85
|
+
it('falls back to static bundle when no deployId even if bundleProvider exists', async () => {
|
|
86
|
+
const staticBundle = stubBundle('static');
|
|
87
|
+
const provider = vi.fn();
|
|
88
|
+
const result = await resolveBundle({ staticBundle, bundleProvider: provider });
|
|
89
|
+
expect(provider).not.toHaveBeenCalled();
|
|
90
|
+
expect(result).toBe(staticBundle);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// resolveSession
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
describe('resolveSession', () => {
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
vi.clearAllMocks();
|
|
99
|
+
mockBuildSessionComponents.mockReturnValue(stubComponents());
|
|
100
|
+
});
|
|
101
|
+
it('returns in-memory session with cached factory (no rebuild)', async () => {
|
|
102
|
+
const session = stubSession('sess-1');
|
|
103
|
+
const mgr = stubSessionManager({ get: vi.fn().mockReturnValue(session) });
|
|
104
|
+
const result = await resolveSession('sess-1', {
|
|
105
|
+
sessionManager: mgr,
|
|
106
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
107
|
+
shared: stubShared(),
|
|
108
|
+
});
|
|
109
|
+
expect(result.session).toBe(session);
|
|
110
|
+
expect(result.toolContextFactory).toBe(session.toolContextFactory);
|
|
111
|
+
expect(mockBuildSessionComponents).not.toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
it('resumes from store when not in memory', async () => {
|
|
114
|
+
const resumed = stubSession('sess-2');
|
|
115
|
+
const mgr = stubSessionManager({
|
|
116
|
+
get: vi.fn().mockReturnValue(undefined),
|
|
117
|
+
resume: vi.fn().mockResolvedValue(resumed),
|
|
118
|
+
});
|
|
119
|
+
const result = await resolveSession('sess-2', {
|
|
120
|
+
sessionManager: mgr,
|
|
121
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
122
|
+
shared: stubShared(),
|
|
123
|
+
});
|
|
124
|
+
expect(result.session).toBe(resumed);
|
|
125
|
+
expect(mockBuildSessionComponents).toHaveBeenCalledOnce();
|
|
126
|
+
expect(mgr.resume).toHaveBeenCalledWith('sess-2', expect.any(Object));
|
|
127
|
+
});
|
|
128
|
+
it('creates new session when session_id not found', async () => {
|
|
129
|
+
const created = stubSession('sess-3');
|
|
130
|
+
const mgr = stubSessionManager({
|
|
131
|
+
get: vi.fn().mockReturnValue(undefined),
|
|
132
|
+
resume: vi.fn().mockResolvedValue(null),
|
|
133
|
+
create: vi.fn().mockReturnValue(created),
|
|
134
|
+
});
|
|
135
|
+
const result = await resolveSession('sess-not-found', {
|
|
136
|
+
sessionManager: mgr,
|
|
137
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
138
|
+
shared: stubShared(),
|
|
139
|
+
});
|
|
140
|
+
expect(result.session).toBe(created);
|
|
141
|
+
expect(mgr.create).toHaveBeenCalledOnce();
|
|
142
|
+
});
|
|
143
|
+
it('creates new session when no session_id provided', async () => {
|
|
144
|
+
const created = stubSession('sess-new');
|
|
145
|
+
const mgr = stubSessionManager({
|
|
146
|
+
create: vi.fn().mockReturnValue(created),
|
|
147
|
+
});
|
|
148
|
+
const result = await resolveSession(undefined, {
|
|
149
|
+
sessionManager: mgr,
|
|
150
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
151
|
+
shared: stubShared(),
|
|
152
|
+
});
|
|
153
|
+
expect(result.session).toBe(created);
|
|
154
|
+
expect(mgr.get).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
it('throws SessionError when no bundle available', async () => {
|
|
157
|
+
const mgr = stubSessionManager();
|
|
158
|
+
await expect(resolveSession(undefined, {
|
|
159
|
+
sessionManager: mgr,
|
|
160
|
+
bundleResolver: {},
|
|
161
|
+
shared: stubShared(),
|
|
162
|
+
})).rejects.toThrow(SessionError);
|
|
163
|
+
});
|
|
164
|
+
it('passes userRoles from role option', async () => {
|
|
165
|
+
const created = stubSession('sess-roles');
|
|
166
|
+
const mgr = stubSessionManager({
|
|
167
|
+
create: vi.fn().mockReturnValue(created),
|
|
168
|
+
});
|
|
169
|
+
await resolveSession(undefined, {
|
|
170
|
+
sessionManager: mgr,
|
|
171
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
172
|
+
shared: stubShared(),
|
|
173
|
+
role: 'analyst',
|
|
174
|
+
});
|
|
175
|
+
expect(mockBuildSessionComponents).toHaveBeenCalledWith(expect.objectContaining({ userRoles: ['analyst'] }));
|
|
176
|
+
});
|
|
177
|
+
it('passes auth context through to the session manager', async () => {
|
|
178
|
+
// Session identity (tenant / user mapping) is no longer threaded
|
|
179
|
+
// through the session manager — callers that need it live at the
|
|
180
|
+
// API boundary. This test is reduced to verify create() is called.
|
|
181
|
+
const created = stubSession('sess-auth');
|
|
182
|
+
const mgr = stubSessionManager({
|
|
183
|
+
create: vi.fn().mockReturnValue(created),
|
|
184
|
+
});
|
|
185
|
+
await resolveSession(undefined, {
|
|
186
|
+
sessionManager: mgr,
|
|
187
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
188
|
+
shared: stubShared(),
|
|
189
|
+
auth: { applicationId: 'app-1', authMethod: 'api_key' },
|
|
190
|
+
});
|
|
191
|
+
expect(mgr.create).toHaveBeenCalled();
|
|
192
|
+
});
|
|
193
|
+
it('stores toolContextFactory on created session via CreateSessionOptions', async () => {
|
|
194
|
+
const created = stubSession('sess-factory');
|
|
195
|
+
const mockCreate = vi.fn().mockReturnValue(created);
|
|
196
|
+
const mgr = stubSessionManager({ create: mockCreate });
|
|
197
|
+
await resolveSession(undefined, {
|
|
198
|
+
sessionManager: mgr,
|
|
199
|
+
bundleResolver: { staticBundle: stubBundle() },
|
|
200
|
+
shared: stubShared(),
|
|
201
|
+
});
|
|
202
|
+
const createArg = mockCreate.mock.calls[0][0];
|
|
203
|
+
expect(createArg['toolContextFactory']).toBeDefined();
|
|
204
|
+
expect(typeof createArg['toolContextFactory']).toBe('function');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
//# sourceMappingURL=session-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-resolver.test.js","sourceRoot":"","sources":["../../../src/routes/session-resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Router } from 'express';
|
|
7
7
|
import type { AutomationDefinition } from '@amodalai/core';
|
|
8
|
-
import type {
|
|
8
|
+
import type { AutomationResult } from '../types.js';
|
|
9
|
+
type AutomationRunnerFn = (automation: AutomationDefinition, payload?: Record<string, unknown>) => Promise<AutomationResult>;
|
|
9
10
|
export interface WebhookRouterOptions {
|
|
10
11
|
automations: AutomationDefinition[];
|
|
11
12
|
runAutomation: AutomationRunnerFn;
|
|
12
13
|
}
|
|
13
14
|
export declare function createWebhookRouter(options: WebhookRouterOptions): Router;
|
|
15
|
+
export {};
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
*/
|
|
6
6
|
import { Router } from 'express';
|
|
7
|
+
import rateLimit from 'express-rate-limit';
|
|
7
8
|
import { WebhookPayloadSchema } from '../types.js';
|
|
8
9
|
import { validate } from '../middleware/request-validation.js';
|
|
9
10
|
import { AppError } from '../middleware/error-handler.js';
|
|
11
|
+
import { asyncHandler } from './route-helpers.js';
|
|
10
12
|
export function createWebhookRouter(options) {
|
|
11
13
|
const router = Router();
|
|
12
14
|
// Build a lookup map: webhook source name → automation definition
|
|
@@ -16,9 +18,15 @@ export function createWebhookRouter(options) {
|
|
|
16
18
|
webhookAutomations.set(a.name, a);
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
21
|
+
// Rate-limit inbound webhooks per IP so validation and automation
|
|
22
|
+
// dispatch can't be triggered unboundedly by an attacker.
|
|
23
|
+
const webhookLimiter = rateLimit({
|
|
24
|
+
windowMs: 60 * 1000,
|
|
25
|
+
max: 100,
|
|
26
|
+
standardHeaders: true,
|
|
27
|
+
legacyHeaders: false,
|
|
28
|
+
});
|
|
29
|
+
router.post('/webhooks/:name', webhookLimiter, validate(WebhookPayloadSchema), asyncHandler(async (req, res, next) => {
|
|
22
30
|
try {
|
|
23
31
|
const { name } = req.params;
|
|
24
32
|
if (!name) {
|
|
@@ -39,7 +47,7 @@ export function createWebhookRouter(options) {
|
|
|
39
47
|
catch (err) {
|
|
40
48
|
next(err);
|
|
41
49
|
}
|
|
42
|
-
});
|
|
50
|
+
}));
|
|
43
51
|
return router;
|
|
44
52
|
}
|
|
45
53
|
//# sourceMappingURL=webhooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../../src/routes/webhooks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../../src/routes/webhooks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAUhD,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,kEAAkE;IAClE,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAgC,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACjC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,0DAA0D;IAC1D,MAAM,cAAc,GAAG,SAAS,CAAC;QAC/B,QAAQ,EAAE,EAAE,GAAG,IAAI;QACnB,GAAG,EAAE,GAAG;QACR,eAAe,EAAE,IAAI;QACrB,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,cAAc,EACd,QAAQ,CAAC,oBAAoB,CAAC,EAC9B,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,6BAA6B,CAAC,CAAC;YACzE,CAAC;YAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,QAAQ,CAChB,GAAG,EACH,sBAAsB,EACtB,gCAAgC,IAAI,GAAG,CACxC,CAAC;YACJ,CAAC;YAED,0DAA0D;YAC1D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9B,KAAK,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAEhD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,UAAU,EAAE,IAAI;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Permission checker interface for tool execution.
|
|
8
|
+
*
|
|
9
|
+
* Extracted from the request tool so that the same permission pipeline
|
|
10
|
+
* can be used by the agent loop (confirmation flow), egress proxy
|
|
11
|
+
* (Roadmap 5.1), async approval (Roadmap 5.3), and PII detection
|
|
12
|
+
* (Roadmap 5.2).
|
|
13
|
+
*
|
|
14
|
+
* The default implementation reads from access.json via ActionGate.
|
|
15
|
+
* Future implementations can add external policy services, audit
|
|
16
|
+
* logging, or rate limiting.
|
|
17
|
+
*/
|
|
18
|
+
import type { AccessConfig } from '@amodalai/types';
|
|
19
|
+
/**
|
|
20
|
+
* Result of a permission check on a tool call.
|
|
21
|
+
*/
|
|
22
|
+
export type PermissionResult = {
|
|
23
|
+
allowed: true;
|
|
24
|
+
requiresConfirmation?: false;
|
|
25
|
+
} | {
|
|
26
|
+
allowed: true;
|
|
27
|
+
requiresConfirmation: true;
|
|
28
|
+
reason: string;
|
|
29
|
+
} | {
|
|
30
|
+
allowed: false;
|
|
31
|
+
reason: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Information about the tool call being checked.
|
|
35
|
+
*/
|
|
36
|
+
export interface PermissionCheckRequest {
|
|
37
|
+
/** The connection being accessed */
|
|
38
|
+
connection: string;
|
|
39
|
+
/** Endpoint path (e.g., "POST /articles") */
|
|
40
|
+
endpointPath: string;
|
|
41
|
+
/** Intent declared by the LLM */
|
|
42
|
+
intent: 'read' | 'write' | 'confirmed_write';
|
|
43
|
+
/** HTTP method */
|
|
44
|
+
method: string;
|
|
45
|
+
/** Request parameters (for threshold evaluation) */
|
|
46
|
+
params?: Record<string, unknown>;
|
|
47
|
+
/** Whether this is from a delegated/task agent */
|
|
48
|
+
isDelegated?: boolean;
|
|
49
|
+
/** Whether plan mode is active */
|
|
50
|
+
planModeActive?: boolean;
|
|
51
|
+
/** Whether the caller is read-only (task agents) */
|
|
52
|
+
readOnly?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Interface for checking tool execution permissions.
|
|
56
|
+
*
|
|
57
|
+
* Implementations decide whether a tool call is allowed, requires
|
|
58
|
+
* confirmation, or is blocked. The request tool and agent loop both
|
|
59
|
+
* use this interface — the tool doesn't know how permissions are checked.
|
|
60
|
+
*/
|
|
61
|
+
export interface PermissionChecker {
|
|
62
|
+
check(request: PermissionCheckRequest): PermissionResult;
|
|
63
|
+
}
|
|
64
|
+
export interface AccessJsonPermissionCheckerConfig {
|
|
65
|
+
accessConfigs: Map<string, AccessConfig>;
|
|
66
|
+
isDelegated: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Permission checker that reads from access.json configuration.
|
|
70
|
+
*
|
|
71
|
+
* Wraps the existing ActionGate to evaluate confirmation tiers,
|
|
72
|
+
* thresholds, and delegation escalation. Adds the intent/method
|
|
73
|
+
* validation that was previously inline in the request tool.
|
|
74
|
+
*/
|
|
75
|
+
export declare class AccessJsonPermissionChecker implements PermissionChecker {
|
|
76
|
+
private readonly gate;
|
|
77
|
+
constructor(config: AccessJsonPermissionCheckerConfig);
|
|
78
|
+
check(request: PermissionCheckRequest): PermissionResult;
|
|
79
|
+
private gateDecisionToResult;
|
|
80
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { ActionGate } from '@amodalai/core';
|
|
7
|
+
/**
|
|
8
|
+
* Permission checker that reads from access.json configuration.
|
|
9
|
+
*
|
|
10
|
+
* Wraps the existing ActionGate to evaluate confirmation tiers,
|
|
11
|
+
* thresholds, and delegation escalation. Adds the intent/method
|
|
12
|
+
* validation that was previously inline in the request tool.
|
|
13
|
+
*/
|
|
14
|
+
export class AccessJsonPermissionChecker {
|
|
15
|
+
gate;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.gate = new ActionGate({
|
|
18
|
+
accessConfigs: config.accessConfigs,
|
|
19
|
+
isDelegated: config.isDelegated,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
check(request) {
|
|
23
|
+
const { method, intent, endpointPath, connection, params, readOnly, planModeActive } = request;
|
|
24
|
+
const isMutating = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method.toUpperCase());
|
|
25
|
+
// Block writes in read-only mode (task agents)
|
|
26
|
+
if (readOnly && isMutating) {
|
|
27
|
+
return { allowed: false, reason: 'Write operations are not allowed in read-only mode' };
|
|
28
|
+
}
|
|
29
|
+
// Block writes in plan mode
|
|
30
|
+
if (planModeActive && isMutating && intent !== 'read') {
|
|
31
|
+
return { allowed: false, reason: 'Write operations are blocked in plan mode. Present your plan for approval first.' };
|
|
32
|
+
}
|
|
33
|
+
// Enforce write intent for mutating methods
|
|
34
|
+
if (isMutating && intent === 'read') {
|
|
35
|
+
return {
|
|
36
|
+
allowed: false,
|
|
37
|
+
reason: `${method.toUpperCase()} requires intent "write" or "confirmed_write", not "read"`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// For read-only operations, allow without gate check
|
|
41
|
+
if (!isMutating) {
|
|
42
|
+
return { allowed: true };
|
|
43
|
+
}
|
|
44
|
+
// Evaluate action gate for write operations
|
|
45
|
+
const gateResult = this.gate.evaluate(endpointPath, connection, params);
|
|
46
|
+
return this.gateDecisionToResult(gateResult.decision, gateResult.reason);
|
|
47
|
+
}
|
|
48
|
+
gateDecisionToResult(decision, reason) {
|
|
49
|
+
switch (decision) {
|
|
50
|
+
case 'allow':
|
|
51
|
+
return { allowed: true };
|
|
52
|
+
case 'confirm':
|
|
53
|
+
return {
|
|
54
|
+
allowed: true,
|
|
55
|
+
requiresConfirmation: true,
|
|
56
|
+
reason: reason ?? 'This operation requires confirmation',
|
|
57
|
+
};
|
|
58
|
+
case 'review':
|
|
59
|
+
return {
|
|
60
|
+
allowed: false,
|
|
61
|
+
reason: reason ?? 'This operation requires human review and cannot be executed by the agent',
|
|
62
|
+
};
|
|
63
|
+
case 'never':
|
|
64
|
+
return {
|
|
65
|
+
allowed: false,
|
|
66
|
+
reason: reason ?? 'This operation is not allowed',
|
|
67
|
+
};
|
|
68
|
+
default: {
|
|
69
|
+
const _exhaustive = decision;
|
|
70
|
+
return { allowed: false, reason: `Unknown gate decision: ${String(_exhaustive)}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=permission-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-checker.js","sourceRoot":"","sources":["../../../src/security/permission-checker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AA6D1C;;;;;;GAMG;AACH,MAAM,OAAO,2BAA2B;IACrB,IAAI,CAAa;IAElC,YAAY,MAAyC;QACnD,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC;YACzB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,WAAW,EAAE,MAAM,CAAC,WAAW;SAChC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAA+B;QACnC,MAAM,EAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAC,GAAG,OAAO,CAAC;QAC7F,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAErF,+CAA+C;QAC/C,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;YAC3B,OAAO,EAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,oDAAoD,EAAC,CAAC;QACxF,CAAC;QAED,4BAA4B;QAC5B,IAAI,cAAc,IAAI,UAAU,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtD,OAAO,EAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,kFAAkF,EAAC,CAAC;QACtH,CAAC;QAED,4CAA4C;QAC5C,IAAI,UAAU,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACpC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,2DAA2D;aAC3F,CAAC;QACJ,CAAC;QAED,qDAAqD;QACrD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;QACzB,CAAC;QAED,4CAA4C;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAExE,OAAO,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3E,CAAC;IAEO,oBAAoB,CAAC,QAAsB,EAAE,MAAe;QAClE,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,OAAO;gBACV,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;YACzB,KAAK,SAAS;gBACZ,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,oBAAoB,EAAE,IAAI;oBAC1B,MAAM,EAAE,MAAM,IAAI,sCAAsC;iBACzD,CAAC;YACJ,KAAK,QAAQ;gBACX,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,MAAM,IAAI,0EAA0E;iBAC7F,CAAC;YACJ,KAAK,OAAO;gBACV,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,MAAM,IAAI,+BAA+B;iBAClD,CAAC;YACJ,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,QAAQ,CAAC;gBACpC,OAAO,EAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,MAAM,CAAC,WAAW,CAAC,EAAE,EAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { AccessJsonPermissionChecker } from './permission-checker.js';
|
|
8
|
+
function makeAccess(overrides = {}) {
|
|
9
|
+
return {
|
|
10
|
+
endpoints: {
|
|
11
|
+
'POST /articles': {
|
|
12
|
+
returns: ['article'],
|
|
13
|
+
confirm: true,
|
|
14
|
+
reason: 'Creates a new article',
|
|
15
|
+
...overrides,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function makeRequest(overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
connection: 'blog-api',
|
|
23
|
+
endpointPath: 'POST /articles',
|
|
24
|
+
intent: 'write',
|
|
25
|
+
method: 'POST',
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
describe('AccessJsonPermissionChecker', () => {
|
|
30
|
+
describe('read operations', () => {
|
|
31
|
+
it('allows GET requests without gate check', () => {
|
|
32
|
+
const checker = new AccessJsonPermissionChecker({
|
|
33
|
+
accessConfigs: new Map([['blog-api', makeAccess()]]),
|
|
34
|
+
isDelegated: false,
|
|
35
|
+
});
|
|
36
|
+
const result = checker.check(makeRequest({ method: 'GET', intent: 'read', endpointPath: 'GET /articles' }));
|
|
37
|
+
expect(result.allowed).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('write intent enforcement', () => {
|
|
41
|
+
it('rejects mutating methods with read intent', () => {
|
|
42
|
+
const checker = new AccessJsonPermissionChecker({
|
|
43
|
+
accessConfigs: new Map(),
|
|
44
|
+
isDelegated: false,
|
|
45
|
+
});
|
|
46
|
+
const result = checker.check(makeRequest({ method: 'POST', intent: 'read' }));
|
|
47
|
+
expect(result.allowed).toBe(false);
|
|
48
|
+
if (!result.allowed) {
|
|
49
|
+
expect(result.reason).toContain('requires intent "write"');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
it('rejects DELETE with read intent', () => {
|
|
53
|
+
const checker = new AccessJsonPermissionChecker({
|
|
54
|
+
accessConfigs: new Map(),
|
|
55
|
+
isDelegated: false,
|
|
56
|
+
});
|
|
57
|
+
const result = checker.check(makeRequest({ method: 'DELETE', intent: 'read' }));
|
|
58
|
+
expect(result.allowed).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('read-only mode (task agents)', () => {
|
|
62
|
+
it('blocks write operations in read-only mode', () => {
|
|
63
|
+
const checker = new AccessJsonPermissionChecker({
|
|
64
|
+
accessConfigs: new Map(),
|
|
65
|
+
isDelegated: false,
|
|
66
|
+
});
|
|
67
|
+
const result = checker.check(makeRequest({ readOnly: true }));
|
|
68
|
+
expect(result.allowed).toBe(false);
|
|
69
|
+
if (!result.allowed) {
|
|
70
|
+
expect(result.reason).toContain('read-only mode');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
it('allows GET in read-only mode', () => {
|
|
74
|
+
const checker = new AccessJsonPermissionChecker({
|
|
75
|
+
accessConfigs: new Map(),
|
|
76
|
+
isDelegated: false,
|
|
77
|
+
});
|
|
78
|
+
const result = checker.check(makeRequest({ method: 'GET', intent: 'read', readOnly: true }));
|
|
79
|
+
expect(result.allowed).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('plan mode', () => {
|
|
83
|
+
it('blocks writes in plan mode', () => {
|
|
84
|
+
const checker = new AccessJsonPermissionChecker({
|
|
85
|
+
accessConfigs: new Map(),
|
|
86
|
+
isDelegated: false,
|
|
87
|
+
});
|
|
88
|
+
const result = checker.check(makeRequest({ planModeActive: true }));
|
|
89
|
+
expect(result.allowed).toBe(false);
|
|
90
|
+
if (!result.allowed) {
|
|
91
|
+
expect(result.reason).toContain('plan mode');
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('confirmation tiers', () => {
|
|
96
|
+
it('returns requiresConfirmation for confirm tier', () => {
|
|
97
|
+
const checker = new AccessJsonPermissionChecker({
|
|
98
|
+
accessConfigs: new Map([['blog-api', makeAccess({ confirm: true })]]),
|
|
99
|
+
isDelegated: false,
|
|
100
|
+
});
|
|
101
|
+
const result = checker.check(makeRequest({ intent: 'write' }));
|
|
102
|
+
expect(result.allowed).toBe(true);
|
|
103
|
+
if (result.allowed) {
|
|
104
|
+
expect(result.requiresConfirmation).toBe(true);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
it('blocks for review tier', () => {
|
|
108
|
+
const checker = new AccessJsonPermissionChecker({
|
|
109
|
+
accessConfigs: new Map([['blog-api', makeAccess({ confirm: 'review' })]]),
|
|
110
|
+
isDelegated: false,
|
|
111
|
+
});
|
|
112
|
+
const result = checker.check(makeRequest({ intent: 'write' }));
|
|
113
|
+
expect(result.allowed).toBe(false);
|
|
114
|
+
if (!result.allowed) {
|
|
115
|
+
expect(result.reason).toBeDefined();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
it('blocks for never tier', () => {
|
|
119
|
+
const checker = new AccessJsonPermissionChecker({
|
|
120
|
+
accessConfigs: new Map([['blog-api', makeAccess({ confirm: 'never' })]]),
|
|
121
|
+
isDelegated: false,
|
|
122
|
+
});
|
|
123
|
+
const result = checker.check(makeRequest({ intent: 'write' }));
|
|
124
|
+
expect(result.allowed).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
it('allows when no confirm field', () => {
|
|
127
|
+
const checker = new AccessJsonPermissionChecker({
|
|
128
|
+
accessConfigs: new Map([['blog-api', makeAccess({ confirm: undefined })]]),
|
|
129
|
+
isDelegated: false,
|
|
130
|
+
});
|
|
131
|
+
const result = checker.check(makeRequest({ intent: 'write' }));
|
|
132
|
+
expect(result.allowed).toBe(true);
|
|
133
|
+
if (result.allowed) {
|
|
134
|
+
expect(result.requiresConfirmation).toBeFalsy();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
it('allows when connection has no access config', () => {
|
|
138
|
+
const checker = new AccessJsonPermissionChecker({
|
|
139
|
+
accessConfigs: new Map(),
|
|
140
|
+
isDelegated: false,
|
|
141
|
+
});
|
|
142
|
+
const result = checker.check(makeRequest({ intent: 'write' }));
|
|
143
|
+
expect(result.allowed).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe('delegation escalation', () => {
|
|
147
|
+
it('escalates confirm to review for delegated agents', () => {
|
|
148
|
+
const access = makeAccess({ confirm: true });
|
|
149
|
+
access.delegations = { enabled: true, escalateConfirm: true };
|
|
150
|
+
const checker = new AccessJsonPermissionChecker({
|
|
151
|
+
accessConfigs: new Map([['blog-api', access]]),
|
|
152
|
+
isDelegated: true,
|
|
153
|
+
});
|
|
154
|
+
const result = checker.check(makeRequest({ intent: 'write' }));
|
|
155
|
+
expect(result.allowed).toBe(false);
|
|
156
|
+
if (!result.allowed) {
|
|
157
|
+
expect(result.reason).toContain('Delegated');
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
describe('threshold escalation', () => {
|
|
162
|
+
it('escalates when threshold is exceeded', () => {
|
|
163
|
+
const access = makeAccess({
|
|
164
|
+
confirm: true,
|
|
165
|
+
thresholds: [{ field: 'amount', above: 10000, escalate: 'never' }],
|
|
166
|
+
});
|
|
167
|
+
const checker = new AccessJsonPermissionChecker({
|
|
168
|
+
accessConfigs: new Map([['blog-api', access]]),
|
|
169
|
+
isDelegated: false,
|
|
170
|
+
});
|
|
171
|
+
const result = checker.check(makeRequest({
|
|
172
|
+
intent: 'write',
|
|
173
|
+
params: { amount: 50000 },
|
|
174
|
+
}));
|
|
175
|
+
expect(result.allowed).toBe(false);
|
|
176
|
+
});
|
|
177
|
+
it('does not escalate when threshold is not exceeded', () => {
|
|
178
|
+
const access = makeAccess({
|
|
179
|
+
confirm: true,
|
|
180
|
+
thresholds: [{ field: 'amount', above: 10000, escalate: 'never' }],
|
|
181
|
+
});
|
|
182
|
+
const checker = new AccessJsonPermissionChecker({
|
|
183
|
+
accessConfigs: new Map([['blog-api', access]]),
|
|
184
|
+
isDelegated: false,
|
|
185
|
+
});
|
|
186
|
+
const result = checker.check(makeRequest({
|
|
187
|
+
intent: 'write',
|
|
188
|
+
params: { amount: 500 },
|
|
189
|
+
}));
|
|
190
|
+
// Still 'confirm' tier (not escalated to 'never')
|
|
191
|
+
expect(result.allowed).toBe(true);
|
|
192
|
+
if (result.allowed) {
|
|
193
|
+
expect(result.requiresConfirmation).toBe(true);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('exhaustive gate decisions', () => {
|
|
198
|
+
it('handles all four gate decisions', () => {
|
|
199
|
+
// allow — tested above (no confirm field)
|
|
200
|
+
// confirm — tested above (confirm: true)
|
|
201
|
+
// review — tested above (confirm: 'review')
|
|
202
|
+
// never — tested above (confirm: 'never')
|
|
203
|
+
// This test exists to document coverage, not add new assertions
|
|
204
|
+
expect(true).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
//# sourceMappingURL=permission-checker.test.js.map
|