@alneas/eaos-server 0.0.0-stage → 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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.ja.md +39 -0
- package/README.md +40 -2
- package/dist/agentic/agent-jobs.d.ts +329 -0
- package/dist/agentic/agent-jobs.js +15 -0
- package/dist/agentic/agent-jobs.js.map +1 -0
- package/dist/agentic/agent-node.d.ts +40 -0
- package/dist/agentic/agent-node.js +7 -0
- package/dist/agentic/agent-node.js.map +1 -0
- package/dist/agentic/agent-runner.d.ts +33 -0
- package/dist/agentic/agent-runner.js +7 -0
- package/dist/agentic/agent-runner.js.map +1 -0
- package/dist/agentic/ai-broker.d.ts +169 -0
- package/dist/agentic/ai-broker.js +8 -0
- package/dist/agentic/ai-broker.js.map +1 -0
- package/dist/agentic/atomic-checkpointer.d.ts +137 -0
- package/dist/agentic/atomic-checkpointer.js +9 -0
- package/dist/agentic/atomic-checkpointer.js.map +1 -0
- package/dist/agentic/autonomous-worker.d.ts +95 -0
- package/dist/agentic/autonomous-worker.js +9 -0
- package/dist/agentic/autonomous-worker.js.map +1 -0
- package/dist/agentic/event-bridge.d.ts +344 -0
- package/dist/agentic/event-bridge.js +9 -0
- package/dist/agentic/event-bridge.js.map +1 -0
- package/dist/agentic/executor.d.ts +91 -0
- package/dist/agentic/executor.js +12 -0
- package/dist/agentic/executor.js.map +1 -0
- package/dist/agentic/graph-projection.d.ts +119 -0
- package/dist/agentic/graph-projection.js +7 -0
- package/dist/agentic/graph-projection.js.map +1 -0
- package/dist/agentic/graph.d.ts +524 -0
- package/dist/agentic/graph.js +7 -0
- package/dist/agentic/graph.js.map +1 -0
- package/dist/agentic/index.d.ts +24 -0
- package/dist/agentic/index.js +86 -0
- package/dist/agentic/index.js.map +1 -0
- package/dist/agentic/local-runner.d.ts +102 -0
- package/dist/agentic/local-runner.js +7 -0
- package/dist/agentic/local-runner.js.map +1 -0
- package/dist/agentic/remote-executor.d.ts +67 -0
- package/dist/agentic/remote-executor.js +8 -0
- package/dist/agentic/remote-executor.js.map +1 -0
- package/dist/agentic/types.d.ts +157 -0
- package/dist/agentic/types.js +7 -0
- package/dist/agentic/types.js.map +1 -0
- package/dist/agentic/workflow-catalog.d.ts +152 -0
- package/dist/agentic/workflow-catalog.js +7 -0
- package/dist/agentic/workflow-catalog.js.map +1 -0
- package/dist/agentic/workflow-compiler.d.ts +44 -0
- package/dist/agentic/workflow-compiler.js +8 -0
- package/dist/agentic/workflow-compiler.js.map +1 -0
- package/dist/agentic/workflow-coordinator.d.ts +170 -0
- package/dist/agentic/workflow-coordinator.js +8 -0
- package/dist/agentic/workflow-coordinator.js.map +1 -0
- package/dist/agentic/workflow-manager.d.ts +91 -0
- package/dist/agentic/workflow-manager.js +7 -0
- package/dist/agentic/workflow-manager.js.map +1 -0
- package/dist/agentic/workflow-registry.d.ts +334 -0
- package/dist/agentic/workflow-registry.js +7 -0
- package/dist/agentic/workflow-registry.js.map +1 -0
- package/dist/artifacts-CteHmCMI.d.ts +614 -0
- package/dist/auth-hooks/api-key.d.ts +75 -0
- package/dist/auth-hooks/api-key.js +7 -0
- package/dist/auth-hooks/api-key.js.map +1 -0
- package/dist/auth-hooks/index.d.ts +80 -0
- package/dist/auth-hooks/index.js +28 -0
- package/dist/auth-hooks/index.js.map +1 -0
- package/dist/browser-auth/account-memberships.d.ts +213 -0
- package/dist/browser-auth/account-memberships.js +13 -0
- package/dist/browser-auth/account-memberships.js.map +1 -0
- package/dist/browser-auth/index.d.ts +8 -0
- package/dist/browser-auth/index.js +59 -0
- package/dist/browser-auth/index.js.map +1 -0
- package/dist/browser-auth/kratos-session.d.ts +37 -0
- package/dist/browser-auth/kratos-session.js +7 -0
- package/dist/browser-auth/kratos-session.js.map +1 -0
- package/dist/browser-auth/platform-session.d.ts +120 -0
- package/dist/browser-auth/platform-session.js +7 -0
- package/dist/browser-auth/platform-session.js.map +1 -0
- package/dist/browser-auth/public-memberships.d.ts +22 -0
- package/dist/browser-auth/public-memberships.js +7 -0
- package/dist/browser-auth/public-memberships.js.map +1 -0
- package/dist/cache/index.d.ts +181 -0
- package/dist/cache/index.js +197 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/chunk-32746UQZ.js +131 -0
- package/dist/chunk-32746UQZ.js.map +1 -0
- package/dist/chunk-3NLSWP2Y.js +223 -0
- package/dist/chunk-3NLSWP2Y.js.map +1 -0
- package/dist/chunk-3P4XTTQH.js +346 -0
- package/dist/chunk-3P4XTTQH.js.map +1 -0
- package/dist/chunk-3VITSHN3.js +15 -0
- package/dist/chunk-3VITSHN3.js.map +1 -0
- package/dist/chunk-46UWPEW2.js +226 -0
- package/dist/chunk-46UWPEW2.js.map +1 -0
- package/dist/chunk-4VORUFKR.js +45 -0
- package/dist/chunk-4VORUFKR.js.map +1 -0
- package/dist/chunk-55D4ERWO.js +1 -0
- package/dist/chunk-55D4ERWO.js.map +1 -0
- package/dist/chunk-5TBBZIND.js +18 -0
- package/dist/chunk-5TBBZIND.js.map +1 -0
- package/dist/chunk-5WOZWPZA.js +211 -0
- package/dist/chunk-5WOZWPZA.js.map +1 -0
- package/dist/chunk-6CBVZ6CT.js +168 -0
- package/dist/chunk-6CBVZ6CT.js.map +1 -0
- package/dist/chunk-6KV5CJBW.js +221 -0
- package/dist/chunk-6KV5CJBW.js.map +1 -0
- package/dist/chunk-6XZXYNJR.js +74 -0
- package/dist/chunk-6XZXYNJR.js.map +1 -0
- package/dist/chunk-753HWDSC.js +222 -0
- package/dist/chunk-753HWDSC.js.map +1 -0
- package/dist/chunk-7AHSS7SO.js +81 -0
- package/dist/chunk-7AHSS7SO.js.map +1 -0
- package/dist/chunk-7FTJN4SA.js +1 -0
- package/dist/chunk-7FTJN4SA.js.map +1 -0
- package/dist/chunk-AVDMLTPF.js +24 -0
- package/dist/chunk-AVDMLTPF.js.map +1 -0
- package/dist/chunk-BKAHUEIT.js +95 -0
- package/dist/chunk-BKAHUEIT.js.map +1 -0
- package/dist/chunk-CJ7OFEF7.js +134 -0
- package/dist/chunk-CJ7OFEF7.js.map +1 -0
- package/dist/chunk-DHQGNP4T.js +770 -0
- package/dist/chunk-DHQGNP4T.js.map +1 -0
- package/dist/chunk-EJKWDZIU.js +111 -0
- package/dist/chunk-EJKWDZIU.js.map +1 -0
- package/dist/chunk-F3O7CELI.js +23 -0
- package/dist/chunk-F3O7CELI.js.map +1 -0
- package/dist/chunk-FD6VC2NA.js +66 -0
- package/dist/chunk-FD6VC2NA.js.map +1 -0
- package/dist/chunk-FRRRWAHO.js +14 -0
- package/dist/chunk-FRRRWAHO.js.map +1 -0
- package/dist/chunk-HCKAGLLB.js +17 -0
- package/dist/chunk-HCKAGLLB.js.map +1 -0
- package/dist/chunk-HVC7FG6B.js +191 -0
- package/dist/chunk-HVC7FG6B.js.map +1 -0
- package/dist/chunk-HXFVOR7Z.js +496 -0
- package/dist/chunk-HXFVOR7Z.js.map +1 -0
- package/dist/chunk-IJWYS2PF.js +122 -0
- package/dist/chunk-IJWYS2PF.js.map +1 -0
- package/dist/chunk-INCHXAFK.js +1395 -0
- package/dist/chunk-INCHXAFK.js.map +1 -0
- package/dist/chunk-JT6BRGQM.js +1 -0
- package/dist/chunk-JT6BRGQM.js.map +1 -0
- package/dist/chunk-L6PMAEMB.js +115 -0
- package/dist/chunk-L6PMAEMB.js.map +1 -0
- package/dist/chunk-LOUGW6MU.js +126 -0
- package/dist/chunk-LOUGW6MU.js.map +1 -0
- package/dist/chunk-LYPZPE53.js +129 -0
- package/dist/chunk-LYPZPE53.js.map +1 -0
- package/dist/chunk-NNCCN7JP.js +186 -0
- package/dist/chunk-NNCCN7JP.js.map +1 -0
- package/dist/chunk-PBC3MZUL.js +212 -0
- package/dist/chunk-PBC3MZUL.js.map +1 -0
- package/dist/chunk-R4ADP2DC.js +20 -0
- package/dist/chunk-R4ADP2DC.js.map +1 -0
- package/dist/chunk-RHN4Y6VS.js +44 -0
- package/dist/chunk-RHN4Y6VS.js.map +1 -0
- package/dist/chunk-THYUY5ZZ.js +204 -0
- package/dist/chunk-THYUY5ZZ.js.map +1 -0
- package/dist/chunk-UGNTCOVR.js +28 -0
- package/dist/chunk-UGNTCOVR.js.map +1 -0
- package/dist/chunk-VC2PCK2S.js +31 -0
- package/dist/chunk-VC2PCK2S.js.map +1 -0
- package/dist/chunk-VF5YDZJC.js +305 -0
- package/dist/chunk-VF5YDZJC.js.map +1 -0
- package/dist/chunk-VISRDVTK.js +675 -0
- package/dist/chunk-VISRDVTK.js.map +1 -0
- package/dist/chunk-W6O3LVBY.js +102 -0
- package/dist/chunk-W6O3LVBY.js.map +1 -0
- package/dist/chunk-WFEAKJLR.js +47 -0
- package/dist/chunk-WFEAKJLR.js.map +1 -0
- package/dist/chunk-XIEY2OF4.js +67 -0
- package/dist/chunk-XIEY2OF4.js.map +1 -0
- package/dist/chunk-XITLHDG3.js +37 -0
- package/dist/chunk-XITLHDG3.js.map +1 -0
- package/dist/chunk-XMWPYZWZ.js +61 -0
- package/dist/chunk-XMWPYZWZ.js.map +1 -0
- package/dist/chunk-XUP6CNOM.js +34 -0
- package/dist/chunk-XUP6CNOM.js.map +1 -0
- package/dist/chunk-YABTQZDJ.js +25 -0
- package/dist/chunk-YABTQZDJ.js.map +1 -0
- package/dist/chunk-YMJ7BRKU.js +510 -0
- package/dist/chunk-YMJ7BRKU.js.map +1 -0
- package/dist/chunk-ZC3XWU7B.js +28 -0
- package/dist/chunk-ZC3XWU7B.js.map +1 -0
- package/dist/chunk-ZHMTD5KV.js +90 -0
- package/dist/chunk-ZHMTD5KV.js.map +1 -0
- package/dist/chunk-ZWZDLOE7.js +160 -0
- package/dist/chunk-ZWZDLOE7.js.map +1 -0
- package/dist/db-package/db/local-first-sqlite-adapter.d.ts +432 -0
- package/dist/db-package/db/local-first-sqlite-adapter.js +7 -0
- package/dist/db-package/db/local-first-sqlite-adapter.js.map +1 -0
- package/dist/db-package/index.d.ts +7 -0
- package/dist/db-package/index.js +16 -0
- package/dist/db-package/index.js.map +1 -0
- package/dist/db-package/migration/aggregator.d.ts +86 -0
- package/dist/db-package/migration/aggregator.js +7 -0
- package/dist/db-package/migration/aggregator.js.map +1 -0
- package/dist/db-package/migration/index.d.ts +3 -0
- package/dist/db-package/migration/index.js +8 -0
- package/dist/db-package/migration/index.js.map +1 -0
- package/dist/engine.d.ts +205 -0
- package/dist/engine.js +20 -0
- package/dist/engine.js.map +1 -0
- package/dist/entitlements/index.d.ts +313 -0
- package/dist/entitlements/index.js +23 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/execution/index.d.ts +50 -0
- package/dist/execution/index.js +57 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/extensions/index.d.ts +172 -0
- package/dist/extensions/index.js +95 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/health/index.d.ts +35 -0
- package/dist/health/index.js +7 -0
- package/dist/health/index.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +160 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/index.d.ts +133 -0
- package/dist/lifecycle/index.js +13 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle-engine/backend-migration.d.ts +2 -0
- package/dist/lifecycle-engine/backend-migration.js +11 -0
- package/dist/lifecycle-engine/backend-migration.js.map +1 -0
- package/dist/lifecycle-engine/baseline.d.ts +2 -0
- package/dist/lifecycle-engine/baseline.js +9 -0
- package/dist/lifecycle-engine/baseline.js.map +1 -0
- package/dist/lifecycle-engine/index.d.ts +1497 -0
- package/dist/lifecycle-engine/index.js +41 -0
- package/dist/lifecycle-engine/index.js.map +1 -0
- package/dist/lifecycle-engine/platform/artifacts.d.ts +4 -0
- package/dist/lifecycle-engine/platform/artifacts.js +7 -0
- package/dist/lifecycle-engine/platform/artifacts.js.map +1 -0
- package/dist/lifecycle-engine/platform/control-plane.d.ts +12 -0
- package/dist/lifecycle-engine/platform/control-plane.js +2 -0
- package/dist/lifecycle-engine/platform/control-plane.js.map +1 -0
- package/dist/lifecycle-engine/platform/database-router.d.ts +118 -0
- package/dist/lifecycle-engine/platform/database-router.js +7 -0
- package/dist/lifecycle-engine/platform/database-router.js.map +1 -0
- package/dist/lifecycle-engine/platform/deployment-placements.d.ts +464 -0
- package/dist/lifecycle-engine/platform/deployment-placements.js +16 -0
- package/dist/lifecycle-engine/platform/deployment-placements.js.map +1 -0
- package/dist/lifecycle-engine/platform/deployment.d.ts +4 -0
- package/dist/lifecycle-engine/platform/deployment.js +17 -0
- package/dist/lifecycle-engine/platform/deployment.js.map +1 -0
- package/dist/lifecycle-engine/platform/index.d.ts +737 -0
- package/dist/lifecycle-engine/platform/index.js +60 -0
- package/dist/lifecycle-engine/platform/index.js.map +1 -0
- package/dist/lifecycle-engine/platform/runtime.d.ts +80 -0
- package/dist/lifecycle-engine/platform/runtime.js +11 -0
- package/dist/lifecycle-engine/platform/runtime.js.map +1 -0
- package/dist/lifecycle-engine/platform/site-publication.d.ts +102 -0
- package/dist/lifecycle-engine/platform/site-publication.js +7 -0
- package/dist/lifecycle-engine/platform/site-publication.js.map +1 -0
- package/dist/lifecycle-engine/platform/user-directory.d.ts +139 -0
- package/dist/lifecycle-engine/platform/user-directory.js +13 -0
- package/dist/lifecycle-engine/platform/user-directory.js.map +1 -0
- package/dist/mcp.d.ts +92 -0
- package/dist/mcp.js +10 -0
- package/dist/mcp.js.map +1 -0
- package/dist/middleware/auth.d.ts +118 -0
- package/dist/middleware/auth.js +7 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +35 -0
- package/dist/middleware/error-handler.js +7 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/module-guard.d.ts +33 -0
- package/dist/middleware/module-guard.js +7 -0
- package/dist/middleware/module-guard.js.map +1 -0
- package/dist/platform-admin/index.d.ts +37 -0
- package/dist/platform-admin/index.js +11 -0
- package/dist/platform-admin/index.js.map +1 -0
- package/dist/platform-admin/store.d.ts +346 -0
- package/dist/platform-admin/store.js +7 -0
- package/dist/platform-admin/store.js.map +1 -0
- package/dist/plugin-data/annotations.d.ts +6 -0
- package/dist/plugin-data/annotations.js +12 -0
- package/dist/plugin-data/annotations.js.map +1 -0
- package/dist/plugin-data/atomic-context.d.ts +41 -0
- package/dist/plugin-data/atomic-context.js +9 -0
- package/dist/plugin-data/atomic-context.js.map +1 -0
- package/dist/plugin-data/atomic.d.ts +6 -0
- package/dist/plugin-data/atomic.js +26 -0
- package/dist/plugin-data/atomic.js.map +1 -0
- package/dist/plugin-data/audit.d.ts +6 -0
- package/dist/plugin-data/audit.js +12 -0
- package/dist/plugin-data/audit.js.map +1 -0
- package/dist/plugin-data/commands.d.ts +6 -0
- package/dist/plugin-data/commands.js +8 -0
- package/dist/plugin-data/commands.js.map +1 -0
- package/dist/plugin-data/composite.d.ts +6 -0
- package/dist/plugin-data/composite.js +8 -0
- package/dist/plugin-data/composite.js.map +1 -0
- package/dist/plugin-data/delivery.d.ts +6 -0
- package/dist/plugin-data/delivery.js +16 -0
- package/dist/plugin-data/delivery.js.map +1 -0
- package/dist/plugin-data/index.d.ts +2798 -0
- package/dist/plugin-data/index.js +102 -0
- package/dist/plugin-data/index.js.map +1 -0
- package/dist/plugin-data/json-documents.d.ts +6 -0
- package/dist/plugin-data/json-documents.js +16 -0
- package/dist/plugin-data/json-documents.js.map +1 -0
- package/dist/plugin-data/outbox.d.ts +6 -0
- package/dist/plugin-data/outbox.js +16 -0
- package/dist/plugin-data/outbox.js.map +1 -0
- package/dist/plugin-data/scoped-records.d.ts +6 -0
- package/dist/plugin-data/scoped-records.js +10 -0
- package/dist/plugin-data/scoped-records.js.map +1 -0
- package/dist/plugin-sdk.d.ts +41 -0
- package/dist/plugin-sdk.js +10 -0
- package/dist/plugin-sdk.js.map +1 -0
- package/dist/sql-lifecycle/index.d.ts +138 -0
- package/dist/sql-lifecycle/index.js +167 -0
- package/dist/sql-lifecycle/index.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +13 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/s3-compatible.d.ts +139 -0
- package/dist/storage/s3-compatible.js +11 -0
- package/dist/storage/s3-compatible.js.map +1 -0
- package/dist/tenant.d.ts +108 -0
- package/dist/tenant.js +9 -0
- package/dist/tenant.js.map +1 -0
- package/dist/workspace-context.d.ts +119 -0
- package/dist/workspace-context.js +9 -0
- package/dist/workspace-context.js.map +1 -0
- package/docs/api.md +29753 -0
- package/docs/db/README.md +49 -0
- package/docs/db/docs/api.md +1613 -0
- package/docs/db/docs/guide.md +47 -0
- package/docs/entitlements.md +60 -0
- package/docs/guide.md +48 -0
- package/docs/index.md +53 -0
- package/docs/ja/db/README.md +46 -0
- package/docs/ja/db/docs/guide.md +53 -0
- package/docs/ja/guide.md +46 -0
- package/docs/ja/lifecycle/README.md +46 -0
- package/docs/ja/lifecycle/docs/guide.md +45 -0
- package/docs/ja/storage/README.md +46 -0
- package/docs/ja/storage/docs/guide.md +43 -0
- package/docs/lifecycle/README.md +49 -0
- package/docs/lifecycle/docs/api.md +3770 -0
- package/docs/lifecycle/docs/baseline.md +50 -0
- package/docs/lifecycle/docs/guide.md +47 -0
- package/docs/storage/README.md +49 -0
- package/docs/storage/docs/api.md +470 -0
- package/docs/storage/docs/guide.md +47 -0
- package/package.json +345 -4
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createCommandAgentExecutor,
|
|
3
|
+
validateAgentRuntimePolicy
|
|
4
|
+
} from "./chunk-32746UQZ.js";
|
|
5
|
+
import {
|
|
6
|
+
ServerAgentExecutionError
|
|
7
|
+
} from "./chunk-YABTQZDJ.js";
|
|
8
|
+
|
|
9
|
+
// src/agentic/autonomous-worker.ts
|
|
10
|
+
import { defaultRuntimePlacementPolicy, McpGatekeeper } from "@alneas/eaos-core";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
function inferRuntimeRole() {
|
|
13
|
+
const configured = process.env.EAOS_RUNTIME_ROLE;
|
|
14
|
+
if (configured === "edge" || configured === "application-worker" || configured === "ai") return configured;
|
|
15
|
+
return process.arch === "arm64" ? "ai" : "application-worker";
|
|
16
|
+
}
|
|
17
|
+
var ServerAutonomousWorker = class {
|
|
18
|
+
gatekeeper;
|
|
19
|
+
goosePath;
|
|
20
|
+
hermesAgentPath;
|
|
21
|
+
litellmUrl;
|
|
22
|
+
litellmModel;
|
|
23
|
+
maxOutputChars;
|
|
24
|
+
runtimePolicy;
|
|
25
|
+
runtimeRole;
|
|
26
|
+
activeTasks = 0;
|
|
27
|
+
fetchImpl;
|
|
28
|
+
executors;
|
|
29
|
+
/**
|
|
30
|
+
* Captures runtime settings and validates resource policy, role, and output ceiling.
|
|
31
|
+
* @param options - Trusted host configuration; omitted gatekeeper uses a new McpGatekeeper and runtime role uses EAOS_RUNTIME_ROLE, then ai on arm64 or application-worker otherwise.
|
|
32
|
+
* @throws Invalid resource policy, role, or output limit.
|
|
33
|
+
*/
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.gatekeeper = options?.gatekeeper || new McpGatekeeper();
|
|
36
|
+
this.goosePath = options?.gooseBinaryPath || process.env.GOOSE_BIN_PATH || "goose";
|
|
37
|
+
this.hermesAgentPath = options?.hermesAgentBinaryPath || process.env.HERMES_AGENT_BIN_PATH || "hermes";
|
|
38
|
+
this.litellmUrl = options?.litellmEndpoint || process.env.LITELLM_ENDPOINT || "http://localhost:4000/v1";
|
|
39
|
+
this.litellmModel = options?.litellmModel || process.env.LITELLM_MODEL;
|
|
40
|
+
this.maxOutputChars = options?.maxOutputChars ?? 1e6;
|
|
41
|
+
this.runtimePolicy = validateAgentRuntimePolicy(options?.runtimePolicy || defaultRuntimePlacementPolicy().ai.agentRuntime);
|
|
42
|
+
this.runtimeRole = options?.runtimeRole || inferRuntimeRole();
|
|
43
|
+
if (!["edge", "application-worker", "ai"].includes(this.runtimeRole)) throw new Error("Invalid agent runtime role");
|
|
44
|
+
this.fetchImpl = options?.fetch || fetch;
|
|
45
|
+
this.executors = options?.executors || {};
|
|
46
|
+
if (!Number.isSafeInteger(this.maxOutputChars) || this.maxOutputChars < 1 || this.maxOutputChars > 1e7) throw new Error("Invalid worker output limit");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Checks task limits and gatekeeper policy before dispatching one runtime attempt.
|
|
50
|
+
* @param request - Verified task identity and instructions; required strings must be nonblank, title at most 500 characters, instructions at most 1000000, and timeout a safe integer 1–3600000 milliseconds when provided.
|
|
51
|
+
* @returns COMPLETED text output, PENDING_APPROVAL ticket data without execution, or FAILED data with error and optional classification/retry advice. Duration is measured in wall-clock milliseconds.
|
|
52
|
+
* @throws Can reject a throwing gatekeeper or malformed gatekeeper interaction before dispatch; runtime execution errors are normally returned as FAILED data.
|
|
53
|
+
* @remarks Gatekeeper receives fixed agent/server_bot roles rather than request user roles. Approval tickets are generated but not persisted here. The configured framework policy overrides request.runtimePolicy before execution. Injected executors and built-in LiteLLM outputs are bounded by maxOutputChars; LiteLLM response bodies are parsed before text length is checked.
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* import { ServerAutonomousWorker, type ServerWorkerOptions } from "@alneas/eaos-server/agentic/autonomous-worker";
|
|
57
|
+
* import type { ServerAgentTaskRequest } from "@alneas/eaos-server/agentic/types";
|
|
58
|
+
* export async function execute(options: ServerWorkerOptions, request: ServerAgentTaskRequest) {
|
|
59
|
+
* return new ServerAutonomousWorker(options).runTask(request);
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
async runTask(request) {
|
|
64
|
+
const startTime = Date.now();
|
|
65
|
+
if (!request || [request.id, request.tenantId, request.userId, request.title, request.instructions].some((value) => typeof value !== "string" || !value.trim())) {
|
|
66
|
+
return { taskId: typeof request?.id === "string" ? request.id : "unknown", status: "FAILED", error: "Invalid agent task request", durationMs: Date.now() - startTime };
|
|
67
|
+
}
|
|
68
|
+
if (request.instructions.length > 1e6 || request.title.length > 500 || request.timeoutMs !== void 0 && (!Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > 36e5)) {
|
|
69
|
+
return { taskId: request.id, status: "FAILED", error: "Agent task exceeds configured limits", durationMs: Date.now() - startTime };
|
|
70
|
+
}
|
|
71
|
+
const policy = this.gatekeeper.validateCall({
|
|
72
|
+
toolName: `server_agent_${request.workerType}`,
|
|
73
|
+
userRoles: ["agent", "server_bot"],
|
|
74
|
+
tenantId: request.tenantId
|
|
75
|
+
});
|
|
76
|
+
if (!policy.allowed) {
|
|
77
|
+
return {
|
|
78
|
+
taskId: request.id,
|
|
79
|
+
status: "FAILED",
|
|
80
|
+
error: `Task execution blocked by security policy: ${policy.reason}`,
|
|
81
|
+
riskLevel: policy.riskLevel,
|
|
82
|
+
durationMs: Date.now() - startTime
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (policy.requireHumanApproval) {
|
|
86
|
+
return {
|
|
87
|
+
taskId: request.id,
|
|
88
|
+
status: "PENDING_APPROVAL",
|
|
89
|
+
approvalTicketId: randomUUID(),
|
|
90
|
+
approvalLevel: policy.approvalLevel,
|
|
91
|
+
riskLevel: policy.riskLevel,
|
|
92
|
+
durationMs: Date.now() - startTime
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const frameworkManaged = !this.executors[request.workerType];
|
|
96
|
+
if (frameworkManaged && (request.workerType === "goose" || request.workerType === "hermes_agent") && this.runtimeRole !== "ai") {
|
|
97
|
+
return {
|
|
98
|
+
taskId: request.id,
|
|
99
|
+
status: "FAILED",
|
|
100
|
+
error: `Built-in ${request.workerType} execution is restricted to the AI runtime placement`,
|
|
101
|
+
failureCode: "AGENT_UNAVAILABLE",
|
|
102
|
+
retryable: true,
|
|
103
|
+
durationMs: Date.now() - startTime
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (frameworkManaged && this.activeTasks >= this.runtimePolicy.maxConcurrent) {
|
|
107
|
+
return {
|
|
108
|
+
taskId: request.id,
|
|
109
|
+
status: "FAILED",
|
|
110
|
+
error: "Agent runtime concurrency limit reached",
|
|
111
|
+
failureCode: "AGENT_UNAVAILABLE",
|
|
112
|
+
retryable: true,
|
|
113
|
+
durationMs: Date.now() - startTime
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (frameworkManaged) this.activeTasks += 1;
|
|
117
|
+
try {
|
|
118
|
+
const output = await this.execute({ ...structuredClone(request), runtimePolicy: structuredClone(this.runtimePolicy) });
|
|
119
|
+
return {
|
|
120
|
+
taskId: request.id,
|
|
121
|
+
status: "COMPLETED",
|
|
122
|
+
output,
|
|
123
|
+
durationMs: Date.now() - startTime
|
|
124
|
+
};
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return {
|
|
127
|
+
taskId: request.id,
|
|
128
|
+
status: "FAILED",
|
|
129
|
+
error: err.message,
|
|
130
|
+
...err instanceof ServerAgentExecutionError ? { failureCode: err.failureCode, retryable: err.retryable } : {},
|
|
131
|
+
durationMs: Date.now() - startTime
|
|
132
|
+
};
|
|
133
|
+
} finally {
|
|
134
|
+
if (frameworkManaged) this.activeTasks -= 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async execute(request) {
|
|
138
|
+
const injected = this.executors[request.workerType];
|
|
139
|
+
if (injected) return this.boundOutput(await injected(structuredClone(request)));
|
|
140
|
+
if (request.workerType === "goose") return this.executeHeadlessGoose(request);
|
|
141
|
+
if (request.workerType === "hermes_agent") return this.executeHeadlessHermesAgent(request);
|
|
142
|
+
if (request.workerType === "litellm") return this.executeLiteLlm(request);
|
|
143
|
+
throw new Error(`No executor configured for worker type '${request.workerType}'`);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Execute Goose in headless batch mode
|
|
147
|
+
*/
|
|
148
|
+
async executeHeadlessGoose(request) {
|
|
149
|
+
return createCommandAgentExecutor({
|
|
150
|
+
command: this.goosePath,
|
|
151
|
+
buildArgs: (task) => ["run", "--text", task.instructions],
|
|
152
|
+
includeProcessEnv: true,
|
|
153
|
+
maxOutputChars: this.maxOutputChars,
|
|
154
|
+
sandbox: { mode: "auto", policy: this.runtimePolicy }
|
|
155
|
+
})(request);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Execute NousResearch Hermes Agent in headless mode (-z / non-interactive)
|
|
159
|
+
* Hermes Agent provides persistent cross-session memory and self-improving skill extraction.
|
|
160
|
+
*/
|
|
161
|
+
async executeHeadlessHermesAgent(request) {
|
|
162
|
+
return createCommandAgentExecutor({
|
|
163
|
+
command: this.hermesAgentPath,
|
|
164
|
+
buildArgs: (task) => ["run", "-z", task.instructions],
|
|
165
|
+
includeProcessEnv: true,
|
|
166
|
+
maxOutputChars: this.maxOutputChars,
|
|
167
|
+
sandbox: { mode: "auto", policy: this.runtimePolicy }
|
|
168
|
+
})(request);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Execute a host-selected model through LiteLLM.
|
|
172
|
+
*/
|
|
173
|
+
async executeLiteLlm(request) {
|
|
174
|
+
if (!this.litellmModel) throw new Error("LiteLLM model is not configured");
|
|
175
|
+
const endpoint = new URL(this.litellmUrl);
|
|
176
|
+
if (!["http:", "https:"].includes(endpoint.protocol)) throw new Error("Invalid LiteLLM endpoint");
|
|
177
|
+
endpoint.pathname = `${endpoint.pathname.replace(/\/$/, "")}/chat/completions`;
|
|
178
|
+
const response = await this.fetchImpl(endpoint, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
181
|
+
body: JSON.stringify({
|
|
182
|
+
model: this.litellmModel,
|
|
183
|
+
messages: [{ role: "user", content: request.instructions }],
|
|
184
|
+
metadata: { tenantId: request.tenantId, taskId: request.id }
|
|
185
|
+
}),
|
|
186
|
+
signal: AbortSignal.timeout(request.timeoutMs ?? 6e4),
|
|
187
|
+
redirect: "error"
|
|
188
|
+
});
|
|
189
|
+
if (!response.ok) throw new Error(`LiteLLM request failed (${response.status})`);
|
|
190
|
+
const body = await response.json();
|
|
191
|
+
const output = body.choices?.[0]?.message?.content;
|
|
192
|
+
if (typeof output !== "string") throw new Error("LiteLLM returned no assistant content");
|
|
193
|
+
return this.boundOutput(output);
|
|
194
|
+
}
|
|
195
|
+
boundOutput(output) {
|
|
196
|
+
if (output.length > this.maxOutputChars) throw new Error("Agent output exceeds configured limit");
|
|
197
|
+
return output;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
ServerAutonomousWorker
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=chunk-THYUY5ZZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agentic/autonomous-worker.ts"],"sourcesContent":["import { defaultRuntimePlacementPolicy, McpGatekeeper, type AgentRuntimeResourcePolicy } from '@alneas/eaos-core'\nimport { ServerAgentExecutionError, type ServerAgentExecutor, type ServerAgentTaskRequest, type ServerAgentTaskResult, type ServerAgentWorkerType } from './types'\nimport { randomUUID } from 'node:crypto'\nimport { createCommandAgentExecutor, validateAgentRuntimePolicy } from './executor'\n\n/**\n * Trusted gatekeeper, runtime endpoints/adapters, and execution limits for {@link ServerAutonomousWorker}.\n */\nexport interface ServerWorkerOptions {\n /** Host security/approval policies; defaults to a new McpGatekeeper. */\n gatekeeper?: McpGatekeeper\n /** Trusted executable path; defaults to GOOSE_BIN_PATH or goose. */\n gooseBinaryPath?: string\n /** Trusted executable path; defaults to HERMES_AGENT_BIN_PATH or hermes. */\n hermesAgentBinaryPath?: string\n /** Host LiteLLM base URL; defaults to LITELLM_ENDPOINT or http://localhost:4000/v1. */\n litellmEndpoint?: string\n /** Host model identifier; defaults to LITELLM_MODEL and is required for built-in LiteLLM execution. */\n litellmModel?: string\n /** Combined command output/string result ceiling, safe integer 1–10000000; defaults to 1000000. */\n maxOutputChars?: number\n /** Framework default is safe for the ARM agent host. Host adapters may tighten it. */\n runtimePolicy?: AgentRuntimeResourcePolicy\n /** Built-in Goose/Hermes execution is permitted only on the AI placement. */\n runtimeRole?: ServerRuntimeRole\n /** Host fetch implementation for built-in LiteLLM; defaults to global fetch. */\n fetch?: typeof fetch\n /** Host/plugin adapters for coding agents and other external runtimes. */\n executors?: Partial<Record<ServerAgentWorkerType, ServerAgentExecutor>>\n}\n\n/**\n * Host placement role controlling whether built-in Goose/Hermes processes may run locally.\n * @see ServerAutonomousWorker.\n */\nexport type ServerRuntimeRole = 'edge' | 'application-worker' | 'ai'\n\nfunction inferRuntimeRole(): ServerRuntimeRole {\n const configured = process.env.EAOS_RUNTIME_ROLE\n if (configured === 'edge' || configured === 'application-worker' || configured === 'ai') return configured\n // The supported three-host topology places the AI runtime on the ARM host.\n // Unknown x64 hosts therefore fail closed as application-worker hosts.\n return process.arch === 'arm64' ? 'ai' : 'application-worker'\n}\n\n/**\n * Admits and executes host agent tasks through a gatekeeper and bounded runtime adapters.\n * @remarks This class runs only when runTask is called; it installs no scheduler. The host authenticates tenant/user claims before admission. Framework-managed tasks share a per-instance concurrency counter; injected executors own scheduling and are exempt from it. Built-in Goose/Hermes require the ai role and Node process/systemd support. Queue persistence, approvals, retries, and side-effect idempotency belong to the host.\n * @example\n * ```typescript\n * import { ServerAutonomousWorker, type ServerWorkerOptions } from \"@alneas/eaos-server/agentic/autonomous-worker\";\n * export function worker(options: ServerWorkerOptions) { return new ServerAutonomousWorker(options); }\n * ```\n */\nexport class ServerAutonomousWorker {\n private gatekeeper: McpGatekeeper\n private goosePath: string\n private hermesAgentPath: string\n private litellmUrl: string\n private litellmModel?: string\n private maxOutputChars: number\n private runtimePolicy: AgentRuntimeResourcePolicy\n private runtimeRole: ServerRuntimeRole\n private activeTasks = 0\n private fetchImpl: typeof fetch\n private executors: Partial<Record<ServerAgentWorkerType, ServerAgentExecutor>>\n\n /**\n * Captures runtime settings and validates resource policy, role, and output ceiling.\n * @param options - Trusted host configuration; omitted gatekeeper uses a new McpGatekeeper and runtime role uses EAOS_RUNTIME_ROLE, then ai on arm64 or application-worker otherwise.\n * @throws Invalid resource policy, role, or output limit.\n */\n constructor(options?: ServerWorkerOptions) {\n this.gatekeeper = options?.gatekeeper || new McpGatekeeper()\n this.goosePath = options?.gooseBinaryPath || process.env.GOOSE_BIN_PATH || 'goose'\n this.hermesAgentPath = options?.hermesAgentBinaryPath || process.env.HERMES_AGENT_BIN_PATH || 'hermes'\n this.litellmUrl = options?.litellmEndpoint || process.env.LITELLM_ENDPOINT || 'http://localhost:4000/v1'\n this.litellmModel = options?.litellmModel || process.env.LITELLM_MODEL\n this.maxOutputChars = options?.maxOutputChars ?? 1_000_000\n this.runtimePolicy = validateAgentRuntimePolicy(options?.runtimePolicy || defaultRuntimePlacementPolicy().ai.agentRuntime)\n this.runtimeRole = options?.runtimeRole || inferRuntimeRole()\n if (!['edge', 'application-worker', 'ai'].includes(this.runtimeRole)) throw new Error('Invalid agent runtime role')\n this.fetchImpl = options?.fetch || fetch\n this.executors = options?.executors || {}\n if (!Number.isSafeInteger(this.maxOutputChars) || this.maxOutputChars < 1 || this.maxOutputChars > 10_000_000) throw new Error('Invalid worker output limit')\n }\n\n /**\n * Checks task limits and gatekeeper policy before dispatching one runtime attempt.\n * @param request - Verified task identity and instructions; required strings must be nonblank, title at most 500 characters, instructions at most 1000000, and timeout a safe integer 1–3600000 milliseconds when provided.\n * @returns COMPLETED text output, PENDING_APPROVAL ticket data without execution, or FAILED data with error and optional classification/retry advice. Duration is measured in wall-clock milliseconds.\n * @throws Can reject a throwing gatekeeper or malformed gatekeeper interaction before dispatch; runtime execution errors are normally returned as FAILED data.\n * @remarks Gatekeeper receives fixed agent/server_bot roles rather than request user roles. Approval tickets are generated but not persisted here. The configured framework policy overrides request.runtimePolicy before execution. Injected executors and built-in LiteLLM outputs are bounded by maxOutputChars; LiteLLM response bodies are parsed before text length is checked.\n * @example\n * ```typescript\n * import { ServerAutonomousWorker, type ServerWorkerOptions } from \"@alneas/eaos-server/agentic/autonomous-worker\";\n * import type { ServerAgentTaskRequest } from \"@alneas/eaos-server/agentic/types\";\n * export async function execute(options: ServerWorkerOptions, request: ServerAgentTaskRequest) {\n * return new ServerAutonomousWorker(options).runTask(request);\n * }\n * ```\n */\n async runTask(request: ServerAgentTaskRequest): Promise<ServerAgentTaskResult> {\n const startTime = Date.now()\n\n if (!request || [request.id, request.tenantId, request.userId, request.title, request.instructions].some(value => typeof value !== 'string' || !value.trim())) {\n return { taskId: typeof request?.id === 'string' ? request.id : 'unknown', status: 'FAILED', error: 'Invalid agent task request', durationMs: Date.now() - startTime }\n }\n if (request.instructions.length > 1_000_000 || request.title.length > 500 || request.timeoutMs !== undefined && (!Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > 3_600_000)) {\n return { taskId: request.id, status: 'FAILED', error: 'Agent task exceeds configured limits', durationMs: Date.now() - startTime }\n }\n\n // 1. Evaluate security and gatekeeper policies\n const policy = this.gatekeeper.validateCall({\n toolName: `server_agent_${request.workerType}`,\n userRoles: ['agent', 'server_bot'],\n tenantId: request.tenantId,\n })\n\n if (!policy.allowed) {\n return {\n taskId: request.id,\n status: 'FAILED',\n error: `Task execution blocked by security policy: ${policy.reason}`,\n riskLevel: policy.riskLevel,\n durationMs: Date.now() - startTime,\n }\n }\n\n if (policy.requireHumanApproval) {\n return {\n taskId: request.id,\n status: 'PENDING_APPROVAL',\n approvalTicketId: randomUUID(),\n approvalLevel: policy.approvalLevel,\n riskLevel: policy.riskLevel,\n durationMs: Date.now() - startTime,\n }\n }\n\n // Explicitly injected executors are host-owned adapters and may implement\n // their own queue/scheduler. The framework-managed Goose/Hermes path is\n // bounded by the default policy so a forgotten host setting cannot create\n // unbounded local agent concurrency.\n const frameworkManaged = !this.executors[request.workerType]\n if (frameworkManaged && (request.workerType === 'goose' || request.workerType === 'hermes_agent') && this.runtimeRole !== 'ai') {\n return {\n taskId: request.id,\n status: 'FAILED',\n error: `Built-in ${request.workerType} execution is restricted to the AI runtime placement`,\n failureCode: 'AGENT_UNAVAILABLE',\n retryable: true,\n durationMs: Date.now() - startTime,\n }\n }\n if (frameworkManaged && this.activeTasks >= this.runtimePolicy.maxConcurrent) {\n return {\n taskId: request.id,\n status: 'FAILED',\n error: 'Agent runtime concurrency limit reached',\n failureCode: 'AGENT_UNAVAILABLE',\n retryable: true,\n durationMs: Date.now() - startTime,\n }\n }\n\n // 2. Dispatch according to worker type\n if (frameworkManaged) this.activeTasks += 1\n try {\n const output = await this.execute({ ...structuredClone(request), runtimePolicy: structuredClone(this.runtimePolicy) })\n return {\n taskId: request.id,\n status: 'COMPLETED',\n output,\n durationMs: Date.now() - startTime,\n }\n } catch (err) {\n return {\n taskId: request.id,\n status: 'FAILED',\n error: (err as Error).message,\n ...(err instanceof ServerAgentExecutionError ? { failureCode: err.failureCode, retryable: err.retryable } : {}),\n durationMs: Date.now() - startTime,\n }\n } finally {\n if (frameworkManaged) this.activeTasks -= 1\n }\n }\n\n private async execute(request: ServerAgentTaskRequest): Promise<string> {\n const injected = this.executors[request.workerType]\n if (injected) return this.boundOutput(await injected(structuredClone(request)))\n if (request.workerType === 'goose') return this.executeHeadlessGoose(request)\n if (request.workerType === 'hermes_agent') return this.executeHeadlessHermesAgent(request)\n if (request.workerType === 'litellm') return this.executeLiteLlm(request)\n throw new Error(`No executor configured for worker type '${request.workerType}'`)\n }\n\n /**\n * Execute Goose in headless batch mode\n */\n private async executeHeadlessGoose(request: ServerAgentTaskRequest): Promise<string> {\n return createCommandAgentExecutor({\n command: this.goosePath,\n buildArgs: task => ['run', '--text', task.instructions],\n includeProcessEnv: true,\n maxOutputChars: this.maxOutputChars,\n sandbox: { mode: 'auto', policy: this.runtimePolicy },\n })(request)\n }\n\n /**\n * Execute NousResearch Hermes Agent in headless mode (-z / non-interactive)\n * Hermes Agent provides persistent cross-session memory and self-improving skill extraction.\n */\n private async executeHeadlessHermesAgent(request: ServerAgentTaskRequest): Promise<string> {\n return createCommandAgentExecutor({\n command: this.hermesAgentPath,\n buildArgs: task => ['run', '-z', task.instructions],\n includeProcessEnv: true,\n maxOutputChars: this.maxOutputChars,\n sandbox: { mode: 'auto', policy: this.runtimePolicy },\n })(request)\n }\n\n /**\n * Execute a host-selected model through LiteLLM.\n */\n private async executeLiteLlm(request: ServerAgentTaskRequest): Promise<string> {\n if (!this.litellmModel) throw new Error('LiteLLM model is not configured')\n const endpoint = new URL(this.litellmUrl)\n if (!['http:', 'https:'].includes(endpoint.protocol)) throw new Error('Invalid LiteLLM endpoint')\n endpoint.pathname = `${endpoint.pathname.replace(/\\/$/, '')}/chat/completions`\n const response = await this.fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.litellmModel,\n messages: [{ role: 'user', content: request.instructions }],\n metadata: { tenantId: request.tenantId, taskId: request.id },\n }),\n signal: AbortSignal.timeout(request.timeoutMs ?? 60_000),\n redirect: 'error',\n })\n if (!response.ok) throw new Error(`LiteLLM request failed (${response.status})`)\n const body = await response.json() as { choices?: Array<{ message?: { content?: unknown } }> }\n const output = body.choices?.[0]?.message?.content\n if (typeof output !== 'string') throw new Error('LiteLLM returned no assistant content')\n return this.boundOutput(output)\n }\n\n private boundOutput(output: string): string {\n if (output.length > this.maxOutputChars) throw new Error('Agent output exceeds configured limit')\n return output\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,+BAA+B,qBAAsD;AAE9F,SAAS,kBAAkB;AAmC3B,SAAS,mBAAsC;AAC7C,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,eAAe,UAAU,eAAe,wBAAwB,eAAe,KAAM,QAAO;AAGhG,SAAO,QAAQ,SAAS,UAAU,OAAO;AAC3C;AAWO,IAAM,yBAAN,MAA6B;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YAAY,SAA+B;AACzC,SAAK,aAAa,SAAS,cAAc,IAAI,cAAc;AAC3D,SAAK,YAAY,SAAS,mBAAmB,QAAQ,IAAI,kBAAkB;AAC3E,SAAK,kBAAkB,SAAS,yBAAyB,QAAQ,IAAI,yBAAyB;AAC9F,SAAK,aAAa,SAAS,mBAAmB,QAAQ,IAAI,oBAAoB;AAC9E,SAAK,eAAe,SAAS,gBAAgB,QAAQ,IAAI;AACzD,SAAK,iBAAiB,SAAS,kBAAkB;AACjD,SAAK,gBAAgB,2BAA2B,SAAS,iBAAiB,8BAA8B,EAAE,GAAG,YAAY;AACzH,SAAK,cAAc,SAAS,eAAe,iBAAiB;AAC5D,QAAI,CAAC,CAAC,QAAQ,sBAAsB,IAAI,EAAE,SAAS,KAAK,WAAW,EAAG,OAAM,IAAI,MAAM,4BAA4B;AAClH,SAAK,YAAY,SAAS,SAAS;AACnC,SAAK,YAAY,SAAS,aAAa,CAAC;AACxC,QAAI,CAAC,OAAO,cAAc,KAAK,cAAc,KAAK,KAAK,iBAAiB,KAAK,KAAK,iBAAiB,IAAY,OAAM,IAAI,MAAM,6BAA6B;AAAA,EAC9J;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,QAAQ,SAAiE;AAC7E,UAAM,YAAY,KAAK,IAAI;AAE3B,QAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,QAAQ,UAAU,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,YAAY,EAAE,KAAK,WAAS,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,CAAC,GAAG;AAC7J,aAAO,EAAE,QAAQ,OAAO,SAAS,OAAO,WAAW,QAAQ,KAAK,WAAW,QAAQ,UAAU,OAAO,8BAA8B,YAAY,KAAK,IAAI,IAAI,UAAU;AAAA,IACvK;AACA,QAAI,QAAQ,aAAa,SAAS,OAAa,QAAQ,MAAM,SAAS,OAAO,QAAQ,cAAc,WAAc,CAAC,OAAO,cAAc,QAAQ,SAAS,KAAK,QAAQ,YAAY,KAAK,QAAQ,YAAY,OAAY;AACpN,aAAO,EAAE,QAAQ,QAAQ,IAAI,QAAQ,UAAU,OAAO,wCAAwC,YAAY,KAAK,IAAI,IAAI,UAAU;AAAA,IACnI;AAGA,UAAM,SAAS,KAAK,WAAW,aAAa;AAAA,MAC1C,UAAU,gBAAgB,QAAQ,UAAU;AAAA,MAC5C,WAAW,CAAC,SAAS,YAAY;AAAA,MACjC,UAAU,QAAQ;AAAA,IACpB,CAAC;AAED,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,OAAO,8CAA8C,OAAO,MAAM;AAAA,QAClE,WAAW,OAAO;AAAA,QAClB,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,OAAO,sBAAsB;AAC/B,aAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,kBAAkB,WAAW;AAAA,QAC7B,eAAe,OAAO;AAAA,QACtB,WAAW,OAAO;AAAA,QAClB,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF;AAMA,UAAM,mBAAmB,CAAC,KAAK,UAAU,QAAQ,UAAU;AAC3D,QAAI,qBAAqB,QAAQ,eAAe,WAAW,QAAQ,eAAe,mBAAmB,KAAK,gBAAgB,MAAM;AAC9H,aAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,OAAO,YAAY,QAAQ,UAAU;AAAA,QACrC,aAAa;AAAA,QACb,WAAW;AAAA,QACX,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF;AACA,QAAI,oBAAoB,KAAK,eAAe,KAAK,cAAc,eAAe;AAC5E,aAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,aAAa;AAAA,QACb,WAAW;AAAA,QACX,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF;AAGA,QAAI,iBAAkB,MAAK,eAAe;AAC1C,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,EAAE,GAAG,gBAAgB,OAAO,GAAG,eAAe,gBAAgB,KAAK,aAAa,EAAE,CAAC;AACrH,aAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR;AAAA,QACA,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,OAAQ,IAAc;AAAA,QACtB,GAAI,eAAe,4BAA4B,EAAE,aAAa,IAAI,aAAa,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,QAC7G,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF,UAAE;AACA,UAAI,iBAAkB,MAAK,eAAe;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,MAAc,QAAQ,SAAkD;AACtE,UAAM,WAAW,KAAK,UAAU,QAAQ,UAAU;AAClD,QAAI,SAAU,QAAO,KAAK,YAAY,MAAM,SAAS,gBAAgB,OAAO,CAAC,CAAC;AAC9E,QAAI,QAAQ,eAAe,QAAS,QAAO,KAAK,qBAAqB,OAAO;AAC5E,QAAI,QAAQ,eAAe,eAAgB,QAAO,KAAK,2BAA2B,OAAO;AACzF,QAAI,QAAQ,eAAe,UAAW,QAAO,KAAK,eAAe,OAAO;AACxE,UAAM,IAAI,MAAM,2CAA2C,QAAQ,UAAU,GAAG;AAAA,EAClF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAAqB,SAAkD;AACnF,WAAO,2BAA2B;AAAA,MAChC,SAAS,KAAK;AAAA,MACd,WAAW,UAAQ,CAAC,OAAO,UAAU,KAAK,YAAY;AAAA,MACtD,mBAAmB;AAAA,MACnB,gBAAgB,KAAK;AAAA,MACrB,SAAS,EAAE,MAAM,QAAQ,QAAQ,KAAK,cAAc;AAAA,IACtD,CAAC,EAAE,OAAO;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,2BAA2B,SAAkD;AACzF,WAAO,2BAA2B;AAAA,MAChC,SAAS,KAAK;AAAA,MACd,WAAW,UAAQ,CAAC,OAAO,MAAM,KAAK,YAAY;AAAA,MAClD,mBAAmB;AAAA,MACnB,gBAAgB,KAAK;AAAA,MACrB,SAAS,EAAE,MAAM,QAAQ,QAAQ,KAAK,cAAc;AAAA,IACtD,CAAC,EAAE,OAAO;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAAe,SAAkD;AAC7E,QAAI,CAAC,KAAK,aAAc,OAAM,IAAI,MAAM,iCAAiC;AACzE,UAAM,WAAW,IAAI,IAAI,KAAK,UAAU;AACxC,QAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,SAAS,QAAQ,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAChG,aAAS,WAAW,GAAG,SAAS,SAAS,QAAQ,OAAO,EAAE,CAAC;AAC3D,UAAM,WAAW,MAAM,KAAK,UAAU,UAAU;AAAA,MAC9C,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,OAAO,KAAK;AAAA,QACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,aAAa,CAAC;AAAA,QAC1D,UAAU,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,GAAG;AAAA,MAC7D,CAAC;AAAA,MACD,QAAQ,YAAY,QAAQ,QAAQ,aAAa,GAAM;AAAA,MACvD,UAAU;AAAA,IACZ,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,GAAG;AAC/E,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,SAAS,KAAK,UAAU,CAAC,GAAG,SAAS;AAC3C,QAAI,OAAO,WAAW,SAAU,OAAM,IAAI,MAAM,uCAAuC;AACvF,WAAO,KAAK,YAAY,MAAM;AAAA,EAChC;AAAA,EAEQ,YAAY,QAAwB;AAC1C,QAAI,OAAO,SAAS,KAAK,eAAgB,OAAM,IAAI,MAAM,uCAAuC;AAChG,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/agentic/agent-node.ts
|
|
2
|
+
function createWorkflowAgentNode(options) {
|
|
3
|
+
if (!options?.runner || typeof options.runner.run !== "function" || typeof options.createRequest !== "function") throw new Error("Workflow agent node requires a runner and request builder");
|
|
4
|
+
return async (state) => {
|
|
5
|
+
const request = await options.createRequest(structuredClone(state));
|
|
6
|
+
if (request.tenantId !== state.tenantId || request.userId !== state.userId) throw new Error("Workflow agent request principal does not match graph state");
|
|
7
|
+
if (request.workflowId !== state.sessionId) throw new Error("Workflow agent request workflowId does not match graph session");
|
|
8
|
+
const result = await options.runner.run(request);
|
|
9
|
+
if (result.status !== "COMPLETED" || !result.proposal) throw new Error(result.error || `Workflow agent runtime ${result.status.toLowerCase()}`);
|
|
10
|
+
const output = options.validateOutput ? await options.validateOutput(result.proposal.output, result.proposal, state) : result.proposal.output;
|
|
11
|
+
const contextKey = options.contextKey ?? "agentProposal";
|
|
12
|
+
return {
|
|
13
|
+
context: {
|
|
14
|
+
...state.context ?? {},
|
|
15
|
+
[contextKey]: structuredClone({ proposal: result.proposal, output })
|
|
16
|
+
},
|
|
17
|
+
history: [
|
|
18
|
+
...state.history,
|
|
19
|
+
{ role: "agent", content: result.proposal.summary, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
createWorkflowAgentNode
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=chunk-UGNTCOVR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agentic/agent-node.ts"],"sourcesContent":["import type { WorkflowAgentProposal, WorkflowAgentRequest, WorkflowAgentRunner } from '@alneas/eaos-core'\nimport type { AgentGraphState, GraphNodeHandler } from './graph'\n\n/**\n * Host runner, request construction, and output validation for {@link createWorkflowAgentNode}.\n * @typeParam TOutput - Domain output expected from the proposal; defaults to unknown and runtime validation remains plugin-owned.\n */\nexport interface WorkflowAgentNodeOptions<TOutput = unknown> {\n /** Host-owned runner that returns proposals; this node performs no proposed action. */\n runner: WorkflowAgentRunner\n /** Called per node invocation with a structured clone of state; must resolve a request matching tenantId, userId, and sessionId/workflowId. */\n createRequest: (state: AgentGraphState) => WorkflowAgentRequest | Promise<WorkflowAgentRequest>\n /** Called after a completed proposal with output, full proposal, and original current state; resolves the domain-validated output stored beside the proposal. Errors reject node execution. */\n validateOutput?: (output: TOutput, proposal: WorkflowAgentProposal<TOutput>, state: AgentGraphState) => TOutput | Promise<TOutput>\n /** Context property receiving cloned proposal and validated output; existing data at this key is overwritten. @defaultValue \"agentProposal\" */\n contextKey?: string\n}\n\n/**\n * Creates a LangGraph node that records an agent proposal in workflow state.\n * It deliberately does not execute proposed actions or create approvals.\n * @typeParam TOutput - Domain-specific proposal output passed to optional plugin validation; defaults to unknown.\n * @param options - Runner, request builder, output validator, and context key.\n * @returns An async graph handler resolving merged context and history with an agent summary entry.\n * @throws Synchronously if runner or request builder is missing. Node execution rejects principal/workflow mismatch, noncompleted runner results, and callback or structuredClone failures.\n * @remarks validateOutput receives proposal output, proposal, and the current state after a completed run; its result is stored alongside the proposal. Unlike createRequest, this callback receives the original state. Host admission, durable checkpointing, and action approval remain outside this node.\n * @example\n * ```typescript\n * import { createWorkflowAgentNode } from \"@alneas/eaos-server/agentic/agent-node\";\n * import type { WorkflowAgentNodeOptions } from \"@alneas/eaos-server/agentic/agent-node\";\n * export function proposalNode(options: WorkflowAgentNodeOptions) {\n * // Host callbacks build authorized requests and validate domain output.\n * return createWorkflowAgentNode(options);\n * }\n * ```\n */\nexport function createWorkflowAgentNode<TOutput = unknown>(options: WorkflowAgentNodeOptions<TOutput>): GraphNodeHandler {\n if (!options?.runner || typeof options.runner.run !== 'function' || typeof options.createRequest !== 'function') throw new Error('Workflow agent node requires a runner and request builder')\n\n return async state => {\n const request = await options.createRequest(structuredClone(state))\n if (request.tenantId !== state.tenantId || request.userId !== state.userId) throw new Error('Workflow agent request principal does not match graph state')\n if (request.workflowId !== state.sessionId) throw new Error('Workflow agent request workflowId does not match graph session')\n\n const result = await options.runner.run<TOutput>(request)\n if (result.status !== 'COMPLETED' || !result.proposal) throw new Error(result.error || `Workflow agent runtime ${result.status.toLowerCase()}`)\n const output = options.validateOutput ? await options.validateOutput(result.proposal.output, result.proposal, state) : result.proposal.output\n const contextKey = options.contextKey ?? 'agentProposal'\n return {\n context: {\n ...(state.context ?? {}),\n [contextKey]: structuredClone({ proposal: result.proposal, output }),\n },\n history: [\n ...state.history,\n { role: 'agent', content: result.proposal.summary, timestamp: new Date().toISOString() },\n ],\n }\n }\n}\n"],"mappings":";AAoCO,SAAS,wBAA2C,SAA8D;AACvH,MAAI,CAAC,SAAS,UAAU,OAAO,QAAQ,OAAO,QAAQ,cAAc,OAAO,QAAQ,kBAAkB,WAAY,OAAM,IAAI,MAAM,2DAA2D;AAE5L,SAAO,OAAM,UAAS;AACpB,UAAM,UAAU,MAAM,QAAQ,cAAc,gBAAgB,KAAK,CAAC;AAClE,QAAI,QAAQ,aAAa,MAAM,YAAY,QAAQ,WAAW,MAAM,OAAQ,OAAM,IAAI,MAAM,6DAA6D;AACzJ,QAAI,QAAQ,eAAe,MAAM,UAAW,OAAM,IAAI,MAAM,gEAAgE;AAE5H,UAAM,SAAS,MAAM,QAAQ,OAAO,IAAa,OAAO;AACxD,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,SAAU,OAAM,IAAI,MAAM,OAAO,SAAS,0BAA0B,OAAO,OAAO,YAAY,CAAC,EAAE;AAC9I,UAAM,SAAS,QAAQ,iBAAiB,MAAM,QAAQ,eAAe,OAAO,SAAS,QAAQ,OAAO,UAAU,KAAK,IAAI,OAAO,SAAS;AACvI,UAAM,aAAa,QAAQ,cAAc;AACzC,WAAO;AAAA,MACL,SAAS;AAAA,QACP,GAAI,MAAM,WAAW,CAAC;AAAA,QACtB,CAAC,UAAU,GAAG,gBAAgB,EAAE,UAAU,OAAO,UAAU,OAAO,CAAC;AAAA,MACrE;AAAA,MACA,SAAS;AAAA,QACP,GAAG,MAAM;AAAA,QACT,EAAE,MAAM,SAAS,SAAS,OAAO,SAAS,SAAS,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAAA,MACzF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/middleware/module-guard.ts
|
|
2
|
+
import { createMiddleware } from "hono/factory";
|
|
3
|
+
function createModuleGuard(moduleKey, options) {
|
|
4
|
+
return createMiddleware(async (c, next) => {
|
|
5
|
+
const isDev = options?.isDev ?? process.env.NODE_ENV === "development";
|
|
6
|
+
if (isDev) {
|
|
7
|
+
return await next();
|
|
8
|
+
}
|
|
9
|
+
const enabledModules = c.get("enabledModules");
|
|
10
|
+
if (!Array.isArray(enabledModules) || enabledModules.some((module) => typeof module !== "string")) {
|
|
11
|
+
console.warn(`[ModuleGuard] Entitlements unavailable for module '${moduleKey}' tenant=${c.get("tenantId")}`);
|
|
12
|
+
return c.json({
|
|
13
|
+
error: "Module Entitlements Unavailable",
|
|
14
|
+
message: "Module entitlements must be verified before accessing this module."
|
|
15
|
+
}, 403);
|
|
16
|
+
}
|
|
17
|
+
if (!enabledModules.includes(moduleKey) && !enabledModules.includes("all")) {
|
|
18
|
+
console.warn(`[ModuleGuard] Blocked request to module '${moduleKey}' for tenant=${c.get("tenantId")}`);
|
|
19
|
+
return c.json({
|
|
20
|
+
error: "Module Not Licensed",
|
|
21
|
+
message: `The module '${moduleKey}' is not enabled for your tenant.`
|
|
22
|
+
}, 403);
|
|
23
|
+
}
|
|
24
|
+
await next();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
createModuleGuard
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chunk-VC2PCK2S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/middleware/module-guard.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory'\nimport type { EaosEnv } from '@alneas/eaos-core'\n\n/**\n * Controls the explicit development bypass for {@link createModuleGuard}.\n */\nexport interface ModuleGuardOptions {\n /** Skip entitlement checks when true; defaults to NODE_ENV === \"development\". */\n isDev?: boolean\n}\n\n/**\n * Rejects requests unless verified enabledModules contains the requested module or \"all\".\n * @param moduleKey - Exact, case-sensitive entitlement key required by this route.\n * @param options - Development bypass; see {@link ModuleGuardOptions}.\n * @returns Hono middleware that responds with 403 for missing, malformed, or insufficient entitlements, or completes downstream handling without returning a value.\n * @throws Request execution propagates downstream middleware rejections.\n * @remarks Install authentication and trusted entitlement resolution first. This guard reads context only and does not query a contract store. The development bypass defaults from NODE_ENV.\n * @example\n * ```typescript\n * import { Hono } from \"hono\";\n * import type { EaosEnv } from \"@alneas/eaos-core\";\n * import { createModuleGuard } from \"@alneas/eaos-server/middleware/module-guard\";\n * const app = new Hono<EaosEnv>();\n * app.use(\"/reports/*\", createModuleGuard(\"reports\", { isDev: false }));\n * // Populate verified enabledModules in earlier host middleware to allow access.\n * app.get(\"/reports/summary\", c => c.json({ available: true }));\n * console.log((await app.request(\"/reports/summary\")).status); // 403\n * ```\n */\nexport function createModuleGuard(moduleKey: string, options?: ModuleGuardOptions) {\n return createMiddleware<EaosEnv>(async (c, next) => {\n const isDev = options?.isDev ?? process.env.NODE_ENV === 'development'\n if (isDev) {\n return await next()\n }\n\n const enabledModules = c.get('enabledModules')\n\n if (!Array.isArray(enabledModules) || enabledModules.some(module => typeof module !== 'string')) {\n console.warn(`[ModuleGuard] Entitlements unavailable for module '${moduleKey}' tenant=${c.get('tenantId')}`)\n return c.json({\n error: 'Module Entitlements Unavailable',\n message: 'Module entitlements must be verified before accessing this module.'\n }, 403)\n }\n\n if (!enabledModules.includes(moduleKey) && !enabledModules.includes('all')) {\n console.warn(`[ModuleGuard] Blocked request to module '${moduleKey}' for tenant=${c.get('tenantId')}`)\n return c.json({\n error: 'Module Not Licensed',\n message: `The module '${moduleKey}' is not enabled for your tenant.`\n }, 403)\n }\n\n await next()\n })\n}\n"],"mappings":";AAAA,SAAS,wBAAwB;AA8B1B,SAAS,kBAAkB,WAAmB,SAA8B;AACjF,SAAO,iBAA0B,OAAO,GAAG,SAAS;AAClD,UAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,aAAa;AACzD,QAAI,OAAO;AACT,aAAO,MAAM,KAAK;AAAA,IACpB;AAEA,UAAM,iBAAiB,EAAE,IAAI,gBAAgB;AAE7C,QAAI,CAAC,MAAM,QAAQ,cAAc,KAAK,eAAe,KAAK,YAAU,OAAO,WAAW,QAAQ,GAAG;AAC/F,cAAQ,KAAK,sDAAsD,SAAS,YAAY,EAAE,IAAI,UAAU,CAAC,EAAE;AAC3G,aAAO,EAAE,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,SAAS;AAAA,MACX,GAAG,GAAG;AAAA,IACR;AAEA,QAAI,CAAC,eAAe,SAAS,SAAS,KAAK,CAAC,eAAe,SAAS,KAAK,GAAG;AAC1E,cAAQ,KAAK,4CAA4C,SAAS,gBAAgB,EAAE,IAAI,UAAU,CAAC,EAAE;AACrG,aAAO,EAAE,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,SAAS,eAAe,SAAS;AAAA,MACnC,GAAG,GAAG;AAAA,IACR;AAEA,UAAM,KAAK;AAAA,EACb,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EntitlementError,
|
|
3
|
+
requiresEntitlementRuntime
|
|
4
|
+
} from "./chunk-46UWPEW2.js";
|
|
5
|
+
|
|
6
|
+
// src/mcp.ts
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
import { bodyLimit } from "hono/body-limit";
|
|
9
|
+
import Ajv from "ajv";
|
|
10
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
11
|
+
import addFormats from "ajv-formats";
|
|
12
|
+
function createMcpRouter(runtime, options) {
|
|
13
|
+
const router = new Hono();
|
|
14
|
+
const ajv = new Ajv({ strict: false, allErrors: true });
|
|
15
|
+
const ajv2020 = new Ajv2020({ strict: false, allErrors: true });
|
|
16
|
+
addFormats(ajv);
|
|
17
|
+
addFormats(ajv2020);
|
|
18
|
+
const entitled = async (p, id) => {
|
|
19
|
+
const rule = runtime.registry.get(id)?.entitlement;
|
|
20
|
+
return !rule || !requiresEntitlementRuntime(rule) || !!options.entitlements && (await options.entitlements.check(p, id, rule)).allowed;
|
|
21
|
+
};
|
|
22
|
+
const admit = async (p, plugin, operation) => {
|
|
23
|
+
if (!plugin.entitlement || !requiresEntitlementRuntime(plugin.entitlement)) return;
|
|
24
|
+
if (!options.entitlements) throw Error("Entitlement service unavailable");
|
|
25
|
+
await options.entitlements.admit(p, plugin.id, plugin.entitlement, operation);
|
|
26
|
+
};
|
|
27
|
+
const preview = async (p, plugin) => {
|
|
28
|
+
if (!plugin.entitlement || !requiresEntitlementRuntime(plugin.entitlement)) return null;
|
|
29
|
+
if (!options.entitlements) return { status: 503, error: "Entitlement service unavailable" };
|
|
30
|
+
const decision = await options.entitlements.check(p, plugin.id, plugin.entitlement);
|
|
31
|
+
return decision.allowed ? null : { status: decision.status, error: decision.reason };
|
|
32
|
+
};
|
|
33
|
+
router.use("*", bodyLimit({ maxSize: 1024 * 1024 }));
|
|
34
|
+
router.onError((error, c) => {
|
|
35
|
+
options.onError?.(error);
|
|
36
|
+
return c.json({ error: "MCP request failed" }, 500);
|
|
37
|
+
});
|
|
38
|
+
function tools() {
|
|
39
|
+
const names = /* @__PURE__ */ new Set();
|
|
40
|
+
return runtime.registry.getAll().flatMap((plugin) => (plugin.mcp?.tools ?? []).map((tool) => {
|
|
41
|
+
if (names.has(tool.name)) throw new Error(`Duplicate MCP tool name: ${tool.name}`);
|
|
42
|
+
names.add(tool.name);
|
|
43
|
+
return { plugin, tool };
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
async function resolveAIAdapters(principal2, pluginId, toolName, traceId) {
|
|
47
|
+
if (!options.resolveAIHostAdapters) return void 0;
|
|
48
|
+
return options.resolveAIHostAdapters({
|
|
49
|
+
principal: principal2,
|
|
50
|
+
pluginId,
|
|
51
|
+
toolName,
|
|
52
|
+
context: { tenantId: principal2.tenantId, userId: principal2.userId, operation: `mcp:${toolName}`, traceId }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function principal(c) {
|
|
56
|
+
const p = await options.resolvePrincipal(c);
|
|
57
|
+
if (!p || typeof p.userId !== "string" || !p.userId.trim() || typeof p.tenantId !== "string" || !p.tenantId.trim() || !Array.isArray(p.roles) || !p.roles.every((v) => typeof v === "string") || !Array.isArray(p.enabledPluginIds) || !p.enabledPluginIds.every((v) => typeof v === "string")) return null;
|
|
58
|
+
if (options.tenantId && p.tenantId !== options.tenantId) return null;
|
|
59
|
+
return { userId: p.userId, tenantId: p.tenantId, roles: [...p.roles], enabledPluginIds: [...p.enabledPluginIds] };
|
|
60
|
+
}
|
|
61
|
+
router.get("/tools", async (c) => {
|
|
62
|
+
const p = await principal(c);
|
|
63
|
+
if (!p) return c.json({ error: "Unauthorized" }, 401);
|
|
64
|
+
const active = /* @__PURE__ */ new Set();
|
|
65
|
+
for (const id of p.enabledPluginIds) if ((!options.isPluginEnabled || await options.isPluginEnabled(p, id)) && await entitled(p, id)) active.add(id);
|
|
66
|
+
return c.json({ tools: tools().filter(
|
|
67
|
+
({ plugin, tool }) => active.has(plugin.id) && runtime.gatekeeper.validateCall({ toolName: tool.name, userRoles: p.roles, tenantId: p.tenantId }).allowed
|
|
68
|
+
).map(({ plugin, tool }) => ({ name: tool.name, description: tool.description, inputSchema: tool.parameters, pluginId: plugin.id })) });
|
|
69
|
+
});
|
|
70
|
+
router.post("/execute", async (c) => {
|
|
71
|
+
const p = await principal(c);
|
|
72
|
+
if (!p) return c.json({ error: "Unauthorized" }, 401);
|
|
73
|
+
if (!/^application\/json(?:;|$)/i.test(c.req.header("Content-Type") ?? "")) return c.json({ error: "JSON content type required" }, 415);
|
|
74
|
+
const body = await c.req.json().catch(() => null);
|
|
75
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || typeof body.name !== "string" || !body.args || typeof body.args !== "object" || Array.isArray(body.args) || Object.keys(body).some((k) => k !== "name" && k !== "args")) return c.json({ error: "Expected { name, args }" }, 400);
|
|
76
|
+
const entry = tools().find(({ tool }) => tool.name === body.name);
|
|
77
|
+
if (!entry || !p.enabledPluginIds.includes(entry.plugin.id) || options.isPluginEnabled && !await options.isPluginEnabled(p, entry.plugin.id)) return c.json({ error: "Tool unavailable" }, 404);
|
|
78
|
+
const admission = await preview(p, entry.plugin);
|
|
79
|
+
if (admission) return c.json({ error: admission.error }, admission.status);
|
|
80
|
+
const traceId = crypto.randomUUID();
|
|
81
|
+
const verdict = runtime.gatekeeper.validateCall({ toolName: entry.tool.name, userRoles: p.roles, tenantId: p.tenantId, traceId });
|
|
82
|
+
const audit = {
|
|
83
|
+
toolName: entry.tool.name,
|
|
84
|
+
userId: p.userId,
|
|
85
|
+
tenantId: p.tenantId,
|
|
86
|
+
traceId,
|
|
87
|
+
riskLevel: verdict.riskLevel,
|
|
88
|
+
approvalLevel: verdict.approvalLevel
|
|
89
|
+
};
|
|
90
|
+
if (!verdict.allowed) {
|
|
91
|
+
runtime.gatekeeper.logCall({ ...audit, status: "BLOCKED", reason: verdict.reason });
|
|
92
|
+
return c.json({ error: "Forbidden", traceId }, 403);
|
|
93
|
+
}
|
|
94
|
+
const schema = entry.tool.parameters;
|
|
95
|
+
const validator = (String(schema.$schema ?? "").includes("2020-12") ? ajv2020 : ajv).compile(schema);
|
|
96
|
+
if (!validator(body.args)) return c.json({ error: "Invalid tool arguments", traceId }, 400);
|
|
97
|
+
if (verdict.requireHumanApproval) {
|
|
98
|
+
runtime.gatekeeper.logCall({ ...audit, status: "PENDING_APPROVAL" });
|
|
99
|
+
return c.json({ requireHumanApproval: true, approvalLevel: verdict.approvalLevel, traceId }, 202);
|
|
100
|
+
}
|
|
101
|
+
if (options.isPluginEnabled && !await options.isPluginEnabled(p, entry.plugin.id)) return c.json({ error: "Tool unavailable" }, 404);
|
|
102
|
+
const dependencies = await options.createToolContext?.(p, entry.plugin.id) ?? {};
|
|
103
|
+
const aiHostAdapters = await resolveAIAdapters(p, entry.plugin.id, entry.tool.name, traceId);
|
|
104
|
+
if (options.isPluginEnabled && !await options.isPluginEnabled(p, entry.plugin.id)) return c.json({ error: "Tool unavailable" }, 404);
|
|
105
|
+
const context = Object.freeze({
|
|
106
|
+
...dependencies,
|
|
107
|
+
...aiHostAdapters ? { aiHostAdapters: Object.freeze(aiHostAdapters) } : {},
|
|
108
|
+
tenantId: p.tenantId,
|
|
109
|
+
userId: p.userId,
|
|
110
|
+
roles: Object.freeze([...p.roles]),
|
|
111
|
+
pluginId: entry.plugin.id,
|
|
112
|
+
traceId
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
await admit(p, entry.plugin, `mcp:${entry.tool.name}`);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error instanceof EntitlementError) {
|
|
118
|
+
runtime.gatekeeper.logCall({ ...audit, status: "BLOCKED", reason: error.message });
|
|
119
|
+
return c.json({ error: error.message }, error.decision.status);
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
runtime.gatekeeper.logCall({ ...audit, status: "ALLOWED" });
|
|
124
|
+
const result = await entry.tool.handler(body.args, context);
|
|
125
|
+
return c.json({ result, traceId });
|
|
126
|
+
});
|
|
127
|
+
const sseSessions = /* @__PURE__ */ new Map();
|
|
128
|
+
const boundedNumber = (value, fallback, minimum) => typeof value === "number" && Number.isFinite(value) ? Math.max(minimum, Math.floor(value)) : fallback;
|
|
129
|
+
const sseSessionTtlMs = boundedNumber(options.sseSessionTtlMs, 30 * 60 * 1e3, 1e3);
|
|
130
|
+
const maxSseSessions = boundedNumber(options.maxSseSessions, 1e3, 1);
|
|
131
|
+
function purgeExpiredSseSessions(now = Date.now()) {
|
|
132
|
+
for (const [id, session] of sseSessions) {
|
|
133
|
+
if (now - session.createdAt >= sseSessionTtlMs || now - session.lastActivityAt >= sseSessionTtlMs) sseSessions.delete(id);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
router.get("/sse", async (c) => {
|
|
137
|
+
const p = await principal(c);
|
|
138
|
+
if (!p) return c.json({ error: "Unauthorized" }, 401);
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
purgeExpiredSseSessions(now);
|
|
141
|
+
if (sseSessions.size >= maxSseSessions) return c.json({ error: "MCP SSE capacity exhausted" }, 503);
|
|
142
|
+
const sessionId = crypto.randomUUID();
|
|
143
|
+
const endpointUrl = `${new URL(c.req.url).pathname.replace(/\/sse$/, "")}/message?sessionId=${sessionId}`;
|
|
144
|
+
let cleanup;
|
|
145
|
+
const stream = new ReadableStream({
|
|
146
|
+
start(controller) {
|
|
147
|
+
const encoder = new TextEncoder();
|
|
148
|
+
const send = (data) => {
|
|
149
|
+
try {
|
|
150
|
+
const session = sseSessions.get(sessionId);
|
|
151
|
+
if (session) session.lastActivityAt = Date.now();
|
|
152
|
+
controller.enqueue(encoder.encode(data));
|
|
153
|
+
} catch {
|
|
154
|
+
cleanup?.();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
sseSessions.set(sessionId, { userId: p.userId, tenantId: p.tenantId, createdAt: now, lastActivityAt: now, send });
|
|
158
|
+
cleanup = () => sseSessions.delete(sessionId);
|
|
159
|
+
send(`event: endpoint
|
|
160
|
+
data: ${endpointUrl}
|
|
161
|
+
|
|
162
|
+
`);
|
|
163
|
+
},
|
|
164
|
+
cancel() {
|
|
165
|
+
cleanup?.();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return new Response(stream, {
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": "text/event-stream",
|
|
171
|
+
"Cache-Control": "no-cache",
|
|
172
|
+
Connection: "keep-alive"
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
router.post("/message", async (c) => {
|
|
177
|
+
const p = await principal(c);
|
|
178
|
+
if (!p) return c.json({ jsonrpc: "2.0", error: { code: -32e3, message: "Unauthorized" }, id: null }, 401);
|
|
179
|
+
const body = await c.req.json().catch(() => null);
|
|
180
|
+
if (!body || typeof body !== "object" || body.jsonrpc !== "2.0" || typeof body.method !== "string") {
|
|
181
|
+
return c.json({ jsonrpc: "2.0", error: { code: -32600, message: "Invalid Request" }, id: body?.id ?? null }, 400);
|
|
182
|
+
}
|
|
183
|
+
const id = body.id ?? null;
|
|
184
|
+
const sessionId = c.req.query("sessionId");
|
|
185
|
+
purgeExpiredSseSessions();
|
|
186
|
+
const session = sessionId ? sseSessions.get(sessionId) : void 0;
|
|
187
|
+
if (sessionId && !session) return c.json({ jsonrpc: "2.0", error: { code: -32e3, message: "MCP session unavailable" }, id }, 404);
|
|
188
|
+
if (session && (session.userId !== p.userId || session.tenantId !== p.tenantId)) {
|
|
189
|
+
return c.json({ jsonrpc: "2.0", error: { code: -32003, message: "MCP session owner mismatch" }, id }, 403);
|
|
190
|
+
}
|
|
191
|
+
if (session) session.lastActivityAt = Date.now();
|
|
192
|
+
const sendRpcResponse = (payload) => {
|
|
193
|
+
const serialized = JSON.stringify(payload);
|
|
194
|
+
if (session) {
|
|
195
|
+
session.send(`event: message
|
|
196
|
+
data: ${serialized}
|
|
197
|
+
|
|
198
|
+
`);
|
|
199
|
+
}
|
|
200
|
+
return c.json(payload);
|
|
201
|
+
};
|
|
202
|
+
if (body.method === "tools/list") {
|
|
203
|
+
const active = /* @__PURE__ */ new Set();
|
|
204
|
+
for (const pid of p.enabledPluginIds) if ((!options.isPluginEnabled || await options.isPluginEnabled(p, pid)) && await entitled(p, pid)) active.add(pid);
|
|
205
|
+
const available = tools().filter(
|
|
206
|
+
({ plugin, tool }) => active.has(plugin.id) && runtime.gatekeeper.validateCall({ toolName: tool.name, userRoles: p.roles, tenantId: p.tenantId }).allowed
|
|
207
|
+
).map(({ plugin, tool }) => ({
|
|
208
|
+
name: tool.name,
|
|
209
|
+
description: tool.description,
|
|
210
|
+
inputSchema: tool.parameters,
|
|
211
|
+
pluginId: plugin.id
|
|
212
|
+
}));
|
|
213
|
+
return sendRpcResponse({ jsonrpc: "2.0", result: { tools: available }, id });
|
|
214
|
+
}
|
|
215
|
+
if (body.method === "tools/call") {
|
|
216
|
+
const toolName = body.params?.name;
|
|
217
|
+
const toolArgs = body.params?.arguments ?? {};
|
|
218
|
+
if (typeof toolName !== "string" || typeof toolArgs !== "object" || Array.isArray(toolArgs)) {
|
|
219
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32602, message: "Invalid params" }, id });
|
|
220
|
+
}
|
|
221
|
+
const entry = tools().find(({ tool }) => tool.name === toolName);
|
|
222
|
+
if (!entry || !p.enabledPluginIds.includes(entry.plugin.id) || options.isPluginEnabled && !await options.isPluginEnabled(p, entry.plugin.id)) {
|
|
223
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32601, message: "Tool not found" }, id });
|
|
224
|
+
}
|
|
225
|
+
const admission = await preview(p, entry.plugin);
|
|
226
|
+
if (admission) return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32003, message: admission.error, data: { status: admission.status } }, id });
|
|
227
|
+
const traceId = crypto.randomUUID();
|
|
228
|
+
const verdict = runtime.gatekeeper.validateCall({ toolName: entry.tool.name, userRoles: p.roles, tenantId: p.tenantId, traceId });
|
|
229
|
+
const audit = {
|
|
230
|
+
toolName: entry.tool.name,
|
|
231
|
+
userId: p.userId,
|
|
232
|
+
tenantId: p.tenantId,
|
|
233
|
+
traceId,
|
|
234
|
+
riskLevel: verdict.riskLevel,
|
|
235
|
+
approvalLevel: verdict.approvalLevel
|
|
236
|
+
};
|
|
237
|
+
if (!verdict.allowed) {
|
|
238
|
+
runtime.gatekeeper.logCall({ ...audit, status: "BLOCKED", reason: verdict.reason });
|
|
239
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32003, message: "Forbidden: tool execution denied" }, id });
|
|
240
|
+
}
|
|
241
|
+
const schema = entry.tool.parameters;
|
|
242
|
+
const validator = (String(schema.$schema ?? "").includes("2020-12") ? ajv2020 : ajv).compile(schema);
|
|
243
|
+
if (!validator(toolArgs)) {
|
|
244
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32602, message: "Invalid tool arguments" }, id });
|
|
245
|
+
}
|
|
246
|
+
if (verdict.requireHumanApproval) {
|
|
247
|
+
runtime.gatekeeper.logCall({ ...audit, status: "PENDING_APPROVAL" });
|
|
248
|
+
return sendRpcResponse({
|
|
249
|
+
jsonrpc: "2.0",
|
|
250
|
+
result: {
|
|
251
|
+
content: [{ type: "text", text: `Human approval required (${verdict.approvalLevel})` }],
|
|
252
|
+
isError: true,
|
|
253
|
+
requireHumanApproval: true,
|
|
254
|
+
approvalLevel: verdict.approvalLevel,
|
|
255
|
+
traceId
|
|
256
|
+
},
|
|
257
|
+
id
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (options.isPluginEnabled && !await options.isPluginEnabled(p, entry.plugin.id)) {
|
|
261
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32601, message: "Tool not found" }, id });
|
|
262
|
+
}
|
|
263
|
+
const dependencies = await options.createToolContext?.(p, entry.plugin.id) ?? {};
|
|
264
|
+
const aiHostAdapters = await resolveAIAdapters(p, entry.plugin.id, entry.tool.name, traceId);
|
|
265
|
+
if (options.isPluginEnabled && !await options.isPluginEnabled(p, entry.plugin.id)) {
|
|
266
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32601, message: "Tool not found" }, id });
|
|
267
|
+
}
|
|
268
|
+
const context = Object.freeze({
|
|
269
|
+
...dependencies,
|
|
270
|
+
...aiHostAdapters ? { aiHostAdapters: Object.freeze(aiHostAdapters) } : {},
|
|
271
|
+
tenantId: p.tenantId,
|
|
272
|
+
userId: p.userId,
|
|
273
|
+
roles: Object.freeze([...p.roles]),
|
|
274
|
+
pluginId: entry.plugin.id,
|
|
275
|
+
traceId
|
|
276
|
+
});
|
|
277
|
+
try {
|
|
278
|
+
await admit(p, entry.plugin, `mcp:${entry.tool.name}`);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (error instanceof EntitlementError) {
|
|
281
|
+
runtime.gatekeeper.logCall({ ...audit, status: "BLOCKED", reason: error.message });
|
|
282
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32003, message: error.message, data: { status: error.decision.status } }, id });
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
runtime.gatekeeper.logCall({ ...audit, status: "ALLOWED" });
|
|
287
|
+
const handlerResult = await entry.tool.handler(toolArgs, context);
|
|
288
|
+
return sendRpcResponse({
|
|
289
|
+
jsonrpc: "2.0",
|
|
290
|
+
result: {
|
|
291
|
+
content: [{ type: "text", text: JSON.stringify(handlerResult) }],
|
|
292
|
+
traceId
|
|
293
|
+
},
|
|
294
|
+
id
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
return sendRpcResponse({ jsonrpc: "2.0", error: { code: -32601, message: "Method not found" }, id });
|
|
298
|
+
});
|
|
299
|
+
return router;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export {
|
|
303
|
+
createMcpRouter
|
|
304
|
+
};
|
|
305
|
+
//# sourceMappingURL=chunk-VF5YDZJC.js.map
|