@amodalai/runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/.last_build +0 -0
- package/dist/src/agent/agent-runner.d.ts +13 -0
- package/dist/src/agent/agent-runner.js +827 -0
- package/dist/src/agent/agent-runner.js.map +1 -0
- package/dist/src/agent/agent-runner.test.d.ts +6 -0
- package/dist/src/agent/agent-runner.test.js +552 -0
- package/dist/src/agent/agent-runner.test.js.map +1 -0
- package/dist/src/agent/agent-types.d.ts +57 -0
- package/dist/src/agent/agent-types.js +17 -0
- package/dist/src/agent/agent-types.js.map +1 -0
- package/dist/src/agent/agent-types.test.d.ts +6 -0
- package/dist/src/agent/agent-types.test.js +44 -0
- package/dist/src/agent/agent-types.test.js.map +1 -0
- package/dist/src/agent/automation-bridge.d.ts +24 -0
- package/dist/src/agent/automation-bridge.js +24 -0
- package/dist/src/agent/automation-bridge.js.map +1 -0
- package/dist/src/agent/automation-bridge.test.d.ts +6 -0
- package/dist/src/agent/automation-bridge.test.js +67 -0
- package/dist/src/agent/automation-bridge.test.js.map +1 -0
- package/dist/src/agent/config-watcher.d.ts +20 -0
- package/dist/src/agent/config-watcher.js +68 -0
- package/dist/src/agent/config-watcher.js.map +1 -0
- package/dist/src/agent/config-watcher.test.d.ts +6 -0
- package/dist/src/agent/config-watcher.test.js +83 -0
- package/dist/src/agent/config-watcher.test.js.map +1 -0
- package/dist/src/agent/custom-tools-e2e.test.d.ts +6 -0
- package/dist/src/agent/custom-tools-e2e.test.js +566 -0
- package/dist/src/agent/custom-tools-e2e.test.js.map +1 -0
- package/dist/src/agent/local-server.d.ts +15 -0
- package/dist/src/agent/local-server.js +158 -0
- package/dist/src/agent/local-server.js.map +1 -0
- package/dist/src/agent/local-server.test.d.ts +6 -0
- package/dist/src/agent/local-server.test.js +126 -0
- package/dist/src/agent/local-server.test.js.map +1 -0
- package/dist/src/agent/proactive/delivery.d.ts +21 -0
- package/dist/src/agent/proactive/delivery.js +68 -0
- package/dist/src/agent/proactive/delivery.js.map +1 -0
- package/dist/src/agent/proactive/delivery.test.d.ts +6 -0
- package/dist/src/agent/proactive/delivery.test.js +65 -0
- package/dist/src/agent/proactive/delivery.test.js.map +1 -0
- package/dist/src/agent/proactive/proactive-runner.d.ts +76 -0
- package/dist/src/agent/proactive/proactive-runner.js +201 -0
- package/dist/src/agent/proactive/proactive-runner.js.map +1 -0
- package/dist/src/agent/proactive/proactive-runner.test.d.ts +6 -0
- package/dist/src/agent/proactive/proactive-runner.test.js +265 -0
- package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -0
- package/dist/src/agent/request-helper.d.ts +16 -0
- package/dist/src/agent/request-helper.js +87 -0
- package/dist/src/agent/request-helper.js.map +1 -0
- package/dist/src/agent/routes/automations.d.ts +19 -0
- package/dist/src/agent/routes/automations.js +58 -0
- package/dist/src/agent/routes/automations.js.map +1 -0
- package/dist/src/agent/routes/automations.test.d.ts +6 -0
- package/dist/src/agent/routes/automations.test.js +117 -0
- package/dist/src/agent/routes/automations.test.js.map +1 -0
- package/dist/src/agent/routes/chat.d.ts +35 -0
- package/dist/src/agent/routes/chat.js +88 -0
- package/dist/src/agent/routes/chat.js.map +1 -0
- package/dist/src/agent/routes/chat.test.d.ts +6 -0
- package/dist/src/agent/routes/chat.test.js +115 -0
- package/dist/src/agent/routes/chat.test.js.map +1 -0
- package/dist/src/agent/routes/inspect.d.ts +12 -0
- package/dist/src/agent/routes/inspect.js +40 -0
- package/dist/src/agent/routes/inspect.js.map +1 -0
- package/dist/src/agent/routes/inspect.test.d.ts +6 -0
- package/dist/src/agent/routes/inspect.test.js +80 -0
- package/dist/src/agent/routes/inspect.test.js.map +1 -0
- package/dist/src/agent/routes/stores.d.ts +20 -0
- package/dist/src/agent/routes/stores.js +137 -0
- package/dist/src/agent/routes/stores.js.map +1 -0
- package/dist/src/agent/routes/stores.test.d.ts +6 -0
- package/dist/src/agent/routes/stores.test.js +191 -0
- package/dist/src/agent/routes/stores.test.js.map +1 -0
- package/dist/src/agent/routes/task.d.ts +11 -0
- package/dist/src/agent/routes/task.js +116 -0
- package/dist/src/agent/routes/task.js.map +1 -0
- package/dist/src/agent/routes/task.test.d.ts +6 -0
- package/dist/src/agent/routes/task.test.js +91 -0
- package/dist/src/agent/routes/task.test.js.map +1 -0
- package/dist/src/agent/routes/webhooks.d.ts +17 -0
- package/dist/src/agent/routes/webhooks.js +53 -0
- package/dist/src/agent/routes/webhooks.js.map +1 -0
- package/dist/src/agent/routes/webhooks.test.d.ts +6 -0
- package/dist/src/agent/routes/webhooks.test.js +100 -0
- package/dist/src/agent/routes/webhooks.test.js.map +1 -0
- package/dist/src/agent/session-manager.d.ts +72 -0
- package/dist/src/agent/session-manager.js +214 -0
- package/dist/src/agent/session-manager.js.map +1 -0
- package/dist/src/agent/session-manager.test.d.ts +6 -0
- package/dist/src/agent/session-manager.test.js +145 -0
- package/dist/src/agent/session-manager.test.js.map +1 -0
- package/dist/src/agent/shell-executor-local.d.ts +16 -0
- package/dist/src/agent/shell-executor-local.js +51 -0
- package/dist/src/agent/shell-executor-local.js.map +1 -0
- package/dist/src/agent/shell-executor-local.test.d.ts +6 -0
- package/dist/src/agent/shell-executor-local.test.js +46 -0
- package/dist/src/agent/shell-executor-local.test.js.map +1 -0
- package/dist/src/agent/snapshot-server.d.ts +38 -0
- package/dist/src/agent/snapshot-server.js +114 -0
- package/dist/src/agent/snapshot-server.js.map +1 -0
- package/dist/src/agent/stores-e2e.test.d.ts +6 -0
- package/dist/src/agent/stores-e2e.test.js +433 -0
- package/dist/src/agent/stores-e2e.test.js.map +1 -0
- package/dist/src/agent/tool-context-builder.d.ts +11 -0
- package/dist/src/agent/tool-context-builder.js +84 -0
- package/dist/src/agent/tool-context-builder.js.map +1 -0
- package/dist/src/agent/tool-context-builder.test.d.ts +6 -0
- package/dist/src/agent/tool-context-builder.test.js +152 -0
- package/dist/src/agent/tool-context-builder.test.js.map +1 -0
- package/dist/src/agent/tool-executor-local.d.ts +15 -0
- package/dist/src/agent/tool-executor-local.js +74 -0
- package/dist/src/agent/tool-executor-local.js.map +1 -0
- package/dist/src/agent/tool-executor-local.test.d.ts +6 -0
- package/dist/src/agent/tool-executor-local.test.js +116 -0
- package/dist/src/agent/tool-executor-local.test.js.map +1 -0
- package/dist/src/agent/tool-harness-template.d.ts +23 -0
- package/dist/src/agent/tool-harness-template.js +94 -0
- package/dist/src/agent/tool-harness-template.js.map +1 -0
- package/dist/src/agent/user-context-fetcher.d.ts +25 -0
- package/dist/src/agent/user-context-fetcher.js +79 -0
- package/dist/src/agent/user-context-fetcher.js.map +1 -0
- package/dist/src/agent/user-context-fetcher.test.d.ts +6 -0
- package/dist/src/agent/user-context-fetcher.test.js +121 -0
- package/dist/src/agent/user-context-fetcher.test.js.map +1 -0
- package/dist/src/audit/audit-client.d.ts +46 -0
- package/dist/src/audit/audit-client.js +83 -0
- package/dist/src/audit/audit-client.js.map +1 -0
- package/dist/src/cron/heartbeat-runner.d.ts +24 -0
- package/dist/src/cron/heartbeat-runner.js +87 -0
- package/dist/src/cron/heartbeat-runner.js.map +1 -0
- package/dist/src/cron/heartbeat-runner.test.d.ts +6 -0
- package/dist/src/cron/heartbeat-runner.test.js +120 -0
- package/dist/src/cron/heartbeat-runner.test.js.map +1 -0
- package/dist/src/cron/heartbeat-scheduler.d.ts +26 -0
- package/dist/src/cron/heartbeat-scheduler.js +54 -0
- package/dist/src/cron/heartbeat-scheduler.js.map +1 -0
- package/dist/src/cron/heartbeat-scheduler.test.d.ts +6 -0
- package/dist/src/cron/heartbeat-scheduler.test.js +61 -0
- package/dist/src/cron/heartbeat-scheduler.test.js.map +1 -0
- package/dist/src/index.d.ts +24 -0
- package/dist/src/index.js +118 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/middleware/auth.d.ts +40 -0
- package/dist/src/middleware/auth.js +135 -0
- package/dist/src/middleware/auth.js.map +1 -0
- package/dist/src/middleware/auth.test.d.ts +6 -0
- package/dist/src/middleware/auth.test.js +268 -0
- package/dist/src/middleware/auth.test.js.map +1 -0
- package/dist/src/middleware/error-handler.d.ts +20 -0
- package/dist/src/middleware/error-handler.js +48 -0
- package/dist/src/middleware/error-handler.js.map +1 -0
- package/dist/src/middleware/error-handler.test.d.ts +6 -0
- package/dist/src/middleware/error-handler.test.js +68 -0
- package/dist/src/middleware/error-handler.test.js.map +1 -0
- package/dist/src/middleware/request-validation.d.ts +13 -0
- package/dist/src/middleware/request-validation.js +26 -0
- package/dist/src/middleware/request-validation.js.map +1 -0
- package/dist/src/middleware/request-validation.test.d.ts +6 -0
- package/dist/src/middleware/request-validation.test.js +57 -0
- package/dist/src/middleware/request-validation.test.js.map +1 -0
- package/dist/src/output/email-output.d.ts +10 -0
- package/dist/src/output/email-output.js +12 -0
- package/dist/src/output/email-output.js.map +1 -0
- package/dist/src/output/output-router.d.ts +12 -0
- package/dist/src/output/output-router.js +36 -0
- package/dist/src/output/output-router.js.map +1 -0
- package/dist/src/output/output-router.test.d.ts +6 -0
- package/dist/src/output/output-router.test.js +132 -0
- package/dist/src/output/output-router.test.js.map +1 -0
- package/dist/src/output/slack-output.d.ts +10 -0
- package/dist/src/output/slack-output.js +54 -0
- package/dist/src/output/slack-output.js.map +1 -0
- package/dist/src/output/webhook-output.d.ts +10 -0
- package/dist/src/output/webhook-output.js +25 -0
- package/dist/src/output/webhook-output.js.map +1 -0
- package/dist/src/routes/ai-stream.d.ts +159 -0
- package/dist/src/routes/ai-stream.js +309 -0
- package/dist/src/routes/ai-stream.js.map +1 -0
- package/dist/src/routes/ai-stream.test.d.ts +6 -0
- package/dist/src/routes/ai-stream.test.js +586 -0
- package/dist/src/routes/ai-stream.test.js.map +1 -0
- package/dist/src/routes/ask-user-response.d.ts +30 -0
- package/dist/src/routes/ask-user-response.js +61 -0
- package/dist/src/routes/ask-user-response.js.map +1 -0
- package/dist/src/routes/ask-user-response.test.d.ts +6 -0
- package/dist/src/routes/ask-user-response.test.js +88 -0
- package/dist/src/routes/ask-user-response.test.js.map +1 -0
- package/dist/src/routes/chat-stream.d.ts +14 -0
- package/dist/src/routes/chat-stream.js +84 -0
- package/dist/src/routes/chat-stream.js.map +1 -0
- package/dist/src/routes/chat-stream.test.d.ts +6 -0
- package/dist/src/routes/chat-stream.test.js +155 -0
- package/dist/src/routes/chat-stream.test.js.map +1 -0
- package/dist/src/routes/chat.d.ts +13 -0
- package/dist/src/routes/chat.js +55 -0
- package/dist/src/routes/chat.js.map +1 -0
- package/dist/src/routes/chat.test.d.ts +6 -0
- package/dist/src/routes/chat.test.js +99 -0
- package/dist/src/routes/chat.test.js.map +1 -0
- package/dist/src/routes/health.d.ts +13 -0
- package/dist/src/routes/health.js +23 -0
- package/dist/src/routes/health.js.map +1 -0
- package/dist/src/routes/health.test.d.ts +6 -0
- package/dist/src/routes/health.test.js +45 -0
- package/dist/src/routes/health.test.js.map +1 -0
- package/dist/src/routes/sessions.d.ts +14 -0
- package/dist/src/routes/sessions.js +82 -0
- package/dist/src/routes/sessions.js.map +1 -0
- package/dist/src/routes/webhooks.d.ts +13 -0
- package/dist/src/routes/webhooks.js +43 -0
- package/dist/src/routes/webhooks.js.map +1 -0
- package/dist/src/routes/webhooks.test.d.ts +6 -0
- package/dist/src/routes/webhooks.test.js +80 -0
- package/dist/src/routes/webhooks.test.js.map +1 -0
- package/dist/src/routes/widget-actions.d.ts +49 -0
- package/dist/src/routes/widget-actions.js +78 -0
- package/dist/src/routes/widget-actions.js.map +1 -0
- package/dist/src/server.d.ts +31 -0
- package/dist/src/server.js +129 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/server.test.d.ts +6 -0
- package/dist/src/server.test.js +153 -0
- package/dist/src/server.test.js.map +1 -0
- package/dist/src/session/history-converter.d.ts +21 -0
- package/dist/src/session/history-converter.js +59 -0
- package/dist/src/session/history-converter.js.map +1 -0
- package/dist/src/session/history-converter.test.d.ts +6 -0
- package/dist/src/session/history-converter.test.js +130 -0
- package/dist/src/session/history-converter.test.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +117 -0
- package/dist/src/session/session-manager.js +480 -0
- package/dist/src/session/session-manager.js.map +1 -0
- package/dist/src/session/session-manager.test.d.ts +6 -0
- package/dist/src/session/session-manager.test.js +586 -0
- package/dist/src/session/session-manager.test.js.map +1 -0
- package/dist/src/session/session-runner.d.ts +30 -0
- package/dist/src/session/session-runner.js +771 -0
- package/dist/src/session/session-runner.js.map +1 -0
- package/dist/src/session/session-runner.test.d.ts +6 -0
- package/dist/src/session/session-runner.test.js +842 -0
- package/dist/src/session/session-runner.test.js.map +1 -0
- package/dist/src/stores/index.d.ts +8 -0
- package/dist/src/stores/index.js +9 -0
- package/dist/src/stores/index.js.map +1 -0
- package/dist/src/stores/key-resolver.d.ts +21 -0
- package/dist/src/stores/key-resolver.js +30 -0
- package/dist/src/stores/key-resolver.js.map +1 -0
- package/dist/src/stores/key-resolver.test.d.ts +6 -0
- package/dist/src/stores/key-resolver.test.js +31 -0
- package/dist/src/stores/key-resolver.test.js.map +1 -0
- package/dist/src/stores/pglite-store-backend.d.ts +36 -0
- package/dist/src/stores/pglite-store-backend.js +227 -0
- package/dist/src/stores/pglite-store-backend.js.map +1 -0
- package/dist/src/stores/pglite-store-backend.test.d.ts +6 -0
- package/dist/src/stores/pglite-store-backend.test.js +150 -0
- package/dist/src/stores/pglite-store-backend.test.js.map +1 -0
- package/dist/src/stores/ttl-resolver.d.ts +24 -0
- package/dist/src/stores/ttl-resolver.js +64 -0
- package/dist/src/stores/ttl-resolver.js.map +1 -0
- package/dist/src/stores/ttl-resolver.test.d.ts +6 -0
- package/dist/src/stores/ttl-resolver.test.js +68 -0
- package/dist/src/stores/ttl-resolver.test.js.map +1 -0
- package/dist/src/types.d.ts +227 -0
- package/dist/src/types.js +50 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/types.test.d.ts +6 -0
- package/dist/src/types.test.js +68 -0
- package/dist/src/types.test.js.map +1 -0
- package/dist/src/utils/jwt-verify.d.ts +20 -0
- package/dist/src/utils/jwt-verify.js +34 -0
- package/dist/src/utils/jwt-verify.js.map +1 -0
- package/dist/src/utils/jwt-verify.test.d.ts +6 -0
- package/dist/src/utils/jwt-verify.test.js +156 -0
- package/dist/src/utils/jwt-verify.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { Router } from 'express';
|
|
7
|
+
import { verifyHmacSignature } from '../proactive/delivery.js';
|
|
8
|
+
/**
|
|
9
|
+
* Creates routes for receiving webhook events from external systems.
|
|
10
|
+
*
|
|
11
|
+
* POST /webhooks/:name — trigger a webhook-based automation
|
|
12
|
+
*/
|
|
13
|
+
export function createWebhookRouter(options) {
|
|
14
|
+
const router = Router();
|
|
15
|
+
router.post('/webhooks/:name', async (req, res) => {
|
|
16
|
+
const automationName = req.params['name'] ?? '';
|
|
17
|
+
// HMAC verification
|
|
18
|
+
if (options.webhookSecret) {
|
|
19
|
+
const signature = req.headers['x-amodal-signature'];
|
|
20
|
+
if (typeof signature !== 'string') {
|
|
21
|
+
res.status(401).json({ error: 'Missing X-Amodal-Signature header' });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const rawBody = typeof req.body === 'string'
|
|
25
|
+
? req.body
|
|
26
|
+
: JSON.stringify(req.body);
|
|
27
|
+
if (!verifyHmacSignature(rawBody, signature, options.webhookSecret)) {
|
|
28
|
+
res.status(401).json({ error: 'Invalid webhook signature' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Express body parsing
|
|
33
|
+
const payload = (req.body ?? {});
|
|
34
|
+
try {
|
|
35
|
+
const result = await options.runner.handleWebhook(automationName, payload);
|
|
36
|
+
if (!result.matched) {
|
|
37
|
+
res.status(404).json({ error: result.error });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (result.error) {
|
|
41
|
+
res.status(500).json({ error: result.error, matched: true });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
res.json({ status: 'accepted', automation: automationName });
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
48
|
+
res.status(500).json({ error: msg });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return router;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../../../src/agent/routes/webhooks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,MAAM,EAAC,MAAM,SAAS,CAAC;AAE/B,OAAO,EAAC,mBAAmB,EAAC,MAAM,0BAA0B,CAAC;AAQ7D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACnE,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAEhD,oBAAoB;QACpB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YACpD,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAClC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,mCAAmC,EAAC,CAAC,CAAC;gBACnE,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAC1C,CAAC,CAAC,GAAG,CAAC,IAAI;gBACV,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAE7B,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;gBACpE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,2BAA2B,EAAC,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;QACH,CAAC;QAED,+FAA+F;QAC/F,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;QAE5D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YAC3E,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAC,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAC,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,GAAG,EAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
7
|
+
import express from 'express';
|
|
8
|
+
import request from 'supertest';
|
|
9
|
+
import { createHmac } from 'node:crypto';
|
|
10
|
+
import { createWebhookRouter } from './webhooks.js';
|
|
11
|
+
function makeMockRunner(overrides) {
|
|
12
|
+
return {
|
|
13
|
+
start: vi.fn(),
|
|
14
|
+
stop: vi.fn(),
|
|
15
|
+
listAutomations: vi.fn().mockReturnValue([]),
|
|
16
|
+
handleWebhook: vi.fn().mockResolvedValue({ matched: true }),
|
|
17
|
+
triggerAutomation: vi.fn().mockResolvedValue({ success: true }),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function createApp(runner, webhookSecret) {
|
|
22
|
+
const app = express();
|
|
23
|
+
app.use(express.json());
|
|
24
|
+
app.use(createWebhookRouter({ runner, webhookSecret }));
|
|
25
|
+
return app;
|
|
26
|
+
}
|
|
27
|
+
describe('repo-webhooks routes', () => {
|
|
28
|
+
it('should accept valid webhook', async () => {
|
|
29
|
+
const runner = makeMockRunner();
|
|
30
|
+
const app = createApp(runner);
|
|
31
|
+
const res = await request(app)
|
|
32
|
+
.post('/webhooks/my-auto')
|
|
33
|
+
.send({ alert: 'cpu-high' });
|
|
34
|
+
expect(res.status).toBe(200);
|
|
35
|
+
expect(res.body.status).toBe('accepted');
|
|
36
|
+
expect(runner.handleWebhook).toHaveBeenCalledWith('my-auto', { alert: 'cpu-high' });
|
|
37
|
+
});
|
|
38
|
+
it('should return 404 for unmatched automation', async () => {
|
|
39
|
+
const runner = makeMockRunner({
|
|
40
|
+
handleWebhook: vi.fn().mockResolvedValue({ matched: false, error: 'Not found' }),
|
|
41
|
+
});
|
|
42
|
+
const app = createApp(runner);
|
|
43
|
+
const res = await request(app)
|
|
44
|
+
.post('/webhooks/unknown')
|
|
45
|
+
.send({});
|
|
46
|
+
expect(res.status).toBe(404);
|
|
47
|
+
});
|
|
48
|
+
it('should return 500 on automation error', async () => {
|
|
49
|
+
const runner = makeMockRunner({
|
|
50
|
+
handleWebhook: vi.fn().mockResolvedValue({ matched: true, error: 'Exec failed' }),
|
|
51
|
+
});
|
|
52
|
+
const app = createApp(runner);
|
|
53
|
+
const res = await request(app)
|
|
54
|
+
.post('/webhooks/my-auto')
|
|
55
|
+
.send({});
|
|
56
|
+
expect(res.status).toBe(500);
|
|
57
|
+
expect(res.body.error).toBe('Exec failed');
|
|
58
|
+
});
|
|
59
|
+
it('should verify HMAC signature when secret is set', async () => {
|
|
60
|
+
const runner = makeMockRunner();
|
|
61
|
+
const secret = 'my-secret';
|
|
62
|
+
const app = createApp(runner, secret);
|
|
63
|
+
const body = JSON.stringify({ alert: 'test' });
|
|
64
|
+
const sig = `sha256=${createHmac('sha256', secret).update(body).digest('hex')}`;
|
|
65
|
+
const res = await request(app)
|
|
66
|
+
.post('/webhooks/my-auto')
|
|
67
|
+
.set('Content-Type', 'application/json')
|
|
68
|
+
.set('X-Amodal-Signature', sig)
|
|
69
|
+
.send(body);
|
|
70
|
+
expect(res.status).toBe(200);
|
|
71
|
+
});
|
|
72
|
+
it('should reject missing signature when secret is set', async () => {
|
|
73
|
+
const runner = makeMockRunner();
|
|
74
|
+
const app = createApp(runner, 'my-secret');
|
|
75
|
+
const res = await request(app)
|
|
76
|
+
.post('/webhooks/my-auto')
|
|
77
|
+
.send({ alert: 'test' });
|
|
78
|
+
expect(res.status).toBe(401);
|
|
79
|
+
expect(res.body.error).toContain('Missing');
|
|
80
|
+
});
|
|
81
|
+
it('should reject invalid signature', async () => {
|
|
82
|
+
const runner = makeMockRunner();
|
|
83
|
+
const app = createApp(runner, 'my-secret');
|
|
84
|
+
const res = await request(app)
|
|
85
|
+
.post('/webhooks/my-auto')
|
|
86
|
+
.set('X-Amodal-Signature', 'sha256=invalid')
|
|
87
|
+
.send({ alert: 'test' });
|
|
88
|
+
expect(res.status).toBe(401);
|
|
89
|
+
expect(res.body.error).toContain('Invalid');
|
|
90
|
+
});
|
|
91
|
+
it('should not require signature when no secret configured', async () => {
|
|
92
|
+
const runner = makeMockRunner();
|
|
93
|
+
const app = createApp(runner);
|
|
94
|
+
const res = await request(app)
|
|
95
|
+
.post('/webhooks/my-auto')
|
|
96
|
+
.send({});
|
|
97
|
+
expect(res.status).toBe(200);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=webhooks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.test.js","sourceRoot":"","sources":["../../../../src/agent/routes/webhooks.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { AmodalRepo } from '@amodalai/core';
|
|
7
|
+
import type { RuntimeTelemetryEvent, CustomToolExecutor, CustomShellExecutor, StoreBackend } from '@amodalai/core';
|
|
8
|
+
import type { AgentSession } from './agent-types.js';
|
|
9
|
+
export type TelemetrySink = (event: RuntimeTelemetryEvent) => void;
|
|
10
|
+
export interface AgentSessionManagerOptions {
|
|
11
|
+
ttlMs?: number;
|
|
12
|
+
telemetrySink?: TelemetrySink;
|
|
13
|
+
toolExecutor?: CustomToolExecutor;
|
|
14
|
+
shellExecutor?: CustomShellExecutor;
|
|
15
|
+
storeBackend?: StoreBackend;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Manages repo-based sessions with TTL eviction.
|
|
19
|
+
*/
|
|
20
|
+
export declare class AgentSessionManager {
|
|
21
|
+
private repo;
|
|
22
|
+
private readonly sessions;
|
|
23
|
+
private readonly ttlMs;
|
|
24
|
+
private readonly telemetrySink?;
|
|
25
|
+
private readonly toolExecutor?;
|
|
26
|
+
private readonly shellExecutor?;
|
|
27
|
+
private readonly storeBackend?;
|
|
28
|
+
private platformTelemetry?;
|
|
29
|
+
private cleanupTimer;
|
|
30
|
+
constructor(repo: AmodalRepo, options?: AgentSessionManagerOptions);
|
|
31
|
+
get size(): number;
|
|
32
|
+
/**
|
|
33
|
+
* Create a new session, optionally fetching user context for role extraction.
|
|
34
|
+
*/
|
|
35
|
+
create(tenantId: string, tenantToken?: string): Promise<AgentSession>;
|
|
36
|
+
/**
|
|
37
|
+
* Create a session from a specific repo (e.g., enriched with tenant credentials).
|
|
38
|
+
* Used by hosted runtime when tenant-specific config is fetched from the platform API.
|
|
39
|
+
*/
|
|
40
|
+
createFromRepo(repo: AmodalRepo, tenantId: string): Promise<AgentSession>;
|
|
41
|
+
/**
|
|
42
|
+
* Re-register a session under a different ID (e.g., restoring original ID on hydration).
|
|
43
|
+
*/
|
|
44
|
+
reregister(session: AgentSession, newId: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Get an existing session by ID.
|
|
47
|
+
*/
|
|
48
|
+
get(sessionId: string): AgentSession | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Destroy a session.
|
|
51
|
+
*/
|
|
52
|
+
destroy(sessionId: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Remove sessions that have been idle longer than TTL.
|
|
55
|
+
* Returns the number of sessions removed.
|
|
56
|
+
*/
|
|
57
|
+
cleanup(): number;
|
|
58
|
+
/**
|
|
59
|
+
* Update the repo for all new sessions (hot reload).
|
|
60
|
+
* Existing sessions keep their old repo.
|
|
61
|
+
*/
|
|
62
|
+
updateRepo(repo: AmodalRepo): void;
|
|
63
|
+
/**
|
|
64
|
+
* Shut down all sessions and stop the cleanup timer.
|
|
65
|
+
*/
|
|
66
|
+
shutdown(): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Initialize MCP servers for a session if the repo has MCP config.
|
|
69
|
+
*/
|
|
70
|
+
private initMcp;
|
|
71
|
+
private getConnectionsMap;
|
|
72
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { setupSession, PlanModeManager, prepareExploreConfig, extractRoles, buildConnectionsMap, PlatformTelemetrySink, McpManager, } from '@amodalai/core';
|
|
8
|
+
import { fetchUserContext } from './user-context-fetcher.js';
|
|
9
|
+
const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
10
|
+
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
11
|
+
/**
|
|
12
|
+
* Manages repo-based sessions with TTL eviction.
|
|
13
|
+
*/
|
|
14
|
+
export class AgentSessionManager {
|
|
15
|
+
repo;
|
|
16
|
+
sessions = new Map();
|
|
17
|
+
ttlMs;
|
|
18
|
+
telemetrySink;
|
|
19
|
+
toolExecutor;
|
|
20
|
+
shellExecutor;
|
|
21
|
+
storeBackend;
|
|
22
|
+
platformTelemetry;
|
|
23
|
+
cleanupTimer = null;
|
|
24
|
+
constructor(repo, options) {
|
|
25
|
+
this.repo = repo;
|
|
26
|
+
this.ttlMs = options?.ttlMs ?? DEFAULT_TTL_MS;
|
|
27
|
+
// If platform config is set and no explicit sink provided, create a platform sink
|
|
28
|
+
if (!options?.telemetrySink && repo.config.platform?.apiKey) {
|
|
29
|
+
const platformUrl = process.env['PLATFORM_API_URL'] ?? 'http://localhost:4000';
|
|
30
|
+
this.platformTelemetry = new PlatformTelemetrySink(platformUrl, repo.config.platform.apiKey);
|
|
31
|
+
this.telemetrySink = this.platformTelemetry.sink();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.telemetrySink = options?.telemetrySink;
|
|
35
|
+
}
|
|
36
|
+
this.toolExecutor = options?.toolExecutor;
|
|
37
|
+
this.shellExecutor = options?.shellExecutor;
|
|
38
|
+
this.storeBackend = options?.storeBackend;
|
|
39
|
+
this.cleanupTimer = setInterval(() => {
|
|
40
|
+
this.cleanup();
|
|
41
|
+
}, CLEANUP_INTERVAL_MS);
|
|
42
|
+
// Unref so the timer doesn't keep the process alive
|
|
43
|
+
if (this.cleanupTimer && typeof this.cleanupTimer === 'object' && 'unref' in this.cleanupTimer) {
|
|
44
|
+
this.cleanupTimer.unref();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
get size() {
|
|
48
|
+
return this.sessions.size;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a new session, optionally fetching user context for role extraction.
|
|
52
|
+
*/
|
|
53
|
+
async create(tenantId, tenantToken) {
|
|
54
|
+
let userRoles = [];
|
|
55
|
+
// Fetch user context if configured
|
|
56
|
+
if (this.repo.config.userContext && tenantToken) {
|
|
57
|
+
try {
|
|
58
|
+
const connectionsMap = this.getConnectionsMap();
|
|
59
|
+
const userData = await fetchUserContext(this.repo, tenantToken, connectionsMap);
|
|
60
|
+
userRoles = extractRoles(userData);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Degraded but functional — empty roles
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const runtime = setupSession({
|
|
67
|
+
repo: this.repo,
|
|
68
|
+
userId: tenantId,
|
|
69
|
+
userRoles,
|
|
70
|
+
isDelegated: false,
|
|
71
|
+
telemetrySink: this.telemetrySink,
|
|
72
|
+
});
|
|
73
|
+
const planModeManager = new PlanModeManager();
|
|
74
|
+
const exploreConfig = prepareExploreConfig(runtime);
|
|
75
|
+
const session = {
|
|
76
|
+
id: randomUUID(),
|
|
77
|
+
runtime,
|
|
78
|
+
tenantId,
|
|
79
|
+
conversationHistory: [],
|
|
80
|
+
createdAt: Date.now(),
|
|
81
|
+
lastAccessedAt: Date.now(),
|
|
82
|
+
planModeManager,
|
|
83
|
+
exploreConfig,
|
|
84
|
+
toolExecutor: this.toolExecutor,
|
|
85
|
+
shellExecutor: this.shellExecutor,
|
|
86
|
+
storeBackend: this.storeBackend,
|
|
87
|
+
};
|
|
88
|
+
// Start MCP servers if configured (non-blocking)
|
|
89
|
+
await this.initMcp(session, this.repo);
|
|
90
|
+
this.sessions.set(session.id, session);
|
|
91
|
+
return session;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create a session from a specific repo (e.g., enriched with tenant credentials).
|
|
95
|
+
* Used by hosted runtime when tenant-specific config is fetched from the platform API.
|
|
96
|
+
*/
|
|
97
|
+
async createFromRepo(repo, tenantId) {
|
|
98
|
+
const runtime = setupSession({
|
|
99
|
+
repo,
|
|
100
|
+
userId: tenantId,
|
|
101
|
+
userRoles: [],
|
|
102
|
+
isDelegated: false,
|
|
103
|
+
telemetrySink: this.telemetrySink,
|
|
104
|
+
});
|
|
105
|
+
const planModeManager = new PlanModeManager();
|
|
106
|
+
const exploreConfig = prepareExploreConfig(runtime);
|
|
107
|
+
const session = {
|
|
108
|
+
id: randomUUID(),
|
|
109
|
+
runtime,
|
|
110
|
+
tenantId,
|
|
111
|
+
conversationHistory: [],
|
|
112
|
+
createdAt: Date.now(),
|
|
113
|
+
lastAccessedAt: Date.now(),
|
|
114
|
+
planModeManager,
|
|
115
|
+
exploreConfig,
|
|
116
|
+
toolExecutor: this.toolExecutor,
|
|
117
|
+
shellExecutor: this.shellExecutor,
|
|
118
|
+
storeBackend: this.storeBackend,
|
|
119
|
+
};
|
|
120
|
+
// Start MCP servers if configured (non-blocking)
|
|
121
|
+
await this.initMcp(session, repo);
|
|
122
|
+
this.sessions.set(session.id, session);
|
|
123
|
+
return session;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Re-register a session under a different ID (e.g., restoring original ID on hydration).
|
|
127
|
+
*/
|
|
128
|
+
reregister(session, newId) {
|
|
129
|
+
this.sessions.delete(session.id);
|
|
130
|
+
session.id = newId;
|
|
131
|
+
this.sessions.set(newId, session);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get an existing session by ID.
|
|
135
|
+
*/
|
|
136
|
+
get(sessionId) {
|
|
137
|
+
const session = this.sessions.get(sessionId);
|
|
138
|
+
if (session) {
|
|
139
|
+
session.lastAccessedAt = Date.now();
|
|
140
|
+
}
|
|
141
|
+
return session;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Destroy a session.
|
|
145
|
+
*/
|
|
146
|
+
destroy(sessionId) {
|
|
147
|
+
this.sessions.delete(sessionId);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Remove sessions that have been idle longer than TTL.
|
|
151
|
+
* Returns the number of sessions removed.
|
|
152
|
+
*/
|
|
153
|
+
cleanup() {
|
|
154
|
+
const now = Date.now();
|
|
155
|
+
let removed = 0;
|
|
156
|
+
for (const [id, session] of this.sessions) {
|
|
157
|
+
if (now - session.lastAccessedAt > this.ttlMs) {
|
|
158
|
+
this.sessions.delete(id);
|
|
159
|
+
removed++;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return removed;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Update the repo for all new sessions (hot reload).
|
|
166
|
+
* Existing sessions keep their old repo.
|
|
167
|
+
*/
|
|
168
|
+
updateRepo(repo) {
|
|
169
|
+
this.repo = repo;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Shut down all sessions and stop the cleanup timer.
|
|
173
|
+
*/
|
|
174
|
+
async shutdown() {
|
|
175
|
+
if (this.cleanupTimer) {
|
|
176
|
+
clearInterval(this.cleanupTimer);
|
|
177
|
+
this.cleanupTimer = null;
|
|
178
|
+
}
|
|
179
|
+
if (this.platformTelemetry) {
|
|
180
|
+
await this.platformTelemetry.destroy();
|
|
181
|
+
this.platformTelemetry = undefined;
|
|
182
|
+
}
|
|
183
|
+
// Shutdown MCP managers for all sessions
|
|
184
|
+
for (const session of this.sessions.values()) {
|
|
185
|
+
if (session.mcpManager) {
|
|
186
|
+
await session.mcpManager.shutdown().catch(() => { });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.sessions.clear();
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Initialize MCP servers for a session if the repo has MCP config.
|
|
193
|
+
*/
|
|
194
|
+
async initMcp(session, repo) {
|
|
195
|
+
if (!repo.mcpServers || Object.keys(repo.mcpServers).length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const manager = new McpManager();
|
|
199
|
+
try {
|
|
200
|
+
await manager.startServers(repo.mcpServers);
|
|
201
|
+
if (manager.connectedCount > 0) {
|
|
202
|
+
session.mcpManager = manager;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
207
|
+
process.stderr.write(`[SESSION] MCP initialization failed: ${msg}\n`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
getConnectionsMap() {
|
|
211
|
+
return buildConnectionsMap(this.repo.connections);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/agent/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { AgentSessionManager } from './session-manager.js';
|
|
8
|
+
// Mock setupSession and friends
|
|
9
|
+
vi.mock('@amodalai/core', async (importOriginal) => {
|
|
10
|
+
const actual = await importOriginal();
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
setupSession: vi.fn(() => ({
|
|
14
|
+
repo: makeRepo(),
|
|
15
|
+
scrubTracker: {},
|
|
16
|
+
fieldScrubber: { scrub: vi.fn() },
|
|
17
|
+
outputGuard: { guard: vi.fn(() => ({ output: '', modified: false, blocked: false, findings: [] })) },
|
|
18
|
+
actionGate: { evaluate: vi.fn(() => ({ decision: 'allow', escalated: false, endpointPath: '' })) },
|
|
19
|
+
contextCompiler: {},
|
|
20
|
+
compiledContext: { systemPrompt: 'test', tokenUsage: { total: 100000, used: 500, remaining: 99500, sectionBreakdown: {} }, sections: [] },
|
|
21
|
+
exploreContext: { systemPrompt: 'explore', tokenUsage: { total: 100000, used: 300, remaining: 99700, sectionBreakdown: {} }, sections: [] },
|
|
22
|
+
outputPipeline: { process: vi.fn((t) => ({ output: t, modified: false, blocked: false, findings: [] })), createStreamProcessor: vi.fn() },
|
|
23
|
+
telemetry: { logScrub: vi.fn(), logGuard: vi.fn(), logGate: vi.fn(), logExplore: vi.fn() },
|
|
24
|
+
connectionsMap: {},
|
|
25
|
+
userRoles: [],
|
|
26
|
+
sessionId: 'test-session',
|
|
27
|
+
isDelegated: false,
|
|
28
|
+
})),
|
|
29
|
+
prepareExploreConfig: vi.fn(() => ({
|
|
30
|
+
systemPrompt: 'explore',
|
|
31
|
+
model: { provider: 'anthropic', model: 'test' },
|
|
32
|
+
connectionsMap: {},
|
|
33
|
+
readOnly: true,
|
|
34
|
+
maxTurns: 10,
|
|
35
|
+
maxDepth: 2,
|
|
36
|
+
})),
|
|
37
|
+
PlanModeManager: vi.fn(() => ({
|
|
38
|
+
isActive: vi.fn(() => false),
|
|
39
|
+
getApprovedPlan: vi.fn(() => null),
|
|
40
|
+
getReason: vi.fn(() => null),
|
|
41
|
+
enter: vi.fn(),
|
|
42
|
+
approve: vi.fn(),
|
|
43
|
+
exit: vi.fn(),
|
|
44
|
+
getPlanningReminder: vi.fn(() => null),
|
|
45
|
+
getApprovedPlanContext: vi.fn(() => null),
|
|
46
|
+
})),
|
|
47
|
+
extractRoles: vi.fn(() => []),
|
|
48
|
+
buildConnectionsMap: vi.fn(() => ({})),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
function makeRepo() {
|
|
52
|
+
return {
|
|
53
|
+
source: 'local',
|
|
54
|
+
origin: '/test',
|
|
55
|
+
config: {
|
|
56
|
+
name: 'test',
|
|
57
|
+
version: '1.0.0',
|
|
58
|
+
models: {
|
|
59
|
+
main: { provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
connections: new Map(),
|
|
63
|
+
skills: [],
|
|
64
|
+
agents: { subagents: [] },
|
|
65
|
+
automations: [],
|
|
66
|
+
knowledge: [],
|
|
67
|
+
evals: [],
|
|
68
|
+
tools: [],
|
|
69
|
+
stores: [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
describe('AgentSessionManager', () => {
|
|
73
|
+
let manager;
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
vi.useFakeTimers();
|
|
76
|
+
manager = new AgentSessionManager(makeRepo(), { ttlMs: 1000 });
|
|
77
|
+
});
|
|
78
|
+
afterEach(async () => {
|
|
79
|
+
await manager.shutdown();
|
|
80
|
+
vi.useRealTimers();
|
|
81
|
+
});
|
|
82
|
+
it('should create a session', async () => {
|
|
83
|
+
const session = await manager.create('tenant-1');
|
|
84
|
+
expect(session.id).toBeTruthy();
|
|
85
|
+
expect(session.tenantId).toBe('tenant-1');
|
|
86
|
+
expect(session.conversationHistory).toEqual([]);
|
|
87
|
+
expect(manager.size).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
it('should get an existing session', async () => {
|
|
90
|
+
const session = await manager.create('tenant-1');
|
|
91
|
+
const found = manager.get(session.id);
|
|
92
|
+
expect(found).toBeDefined();
|
|
93
|
+
expect(found?.id).toBe(session.id);
|
|
94
|
+
});
|
|
95
|
+
it('should return undefined for unknown session', () => {
|
|
96
|
+
expect(manager.get('nonexistent')).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
it('should destroy a session', async () => {
|
|
99
|
+
const session = await manager.create('tenant-1');
|
|
100
|
+
manager.destroy(session.id);
|
|
101
|
+
expect(manager.get(session.id)).toBeUndefined();
|
|
102
|
+
expect(manager.size).toBe(0);
|
|
103
|
+
});
|
|
104
|
+
it('should clean up expired sessions', async () => {
|
|
105
|
+
await manager.create('tenant-1');
|
|
106
|
+
expect(manager.size).toBe(1);
|
|
107
|
+
vi.advanceTimersByTime(1500);
|
|
108
|
+
const removed = manager.cleanup();
|
|
109
|
+
expect(removed).toBe(1);
|
|
110
|
+
expect(manager.size).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
it('should not clean up active sessions', async () => {
|
|
113
|
+
const session = await manager.create('tenant-1');
|
|
114
|
+
vi.advanceTimersByTime(500);
|
|
115
|
+
// Access the session to update lastAccessedAt
|
|
116
|
+
manager.get(session.id);
|
|
117
|
+
vi.advanceTimersByTime(600);
|
|
118
|
+
const removed = manager.cleanup();
|
|
119
|
+
expect(removed).toBe(0);
|
|
120
|
+
expect(manager.size).toBe(1);
|
|
121
|
+
});
|
|
122
|
+
it('should update repo for new sessions', async () => {
|
|
123
|
+
const newRepo = makeRepo();
|
|
124
|
+
newRepo.config.name = 'updated';
|
|
125
|
+
manager.updateRepo(newRepo);
|
|
126
|
+
// The next created session should use the new repo
|
|
127
|
+
const session = await manager.create('tenant-1');
|
|
128
|
+
expect(session).toBeDefined();
|
|
129
|
+
});
|
|
130
|
+
it('should shutdown cleanly', async () => {
|
|
131
|
+
await manager.create('tenant-1');
|
|
132
|
+
await manager.create('tenant-2');
|
|
133
|
+
expect(manager.size).toBe(2);
|
|
134
|
+
await manager.shutdown();
|
|
135
|
+
expect(manager.size).toBe(0);
|
|
136
|
+
});
|
|
137
|
+
it('should update lastAccessedAt on get', async () => {
|
|
138
|
+
const session = await manager.create('tenant-1');
|
|
139
|
+
const initialAccess = session.lastAccessedAt;
|
|
140
|
+
vi.advanceTimersByTime(100);
|
|
141
|
+
manager.get(session.id);
|
|
142
|
+
expect(session.lastAccessedAt).toBeGreaterThan(initialAccess);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=session-manager.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.test.js","sourceRoot":"","sources":["../../../src/agent/session-manager.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { CustomShellExecutor } from '@amodalai/core';
|
|
7
|
+
/**
|
|
8
|
+
* Executes shell commands locally via child_process.
|
|
9
|
+
*/
|
|
10
|
+
export declare class LocalShellExecutor implements CustomShellExecutor {
|
|
11
|
+
exec(command: string, timeout: number, signal: AbortSignal): Promise<{
|
|
12
|
+
stdout: string;
|
|
13
|
+
stderr: string;
|
|
14
|
+
exitCode: number;
|
|
15
|
+
}>;
|
|
16
|
+
}
|