atabey 0.0.13 → 0.0.15
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/ATABEY.md +1 -1
- package/PRIVACY.md +8 -14
- package/README.md +405 -56
- package/bin/cli.js +72 -5
- package/dist/framework-mcp/src/index.js +988 -63
- package/dist/framework-mcp/src/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/compliance/mask_pii.d.ts +17 -0
- package/dist/framework-mcp/src/tools/compliance/mask_pii.js +56 -0
- package/dist/framework-mcp/src/tools/compliance/mask_pii.js.map +1 -0
- package/dist/framework-mcp/src/tools/definitions.js +10 -9
- package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
- package/dist/framework-mcp/src/tools/framework/run_tests.js +25 -0
- package/dist/framework-mcp/src/tools/framework/run_tests.js.map +1 -1
- package/dist/framework-mcp/src/tools/index.js +7 -8
- package/dist/framework-mcp/src/tools/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/approve_operation.d.ts +22 -0
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js +81 -0
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js.map +1 -0
- package/dist/framework-mcp/src/tools/messaging/ask_human.d.ts +15 -0
- package/dist/framework-mcp/src/tools/messaging/ask_human.js +89 -20
- package/dist/framework-mcp/src/tools/messaging/ask_human.js.map +1 -1
- package/dist/framework-mcp/src/tools/observability/check_ports.d.ts +1 -0
- package/dist/framework-mcp/src/tools/observability/check_ports.js +24 -8
- package/dist/framework-mcp/src/tools/observability/check_ports.js.map +1 -1
- package/dist/framework-mcp/src/tools/quality/analyze_code_quality.js +13 -64
- package/dist/framework-mcp/src/tools/quality/analyze_code_quality.js.map +1 -1
- package/dist/framework-mcp/src/tools/schemas.d.ts +48 -53
- package/dist/framework-mcp/src/tools/schemas.js +18 -22
- package/dist/framework-mcp/src/tools/schemas.js.map +1 -1
- package/dist/framework-mcp/src/tools/shell/run_command.js +146 -90
- package/dist/framework-mcp/src/tools/shell/run_command.js.map +1 -1
- package/dist/framework-mcp/src/tools/types.d.ts +11 -105
- package/dist/framework-mcp/src/tools/types.js +1 -0
- package/dist/framework-mcp/src/tools/types.js.map +1 -1
- package/dist/framework-mcp/src/utils/auth.d.ts +36 -0
- package/dist/framework-mcp/src/utils/auth.js +79 -0
- package/dist/framework-mcp/src/utils/auth.js.map +1 -0
- package/dist/framework-mcp/src/utils/auto-rollback.d.ts +71 -0
- package/dist/framework-mcp/src/utils/auto-rollback.js +266 -0
- package/dist/framework-mcp/src/utils/auto-rollback.js.map +1 -0
- package/dist/framework-mcp/src/utils/compliance.js +12 -4
- package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
- package/dist/framework-mcp/src/utils/context-optimizer.d.ts +69 -0
- package/dist/framework-mcp/src/utils/context-optimizer.js +157 -0
- package/dist/framework-mcp/src/utils/context-optimizer.js.map +1 -0
- package/dist/framework-mcp/src/utils/discipline.d.ts +68 -0
- package/dist/framework-mcp/src/utils/discipline.js +218 -0
- package/dist/framework-mcp/src/utils/discipline.js.map +1 -0
- package/dist/framework-mcp/src/utils/finops.d.ts +143 -0
- package/dist/framework-mcp/src/utils/finops.js +326 -0
- package/dist/framework-mcp/src/utils/finops.js.map +1 -0
- package/dist/framework-mcp/src/utils/human-in-loop.d.ts +95 -0
- package/dist/framework-mcp/src/utils/human-in-loop.js +216 -0
- package/dist/framework-mcp/src/utils/human-in-loop.js.map +1 -0
- package/dist/framework-mcp/src/utils/license-scanner.d.ts +71 -0
- package/dist/framework-mcp/src/utils/license-scanner.js +284 -0
- package/dist/framework-mcp/src/utils/license-scanner.js.map +1 -0
- package/dist/framework-mcp/src/utils/loop-detector.d.ts +105 -0
- package/dist/framework-mcp/src/utils/loop-detector.js +362 -0
- package/dist/framework-mcp/src/utils/loop-detector.js.map +1 -0
- package/dist/framework-mcp/src/utils/quality.d.ts +19 -0
- package/dist/framework-mcp/src/utils/quality.js +125 -0
- package/dist/framework-mcp/src/utils/quality.js.map +1 -0
- package/dist/framework-mcp/src/utils/rules-engine.d.ts +78 -0
- package/dist/framework-mcp/src/utils/rules-engine.js +232 -0
- package/dist/framework-mcp/src/utils/rules-engine.js.map +1 -0
- package/dist/framework-mcp/src/utils/silent-router.d.ts +49 -0
- package/dist/framework-mcp/src/utils/silent-router.js +155 -0
- package/dist/framework-mcp/src/utils/silent-router.js.map +1 -0
- package/dist/framework-mcp/src/utils/storage.d.ts +1 -0
- package/dist/framework-mcp/src/utils/storage.js +2 -2
- package/dist/framework-mcp/src/utils/storage.js.map +1 -1
- package/dist/framework-mcp/src/utils/telemetry-streamer.d.ts +133 -0
- package/dist/framework-mcp/src/utils/telemetry-streamer.js +397 -0
- package/dist/framework-mcp/src/utils/telemetry-streamer.js.map +1 -0
- package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js +2 -2
- package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/framework/run_tests.test.d.ts +1 -0
- package/dist/framework-mcp/tests/tools/framework/run_tests.test.js +49 -0
- package/dist/framework-mcp/tests/tools/framework/run_tests.test.js.map +1 -0
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +14 -5
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.d.ts +1 -0
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.js +44 -0
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.js.map +1 -0
- package/dist/framework-mcp/tests/tools/shell/run_command.test.js +44 -14
- package/dist/framework-mcp/tests/tools/shell/run_command.test.js.map +1 -1
- package/dist/framework-mcp/tests/utils/auto-rollback.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/auto-rollback.test.js +178 -0
- package/dist/framework-mcp/tests/utils/auto-rollback.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/context-optimizer.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/context-optimizer.test.js +62 -0
- package/dist/framework-mcp/tests/utils/context-optimizer.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/finops.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/finops.test.js +157 -0
- package/dist/framework-mcp/tests/utils/finops.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/human-in-loop.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/human-in-loop.test.js +59 -0
- package/dist/framework-mcp/tests/utils/human-in-loop.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/license-scanner.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/license-scanner.test.js +131 -0
- package/dist/framework-mcp/tests/utils/license-scanner.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/loop-detector.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/loop-detector.test.js +204 -0
- package/dist/framework-mcp/tests/utils/loop-detector.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/rules-engine.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/rules-engine.test.js +106 -0
- package/dist/framework-mcp/tests/utils/rules-engine.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/telemetry-streamer.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/telemetry-streamer.test.js +201 -0
- package/dist/framework-mcp/tests/utils/telemetry-streamer.test.js.map +1 -0
- package/dist/src/cli/commands/check.js +16 -1
- package/dist/src/cli/commands/check.js.map +1 -1
- package/dist/src/cli/commands/dashboard.js +188 -164
- package/dist/src/cli/commands/dashboard.js.map +1 -1
- package/dist/src/cli/commands/gateway.js +2 -2
- package/dist/src/cli/commands/gateway.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-core.d.ts +2 -0
- package/dist/src/cli/commands/init/scaffold-core.js +16 -1
- package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-ops.js +2 -2
- package/dist/src/cli/commands/init/scaffold-ops.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-standards.js +13 -1
- package/dist/src/cli/commands/init/scaffold-standards.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts +4 -0
- package/dist/src/cli/commands/init.js +94 -31
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/mcp.js +3 -1
- package/dist/src/cli/commands/mcp.js.map +1 -1
- package/dist/src/cli/commands/quickstart.d.ts +1 -1
- package/dist/src/cli/commands/quickstart.js +12 -24
- package/dist/src/cli/commands/quickstart.js.map +1 -1
- package/dist/src/cli/commands/script.js +8 -1
- package/dist/src/cli/commands/script.js.map +1 -1
- package/dist/src/cli/index.js +24 -23
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/shims.js +7 -7
- package/dist/src/cli/utils/pkg.js +2 -3
- package/dist/src/cli/utils/pkg.js.map +1 -1
- package/dist/src/cli/utils/quality.d.ts +10 -0
- package/dist/src/cli/utils/quality.js +10 -0
- package/dist/src/cli/utils/quality.js.map +1 -0
- package/dist/src/modules/agents/definitions.js +45 -40
- package/dist/src/modules/agents/definitions.js.map +1 -1
- package/dist/src/modules/agents/registry/manager.js +6 -1
- package/dist/src/modules/agents/registry/manager.js.map +1 -1
- package/dist/src/modules/engines/agent-executor.d.ts +6 -10
- package/dist/src/modules/engines/agent-executor.js +63 -21
- package/dist/src/modules/engines/agent-executor.js.map +1 -1
- package/dist/src/modules/engines/agent-loop.d.ts +34 -0
- package/dist/src/modules/engines/agent-loop.js +102 -0
- package/dist/src/modules/engines/agent-loop.js.map +1 -0
- package/dist/src/modules/engines/evaluation-engine.d.ts +12 -2
- package/dist/src/modules/engines/evaluation-engine.js +78 -11
- package/dist/src/modules/engines/evaluation-engine.js.map +1 -1
- package/dist/src/modules/engines/quality-gate.d.ts +13 -5
- package/dist/src/modules/engines/quality-gate.js +39 -31
- package/dist/src/modules/engines/quality-gate.js.map +1 -1
- package/dist/src/modules/engines/routing-engine.d.ts +5 -3
- package/dist/src/modules/engines/routing-engine.js +16 -9
- package/dist/src/modules/engines/routing-engine.js.map +1 -1
- package/dist/src/modules/gateway/cost-tracker.d.ts +5 -0
- package/dist/src/modules/gateway/cost-tracker.js +71 -0
- package/dist/src/modules/gateway/cost-tracker.js.map +1 -1
- package/dist/src/modules/gateway/gateway.d.ts +4 -0
- package/dist/src/modules/gateway/gateway.js +8 -0
- package/dist/src/modules/gateway/gateway.js.map +1 -1
- package/dist/src/shared/audit.js +3 -2
- package/dist/src/shared/audit.js.map +1 -1
- package/dist/src/shared/pii.d.ts +41 -9
- package/dist/src/shared/pii.js +122 -31
- package/dist/src/shared/pii.js.map +1 -1
- package/dist/src/shared/storage.d.ts +62 -0
- package/dist/src/shared/storage.js +47 -7
- package/dist/src/shared/storage.js.map +1 -1
- package/dist/tests/embedding.test.d.ts +1 -0
- package/dist/tests/embedding.test.js +82 -0
- package/dist/tests/embedding.test.js.map +1 -0
- package/dist/tests/engines/evaluation-engine.test.d.ts +1 -0
- package/dist/tests/engines/evaluation-engine.test.js +71 -0
- package/dist/tests/engines/evaluation-engine.test.js.map +1 -0
- package/dist/tests/engines/planning-engine.test.d.ts +1 -0
- package/dist/tests/engines/planning-engine.test.js +100 -0
- package/dist/tests/engines/planning-engine.test.js.map +1 -0
- package/dist/tests/engines/quality-gate.test.d.ts +1 -0
- package/dist/tests/engines/quality-gate.test.js +59 -0
- package/dist/tests/engines/quality-gate.test.js.map +1 -0
- package/dist/tests/engines/risk-engine.test.d.ts +1 -0
- package/dist/tests/engines/risk-engine.test.js +80 -0
- package/dist/tests/engines/risk-engine.test.js.map +1 -0
- package/dist/tests/engines/routing-engine.test.d.ts +1 -0
- package/dist/tests/engines/routing-engine.test.js +78 -0
- package/dist/tests/engines/routing-engine.test.js.map +1 -0
- package/dist/tests/integration/hermes_locking.test.js +1 -0
- package/dist/tests/integration/hermes_locking.test.js.map +1 -1
- package/dist/tests/modules/engines/agent-executor.test.js +1 -1
- package/dist/tests/modules/engines/agent-executor.test.js.map +1 -1
- package/dist/tests/modules/gateway/cost-tracker.test.js +20 -1
- package/dist/tests/modules/gateway/cost-tracker.test.js.map +1 -1
- package/dist/tests/shared/lock.test.d.ts +1 -0
- package/dist/tests/shared/lock.test.js +95 -0
- package/dist/tests/shared/lock.test.js.map +1 -0
- package/dist/tests/shared/pii.test.d.ts +1 -0
- package/dist/tests/shared/pii.test.js +183 -0
- package/dist/tests/shared/pii.test.js.map +1 -0
- package/framework-mcp/README.md +307 -35
- package/framework-mcp/dist/dashboard/assets/index-B_rK57vi.js +1221 -0
- package/framework-mcp/dist/dashboard/index.html +1 -1
- package/framework-mcp/dist/framework-mcp/src/index.js +988 -63
- package/framework-mcp/dist/framework-mcp/src/tools/compliance/mask_pii.js +55 -0
- package/framework-mcp/dist/framework-mcp/src/tools/definitions.js +10 -9
- package/framework-mcp/dist/framework-mcp/src/tools/framework/run_tests.js +25 -0
- package/framework-mcp/dist/framework-mcp/src/tools/index.js +7 -8
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/approve_operation.js +80 -0
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/ask_human.js +89 -20
- package/framework-mcp/dist/framework-mcp/src/tools/observability/check_ports.js +24 -8
- package/framework-mcp/dist/framework-mcp/src/tools/quality/analyze_code_quality.js +13 -64
- package/framework-mcp/dist/framework-mcp/src/tools/schemas.js +18 -22
- package/framework-mcp/dist/framework-mcp/src/tools/shell/run_command.js +146 -90
- package/framework-mcp/dist/framework-mcp/src/tools/types.js +1 -0
- package/framework-mcp/dist/framework-mcp/src/utils/auth.js +78 -0
- package/framework-mcp/dist/framework-mcp/src/utils/auto-rollback.js +265 -0
- package/framework-mcp/dist/framework-mcp/src/utils/compliance.js +12 -4
- package/framework-mcp/dist/framework-mcp/src/utils/context-optimizer.js +156 -0
- package/framework-mcp/dist/framework-mcp/src/utils/discipline.js +217 -0
- package/framework-mcp/dist/framework-mcp/src/utils/finops.js +325 -0
- package/framework-mcp/dist/framework-mcp/src/utils/human-in-loop.js +215 -0
- package/framework-mcp/dist/framework-mcp/src/utils/license-scanner.js +283 -0
- package/framework-mcp/dist/framework-mcp/src/utils/loop-detector.js +361 -0
- package/framework-mcp/dist/framework-mcp/src/utils/quality.js +124 -0
- package/framework-mcp/dist/framework-mcp/src/utils/rules-engine.js +231 -0
- package/framework-mcp/dist/framework-mcp/src/utils/silent-router.js +154 -0
- package/framework-mcp/dist/framework-mcp/src/utils/storage.js +2 -2
- package/framework-mcp/dist/framework-mcp/src/utils/telemetry-streamer.js +396 -0
- package/framework-mcp/dist/src/cli/platforms/core.js +72 -0
- package/framework-mcp/dist/src/cli/platforms/index.js +5 -0
- package/framework-mcp/dist/src/cli/platforms/paths.js +101 -0
- package/framework-mcp/dist/src/cli/platforms/scaffold.js +72 -0
- package/framework-mcp/dist/src/cli/platforms/utils.js +75 -0
- package/framework-mcp/dist/src/cli/utils/claude.js +57 -0
- package/framework-mcp/dist/src/cli/utils/compliance.js +12 -0
- package/framework-mcp/dist/src/cli/utils/fs.js +138 -0
- package/framework-mcp/dist/src/cli/utils/pkg.js +282 -0
- package/framework-mcp/dist/src/cli/utils/quality.js +9 -0
- package/framework-mcp/dist/src/modules/agents/definitions.js +563 -0
- package/framework-mcp/dist/src/modules/agents/registry/analyst.js +39 -0
- package/framework-mcp/dist/src/modules/agents/registry/architect.js +42 -0
- package/framework-mcp/dist/src/modules/agents/registry/backend.js +50 -0
- package/framework-mcp/dist/src/modules/agents/registry/database.js +45 -0
- package/framework-mcp/dist/src/modules/agents/registry/devops.js +45 -0
- package/framework-mcp/dist/src/modules/agents/registry/explorer.js +37 -0
- package/framework-mcp/dist/src/modules/agents/registry/frontend.js +52 -0
- package/framework-mcp/dist/src/modules/agents/registry/git.js +36 -0
- package/framework-mcp/dist/src/modules/agents/registry/manager.js +64 -0
- package/framework-mcp/dist/src/modules/agents/registry/mobile.js +41 -0
- package/framework-mcp/dist/src/modules/agents/registry/native.js +41 -0
- package/framework-mcp/dist/src/modules/agents/registry/quality.js +43 -0
- package/framework-mcp/dist/src/modules/agents/registry/security.js +42 -0
- package/framework-mcp/dist/src/modules/agents/types.js +1 -0
- package/framework-mcp/dist/src/modules/engines/evaluation-engine.js +169 -0
- package/framework-mcp/dist/src/modules/engines/quality-gate.js +92 -0
- package/framework-mcp/dist/src/modules/engines/risk-engine.js +105 -0
- package/framework-mcp/dist/src/modules/engines/routing-engine.js +408 -0
- package/framework-mcp/dist/src/modules/engines/types.js +1 -0
- package/framework-mcp/dist/src/modules/providers/definitions.js +140 -0
- package/framework-mcp/dist/src/modules/providers/registry.js +18 -0
- package/framework-mcp/dist/src/modules/providers/shared.js +104 -0
- package/framework-mcp/dist/src/modules/providers/types.js +1 -0
- package/framework-mcp/dist/src/modules/skills/adapter-skills.js +449 -0
- package/framework-mcp/dist/src/modules/skills/definitions.js +70 -0
- package/framework-mcp/dist/src/shared/audit.js +220 -0
- package/framework-mcp/dist/src/shared/errors.js +68 -0
- package/framework-mcp/dist/src/shared/lock.js +159 -0
- package/framework-mcp/dist/src/shared/pii.js +122 -31
- package/framework-mcp/dist/src/shared/retention.js +170 -0
- package/framework-mcp/dist/src/shared/storage.js +47 -7
- package/framework-mcp/dist/src/shared/string.js +29 -0
- package/framework-mcp/package.json +8 -2
- package/mcp.json +12 -6
- package/package.json +11 -5
- package/templates/full/ATABEY_FULL.md +1 -1
- package/framework-mcp/dist/dashboard/assets/index-BAPGLiWE.js +0 -267
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ─── FINOPS / BUDGET ENFORCEMENT ──────────────────────────────────
|
|
3
|
+
*
|
|
4
|
+
* Enterprise budget management for AI token consumption.
|
|
5
|
+
* Enforces strict cost limits per team, agent, or project.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Team-based budget allocation (monthly/quarterly)
|
|
9
|
+
* - Per-agent spending limits
|
|
10
|
+
* - Automatic shutdown when budget exceeded
|
|
11
|
+
* - Central server sync for budget policies
|
|
12
|
+
* - Real-time cost tracking
|
|
13
|
+
* - Budget alerts at configurable thresholds (50%, 80%, 90%, 100%)
|
|
14
|
+
*
|
|
15
|
+
* Architecture:
|
|
16
|
+
* [Local MCP] → Budget Check → [Enterprise Server] → Shutdown Signal
|
|
17
|
+
*
|
|
18
|
+
* Environment Variables:
|
|
19
|
+
* ATABEY_BUDGET_TEAM - Team name for budget grouping
|
|
20
|
+
* ATABEY_BUDGET_MONTHLY - Monthly budget in USD (local override)
|
|
21
|
+
* ATABEY_BUDGET_AGENT_MAX - Max spend per agent in USD
|
|
22
|
+
* ATABEY_BUDGET_SYNC_URL - Enterprise server URL for budget sync
|
|
23
|
+
*/
|
|
24
|
+
import fs from "fs";
|
|
25
|
+
import path from "path";
|
|
26
|
+
// ─── Configuration ────────────────────────────────────────────────
|
|
27
|
+
const CONFIG = {
|
|
28
|
+
/** Team name for budget grouping */
|
|
29
|
+
TEAM: process.env.ATABEY_BUDGET_TEAM || "default",
|
|
30
|
+
/** Monthly budget in USD (0 = unlimited) */
|
|
31
|
+
MONTHLY_BUDGET: parseFloat(process.env.ATABEY_BUDGET_MONTHLY || "0"),
|
|
32
|
+
/** Max spend per agent in USD (0 = unlimited) */
|
|
33
|
+
AGENT_MAX_BUDGET: parseFloat(process.env.ATABEY_BUDGET_AGENT_MAX || "0"),
|
|
34
|
+
/** Enterprise server URL for budget policy sync */
|
|
35
|
+
SYNC_URL: process.env.ATABEY_BUDGET_SYNC_URL || "",
|
|
36
|
+
/** Sync token */
|
|
37
|
+
SYNC_TOKEN: process.env.ATABEY_BUDGET_SYNC_TOKEN || "",
|
|
38
|
+
/** Budget alert thresholds (percentages) */
|
|
39
|
+
ALERT_THRESHOLDS: (process.env.ATABEY_BUDGET_ALERT_THRESHOLDS || "50,80,90,100")
|
|
40
|
+
.split(",").map(t => parseInt(t.trim(), 10)).filter(t => !isNaN(t)),
|
|
41
|
+
/** Cost per 1K tokens (USD) – default: $0.003 for Claude */
|
|
42
|
+
COST_PER_1K_TOKENS: parseFloat(process.env.ATABEY_COST_PER_1K_TOKENS || "0.003"),
|
|
43
|
+
/** Sync interval in ms */
|
|
44
|
+
SYNC_INTERVAL_MS: parseInt(process.env.ATABEY_BUDGET_SYNC_INTERVAL || "60000", 10),
|
|
45
|
+
/** Enable/disable budget enforcement */
|
|
46
|
+
ENABLED: process.env.ATABEY_BUDGET_ENABLED === "true",
|
|
47
|
+
};
|
|
48
|
+
// ─── Budget Manager ───────────────────────────────────────────────
|
|
49
|
+
export class BudgetManager {
|
|
50
|
+
static instance;
|
|
51
|
+
state;
|
|
52
|
+
syncTimer = null;
|
|
53
|
+
localMetricsPath;
|
|
54
|
+
constructor() {
|
|
55
|
+
const now = new Date();
|
|
56
|
+
const periodStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
|
|
57
|
+
const periodEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString();
|
|
58
|
+
this.state = {
|
|
59
|
+
team: CONFIG.TEAM,
|
|
60
|
+
monthlySpend: 0,
|
|
61
|
+
agentSpend: {},
|
|
62
|
+
periodStart,
|
|
63
|
+
periodEnd,
|
|
64
|
+
blocked: false,
|
|
65
|
+
blockReason: null,
|
|
66
|
+
triggeredAlerts: [],
|
|
67
|
+
lastSync: null,
|
|
68
|
+
};
|
|
69
|
+
const frameworkDir = process.env.ATABEY_FRAMEWORK_DIR || path.join(process.cwd(), ".atabey");
|
|
70
|
+
this.localMetricsPath = path.join(frameworkDir, "observability", "metrics.json");
|
|
71
|
+
// Load existing state
|
|
72
|
+
this.loadState();
|
|
73
|
+
}
|
|
74
|
+
static getInstance() {
|
|
75
|
+
if (!BudgetManager.instance) {
|
|
76
|
+
BudgetManager.instance = new BudgetManager();
|
|
77
|
+
}
|
|
78
|
+
return BudgetManager.instance;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Start the budget manager.
|
|
82
|
+
* Begins periodic sync with enterprise server.
|
|
83
|
+
*/
|
|
84
|
+
start() {
|
|
85
|
+
if (!CONFIG.ENABLED) {
|
|
86
|
+
process.stderr.write("[FINOPS] Budget enforcement disabled.\n");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
process.stderr.write(`[FINOPS] Starting budget manager for team "${CONFIG.TEAM}"\n`);
|
|
90
|
+
if (CONFIG.MONTHLY_BUDGET > 0) {
|
|
91
|
+
process.stderr.write(`[FINOPS] Monthly budget: $${CONFIG.MONTHLY_BUDGET.toFixed(2)}\n`);
|
|
92
|
+
}
|
|
93
|
+
// Periodic sync with enterprise server
|
|
94
|
+
if (CONFIG.SYNC_URL) {
|
|
95
|
+
this.syncTimer = setInterval(() => this.syncWithServer(), CONFIG.SYNC_INTERVAL_MS);
|
|
96
|
+
this.syncWithServer(); // Initial sync
|
|
97
|
+
}
|
|
98
|
+
// Recalculate from local metrics
|
|
99
|
+
this.recalculateFromLocalMetrics();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Stop the budget manager.
|
|
103
|
+
*/
|
|
104
|
+
stop() {
|
|
105
|
+
if (this.syncTimer) {
|
|
106
|
+
clearInterval(this.syncTimer);
|
|
107
|
+
this.syncTimer = null;
|
|
108
|
+
}
|
|
109
|
+
this.saveState();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Record token usage and check budget.
|
|
113
|
+
* Returns error message if budget exceeded, null if allowed.
|
|
114
|
+
*/
|
|
115
|
+
recordUsage(agent, tokens) {
|
|
116
|
+
if (!CONFIG.ENABLED)
|
|
117
|
+
return null;
|
|
118
|
+
const cost = (tokens / 1000) * CONFIG.COST_PER_1K_TOKENS;
|
|
119
|
+
// Check if team is blocked
|
|
120
|
+
if (this.state.blocked) {
|
|
121
|
+
return `[FINOPS] ⛔ Budget exceeded: ${this.state.blockReason}`;
|
|
122
|
+
}
|
|
123
|
+
// Update spend
|
|
124
|
+
this.state.monthlySpend += cost;
|
|
125
|
+
this.state.agentSpend[agent] = (this.state.agentSpend[agent] || 0) + cost;
|
|
126
|
+
// Check team budget
|
|
127
|
+
if (CONFIG.MONTHLY_BUDGET > 0 && this.state.monthlySpend > CONFIG.MONTHLY_BUDGET) {
|
|
128
|
+
this.state.blocked = true;
|
|
129
|
+
this.state.blockReason = `Team "${CONFIG.TEAM}" exceeded monthly budget of $${CONFIG.MONTHLY_BUDGET.toFixed(2)}`;
|
|
130
|
+
this.saveState();
|
|
131
|
+
this.triggerAlert(100);
|
|
132
|
+
return `[FINOPS] ⛔ ${this.state.blockReason}`;
|
|
133
|
+
}
|
|
134
|
+
// Check agent budget
|
|
135
|
+
if (CONFIG.AGENT_MAX_BUDGET > 0 && (this.state.agentSpend[agent] || 0) > CONFIG.AGENT_MAX_BUDGET) {
|
|
136
|
+
this.state.blocked = true;
|
|
137
|
+
this.state.blockReason = `Agent "${agent}" exceeded max budget of $${CONFIG.AGENT_MAX_BUDGET.toFixed(2)}`;
|
|
138
|
+
this.saveState();
|
|
139
|
+
this.triggerAlert(100);
|
|
140
|
+
return `[FINOPS] ⛔ ${this.state.blockReason}`;
|
|
141
|
+
}
|
|
142
|
+
// Check alert thresholds
|
|
143
|
+
if (CONFIG.MONTHLY_BUDGET > 0) {
|
|
144
|
+
const usagePercent = (this.state.monthlySpend / CONFIG.MONTHLY_BUDGET) * 100;
|
|
145
|
+
for (const threshold of CONFIG.ALERT_THRESHOLDS) {
|
|
146
|
+
if (usagePercent >= threshold && !this.state.triggeredAlerts.includes(threshold)) {
|
|
147
|
+
this.triggerAlert(threshold);
|
|
148
|
+
process.stderr.write(`[FINOPS] ⚠️ Budget alert: ${Math.round(usagePercent)}% used ($${this.state.monthlySpend.toFixed(2)} / $${CONFIG.MONTHLY_BUDGET.toFixed(2)})\n`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
this.saveState();
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Check if an agent is within budget without recording usage.
|
|
157
|
+
*/
|
|
158
|
+
checkBudget(agent) {
|
|
159
|
+
const agentSpend = this.state.agentSpend[agent] || 0;
|
|
160
|
+
const usagePercent = CONFIG.MONTHLY_BUDGET > 0
|
|
161
|
+
? (this.state.monthlySpend / CONFIG.MONTHLY_BUDGET) * 100
|
|
162
|
+
: 0;
|
|
163
|
+
return {
|
|
164
|
+
allowed: !this.state.blocked,
|
|
165
|
+
monthlySpend: this.state.monthlySpend,
|
|
166
|
+
monthlyBudget: CONFIG.MONTHLY_BUDGET,
|
|
167
|
+
agentSpend,
|
|
168
|
+
agentMaxBudget: CONFIG.AGENT_MAX_BUDGET,
|
|
169
|
+
usagePercent: Math.round(usagePercent * 100) / 100,
|
|
170
|
+
blocked: this.state.blocked,
|
|
171
|
+
blockReason: this.state.blockReason,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get full budget state.
|
|
176
|
+
*/
|
|
177
|
+
getState() {
|
|
178
|
+
return { ...this.state };
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Reset budget for new period.
|
|
182
|
+
*/
|
|
183
|
+
resetPeriod() {
|
|
184
|
+
const now = new Date();
|
|
185
|
+
this.state.monthlySpend = 0;
|
|
186
|
+
this.state.agentSpend = {};
|
|
187
|
+
this.state.periodStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
|
|
188
|
+
this.state.periodEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString();
|
|
189
|
+
this.state.blocked = false;
|
|
190
|
+
this.state.blockReason = null;
|
|
191
|
+
this.state.triggeredAlerts = [];
|
|
192
|
+
this.saveState();
|
|
193
|
+
process.stderr.write("[FINOPS] Budget period reset.\n");
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Apply a budget policy from the enterprise server.
|
|
197
|
+
*/
|
|
198
|
+
applyPolicy(policy) {
|
|
199
|
+
if (policy.team !== CONFIG.TEAM)
|
|
200
|
+
return;
|
|
201
|
+
if (policy.blocked) {
|
|
202
|
+
this.state.blocked = true;
|
|
203
|
+
this.state.blockReason = policy.blockReason || "Blocked by enterprise policy";
|
|
204
|
+
process.stderr.write(`[FINOPS] 🛑 Team "${CONFIG.TEAM}" blocked by enterprise policy: ${this.state.blockReason}\n`);
|
|
205
|
+
}
|
|
206
|
+
this.saveState();
|
|
207
|
+
}
|
|
208
|
+
// ─── Private Methods ───────────────────────────────────────
|
|
209
|
+
/**
|
|
210
|
+
* Sync budget state with enterprise server.
|
|
211
|
+
*/
|
|
212
|
+
async syncWithServer() {
|
|
213
|
+
if (!CONFIG.SYNC_URL)
|
|
214
|
+
return;
|
|
215
|
+
try {
|
|
216
|
+
// Send local state to server
|
|
217
|
+
const url = `${CONFIG.SYNC_URL.replace(/\/$/, "")}/api/finops/sync`;
|
|
218
|
+
const response = await fetch(url, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
headers: {
|
|
221
|
+
"Content-Type": "application/json",
|
|
222
|
+
"Authorization": `Bearer ${CONFIG.SYNC_TOKEN}`,
|
|
223
|
+
"X-Team": CONFIG.TEAM,
|
|
224
|
+
},
|
|
225
|
+
body: JSON.stringify({
|
|
226
|
+
team: this.state.team,
|
|
227
|
+
monthlySpend: this.state.monthlySpend,
|
|
228
|
+
agentSpend: this.state.agentSpend,
|
|
229
|
+
periodStart: this.state.periodStart,
|
|
230
|
+
periodEnd: this.state.periodEnd,
|
|
231
|
+
}),
|
|
232
|
+
});
|
|
233
|
+
if (response.ok) {
|
|
234
|
+
const policy = (await response.json());
|
|
235
|
+
this.applyPolicy(policy);
|
|
236
|
+
this.state.lastSync = new Date().toISOString();
|
|
237
|
+
}
|
|
238
|
+
else if (response.status === 402) {
|
|
239
|
+
// Payment required – budget exceeded on server side
|
|
240
|
+
const data = (await response.json());
|
|
241
|
+
this.state.blocked = true;
|
|
242
|
+
this.state.blockReason = data.reason || "Budget exceeded on enterprise server";
|
|
243
|
+
process.stderr.write(`[FINOPS] 🛑 Enterprise budget exceeded: ${this.state.blockReason}\n`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
// Server unreachable – continue with local budget
|
|
248
|
+
process.stderr.write(`[FINOPS] Sync failed (server unreachable): ${error}\n`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Recalculate spend from local metrics file.
|
|
253
|
+
*/
|
|
254
|
+
recalculateFromLocalMetrics() {
|
|
255
|
+
try {
|
|
256
|
+
if (!fs.existsSync(this.localMetricsPath))
|
|
257
|
+
return;
|
|
258
|
+
const metrics = JSON.parse(fs.readFileSync(this.localMetricsPath, "utf8"));
|
|
259
|
+
const periodStart = new Date(this.state.periodStart);
|
|
260
|
+
const periodEnd = new Date(this.state.periodEnd);
|
|
261
|
+
let totalSpend = 0;
|
|
262
|
+
const agentSpend = {};
|
|
263
|
+
for (const entry of metrics) {
|
|
264
|
+
const entryDate = new Date(entry.timestamp);
|
|
265
|
+
if (entryDate >= periodStart && entryDate <= periodEnd) {
|
|
266
|
+
const cost = (entry.estimatedTokens / 1000) * CONFIG.COST_PER_1K_TOKENS;
|
|
267
|
+
totalSpend += cost;
|
|
268
|
+
agentSpend[entry.agent] = (agentSpend[entry.agent] || 0) + cost;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this.state.monthlySpend = totalSpend;
|
|
272
|
+
this.state.agentSpend = agentSpend;
|
|
273
|
+
}
|
|
274
|
+
catch { /* ignore */ }
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Trigger a budget alert.
|
|
278
|
+
*/
|
|
279
|
+
triggerAlert(threshold) {
|
|
280
|
+
if (!this.state.triggeredAlerts.includes(threshold)) {
|
|
281
|
+
this.state.triggeredAlerts.push(threshold);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Save budget state to disk.
|
|
286
|
+
*/
|
|
287
|
+
saveState() {
|
|
288
|
+
try {
|
|
289
|
+
const frameworkDir = process.env.ATABEY_FRAMEWORK_DIR || path.join(process.cwd(), ".atabey");
|
|
290
|
+
const finopsDir = path.join(frameworkDir, "finops");
|
|
291
|
+
if (!fs.existsSync(finopsDir)) {
|
|
292
|
+
fs.mkdirSync(finopsDir, { recursive: true });
|
|
293
|
+
}
|
|
294
|
+
const statePath = path.join(finopsDir, "budget-state.json");
|
|
295
|
+
fs.writeFileSync(statePath, JSON.stringify(this.state, null, 2));
|
|
296
|
+
}
|
|
297
|
+
catch { /* ignore */ }
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Reset the singleton instance (for testing).
|
|
301
|
+
*/
|
|
302
|
+
static resetInstance() {
|
|
303
|
+
BudgetManager.instance = undefined;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Load budget state from disk.
|
|
307
|
+
*/
|
|
308
|
+
loadState() {
|
|
309
|
+
try {
|
|
310
|
+
const frameworkDir = process.env.ATABEY_FRAMEWORK_DIR || path.join(process.cwd(), ".atabey");
|
|
311
|
+
const statePath = path.join(frameworkDir, "finops", "budget-state.json");
|
|
312
|
+
if (fs.existsSync(statePath)) {
|
|
313
|
+
const saved = JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
314
|
+
// Only load if same period
|
|
315
|
+
if (saved.periodStart === this.state.periodStart) {
|
|
316
|
+
this.state = saved;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch { /* ignore */ }
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ─── Singleton Export ─────────────────────────────────────────────
|
|
324
|
+
export const budgetManager = BudgetManager.getInstance();
|
|
325
|
+
export { CONFIG as FinOpsConfig };
|
|
326
|
+
//# sourceMappingURL=finops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finops.js","sourceRoot":"","sources":["../../../../framework-mcp/src/utils/finops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,qEAAqE;AAErE,MAAM,MAAM,GAAG;IACX,oCAAoC;IACpC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS;IACjD,4CAA4C;IAC5C,cAAc,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,GAAG,CAAC;IACpE,iDAAiD;IACjD,gBAAgB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,CAAC;IACxE,mDAAmD;IACnD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE;IAClD,iBAAiB;IACjB,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE;IACtD,4CAA4C;IAC5C,gBAAgB,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,cAAc,CAAC;SAC3E,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvE,4DAA4D;IAC5D,kBAAkB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,OAAO,CAAC;IAChF,0BAA0B;IAC1B,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,OAAO,EAAE,EAAE,CAAC;IAClF,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,MAAM;CACxD,CAAC;AAiCF,qEAAqE;AAErE,MAAM,OAAO,aAAa;IACd,MAAM,CAAC,QAAQ,CAAgB;IAC/B,KAAK,CAAc;IACnB,SAAS,GAA0C,IAAI,CAAC;IACxD,gBAAgB,CAAS;IAEjC;QACI,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACjF,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAEnF,IAAI,CAAC,KAAK,GAAG;YACT,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,YAAY,EAAE,CAAC;YACf,UAAU,EAAE,EAAE;YACd,WAAW;YACX,SAAS;YACT,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,EAAE;YACnB,QAAQ,EAAE,IAAI;SACjB,CAAC;QAEF,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;QAC7F,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC;QAEjF,sBAAsB;QACtB,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IAEM,MAAM,CAAC,WAAW;QACrB,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YAC1B,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACjD,CAAC;QACD,OAAO,aAAa,CAAC,QAAQ,CAAC;IAClC,CAAC;IAED;;;OAGG;IACI,KAAK;QACR,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAChE,OAAO;QACX,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;QACrF,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5F,CAAC;QAED,uCAAuC;QACvC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACnF,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,eAAe;QAC1C,CAAC;QAED,iCAAiC;QACjC,IAAI,CAAC,2BAA2B,EAAE,CAAC;IACvC,CAAC;IAED;;OAEG;IACI,IAAI;QACP,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QAC1B,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IAED;;;OAGG;IACI,WAAW,CAAC,KAAa,EAAE,MAAc;QAC5C,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAEjC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC;QAEzD,2BAA2B;QAC3B,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,+BAA+B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACnE,CAAC;QAED,eAAe;QACf,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAE1E,oBAAoB;QACpB,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC;YAC/E,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,SAAS,MAAM,CAAC,IAAI,iCAAiC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACjH,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,cAAc,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAClD,CAAC;QAED,qBAAqB;QACrB,IAAI,MAAM,CAAC,gBAAgB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC/F,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,UAAU,KAAK,6BAA6B,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1G,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,cAAc,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAClD,CAAC;QAED,yBAAyB;QACzB,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC;YAC7E,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;gBAC9C,IAAI,YAAY,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC/E,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;oBAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1K,CAAC;YACL,CAAC;QACL,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACI,WAAW,CAAC,KAAa;QAU5B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,GAAG,CAAC;YAC1C,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,GAAG,GAAG;YACzD,CAAC,CAAC,CAAC,CAAC;QAER,OAAO;YACH,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO;YAC5B,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;YACrC,aAAa,EAAE,MAAM,CAAC,cAAc;YACpC,UAAU;YACV,cAAc,EAAE,MAAM,CAAC,gBAAgB;YACvC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;YAClD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO;YAC3B,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW;SACtC,CAAC;IACN,CAAC;IAED;;OAEG;IACI,QAAQ;QACX,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED;;OAEG;IACI,WAAW;QACd,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtF,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACxF,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAC5D,CAAC;IAED;;OAEG;IACI,WAAW,CAAC,MAAoB;QACnC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;YAAE,OAAO;QAExC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,8BAA8B,CAAC;YAC9E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,MAAM,CAAC,IAAI,mCAAmC,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;QACxH,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IAED,8DAA8D;IAE9D;;OAEG;IACK,KAAK,CAAC,cAAc;QACxB,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,OAAO;QAE7B,IAAI,CAAC;YACD,6BAA6B;YAC7B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC;YACpE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC9B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACL,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,UAAU,MAAM,CAAC,UAAU,EAAE;oBAC9C,QAAQ,EAAE,MAAM,CAAC,IAAI;iBACxB;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACjB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;oBACrB,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;oBACrC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU;oBACjC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW;oBACnC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS;iBAClC,CAAC;aACL,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACd,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAiB,CAAC;gBACvD,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACnD,CAAC;iBAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACjC,oDAAoD;gBACpD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;gBAC5D,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,IAAI,sCAAsC,CAAC;gBAC/E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;YAChG,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,kDAAkD;YAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,KAAK,IAAI,CAAC,CAAC;QAClF,CAAC;IACL,CAAC;IAED;;OAEG;IACK,2BAA2B;QAC/B,IAAI,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC;gBAAE,OAAO;YAElD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAIvE,CAAC;YAEH,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACrD,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAEjD,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,MAAM,UAAU,GAA2B,EAAE,CAAC;YAE9C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC1B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBAC5C,IAAI,SAAS,IAAI,WAAW,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;oBACrD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC;oBACxE,UAAU,IAAI,IAAI,CAAC;oBACnB,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;gBACpE,CAAC;YACL,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,UAAU,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,SAAiB;QAClC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IAED;;OAEG;IACK,SAAS;QACb,IAAI,CAAC;YACD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;YAC7F,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;YACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC5B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACjD,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;YAC5D,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,aAAa;QACvB,aAAa,CAAC,QAAQ,GAAG,SAAqC,CAAC;IACnE,CAAC;IAED;;OAEG;IACK,SAAS;QACb,IAAI,CAAC;YACD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;YAC7F,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC;YACzE,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAgB,CAAC;gBAC5E,2BAA2B;gBAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;oBAC/C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;gBACvB,CAAC;YACL,CAAC;QACL,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;CACJ;AAED,qEAAqE;AAErE,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC;AACzD,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ─── HUMAN-IN-THE-LOOP (In-CLI Approval) ──────────────────────────
|
|
3
|
+
*
|
|
4
|
+
* Allows risk-gated approvals without the developer leaving their AI CLI chat.
|
|
5
|
+
*
|
|
6
|
+
* Key Innovation: Instead of forcing developers to switch to a web dashboard
|
|
7
|
+
* or another terminal, approval requests are sent as MCP notifications into
|
|
8
|
+
* the developer's existing chat interface.
|
|
9
|
+
*
|
|
10
|
+
* Flow:
|
|
11
|
+
* 1. Tool call triggers risk detection
|
|
12
|
+
* 2. If risk >= threshold, tool is blocked with a structured error
|
|
13
|
+
* 3. Error message includes the traceId and approval command
|
|
14
|
+
* 4. Developer types "atabey approve <traceId>" in the same chat
|
|
15
|
+
* 5. Hermes message status updates → retry succeeds
|
|
16
|
+
*
|
|
17
|
+
* Risk Levels:
|
|
18
|
+
* - LOW (0-30): Silent pass (no approval needed)
|
|
19
|
+
* - MEDIUM (31-59): Stealth warning to stderr only
|
|
20
|
+
* - HIGH (60-100): Blocked until approval
|
|
21
|
+
*/
|
|
22
|
+
declare const CONFIG: {
|
|
23
|
+
/** Risk threshold for blocking (0-100) */
|
|
24
|
+
HIGH_RISK_THRESHOLD: number;
|
|
25
|
+
/** Risk threshold for medium warning */
|
|
26
|
+
MEDIUM_RISK_THRESHOLD: number;
|
|
27
|
+
/** Auto-approve low risk operations */
|
|
28
|
+
AUTO_APPROVE_LOW_RISK: boolean;
|
|
29
|
+
/** Approval timeout in seconds */
|
|
30
|
+
APPROVAL_TIMEOUT_SECONDS: number;
|
|
31
|
+
};
|
|
32
|
+
export type RiskLevel = "LOW" | "MEDIUM" | "HIGH";
|
|
33
|
+
export interface ApprovalRequest {
|
|
34
|
+
traceId: string;
|
|
35
|
+
toolName: string;
|
|
36
|
+
agent: string;
|
|
37
|
+
riskScore: number;
|
|
38
|
+
riskLevel: RiskLevel;
|
|
39
|
+
reason: string;
|
|
40
|
+
args: Record<string, unknown>;
|
|
41
|
+
createdAt: number;
|
|
42
|
+
expiresAt: number;
|
|
43
|
+
status: "PENDING" | "APPROVED" | "REJECTED" | "EXPIRED";
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Determine risk level from risk score.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getRiskLevel(score: number): RiskLevel;
|
|
49
|
+
/**
|
|
50
|
+
* Create an approval request for a high-risk operation.
|
|
51
|
+
* Returns the approval request and a message for the AI/developer.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createApprovalRequest(traceId: string, toolName: string, agent: string, riskScore: number, reason: string, args: Record<string, unknown>): {
|
|
54
|
+
request: ApprovalRequest;
|
|
55
|
+
message: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Check if an operation should be blocked based on risk score.
|
|
59
|
+
* Returns:
|
|
60
|
+
* - null if allowed (LOW risk or auto-approved)
|
|
61
|
+
* - { blocked: true, message } if blocked (HIGH risk)
|
|
62
|
+
* - { blocked: false, warning } if warning only (MEDIUM risk)
|
|
63
|
+
*/
|
|
64
|
+
export declare function checkRiskGate(traceId: string, toolName: string, agent: string, riskScore: number, reason: string, args: Record<string, unknown>): {
|
|
65
|
+
blocked: boolean;
|
|
66
|
+
message: string | null;
|
|
67
|
+
warning?: string;
|
|
68
|
+
} | null;
|
|
69
|
+
/**
|
|
70
|
+
* Check if an approval request exists and is still valid.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getApprovalStatus(traceId: string): ApprovalRequest | null;
|
|
73
|
+
/**
|
|
74
|
+
* Approve a pending operation.
|
|
75
|
+
*/
|
|
76
|
+
export declare function approveOperation(traceId: string): {
|
|
77
|
+
success: boolean;
|
|
78
|
+
message: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Reject a pending operation.
|
|
82
|
+
*/
|
|
83
|
+
export declare function rejectOperation(traceId: string): {
|
|
84
|
+
success: boolean;
|
|
85
|
+
message: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Get all pending approval requests.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getPendingApprovals(): ApprovalRequest[];
|
|
91
|
+
/**
|
|
92
|
+
* Get HITL config.
|
|
93
|
+
*/
|
|
94
|
+
export declare function getHumanInLoopConfig(): typeof CONFIG;
|
|
95
|
+
export { CONFIG as HumanInLoopConfig };
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ─── HUMAN-IN-THE-LOOP (In-CLI Approval) ──────────────────────────
|
|
3
|
+
*
|
|
4
|
+
* Allows risk-gated approvals without the developer leaving their AI CLI chat.
|
|
5
|
+
*
|
|
6
|
+
* Key Innovation: Instead of forcing developers to switch to a web dashboard
|
|
7
|
+
* or another terminal, approval requests are sent as MCP notifications into
|
|
8
|
+
* the developer's existing chat interface.
|
|
9
|
+
*
|
|
10
|
+
* Flow:
|
|
11
|
+
* 1. Tool call triggers risk detection
|
|
12
|
+
* 2. If risk >= threshold, tool is blocked with a structured error
|
|
13
|
+
* 3. Error message includes the traceId and approval command
|
|
14
|
+
* 4. Developer types "atabey approve <traceId>" in the same chat
|
|
15
|
+
* 5. Hermes message status updates → retry succeeds
|
|
16
|
+
*
|
|
17
|
+
* Risk Levels:
|
|
18
|
+
* - LOW (0-30): Silent pass (no approval needed)
|
|
19
|
+
* - MEDIUM (31-59): Stealth warning to stderr only
|
|
20
|
+
* - HIGH (60-100): Blocked until approval
|
|
21
|
+
*/
|
|
22
|
+
import { AtabeyStorage } from "../../../src/shared/storage.js";
|
|
23
|
+
import { asAgentID, asTraceID } from "../../../src/shared/types.js";
|
|
24
|
+
// ─── Configuration ────────────────────────────────────────────────
|
|
25
|
+
const CONFIG = {
|
|
26
|
+
/** Risk threshold for blocking (0-100) */
|
|
27
|
+
HIGH_RISK_THRESHOLD: parseInt(process.env.MCP_HIGH_RISK_THRESHOLD || "60", 10),
|
|
28
|
+
/** Risk threshold for medium warning */
|
|
29
|
+
MEDIUM_RISK_THRESHOLD: parseInt(process.env.MCP_MEDIUM_RISK_THRESHOLD || "30", 10),
|
|
30
|
+
/** Auto-approve low risk operations */
|
|
31
|
+
AUTO_APPROVE_LOW_RISK: process.env.MCP_AUTO_APPROVE_LOW_RISK !== "false",
|
|
32
|
+
/** Approval timeout in seconds */
|
|
33
|
+
APPROVAL_TIMEOUT_SECONDS: parseInt(process.env.MCP_APPROVAL_TIMEOUT || "300", 10), // 5 min
|
|
34
|
+
};
|
|
35
|
+
// ─── Active Approval Requests ─────────────────────────────────────
|
|
36
|
+
const activeApprovals = new Map();
|
|
37
|
+
/**
|
|
38
|
+
* Determine risk level from risk score.
|
|
39
|
+
*/
|
|
40
|
+
export function getRiskLevel(score) {
|
|
41
|
+
if (score >= CONFIG.HIGH_RISK_THRESHOLD)
|
|
42
|
+
return "HIGH";
|
|
43
|
+
if (score >= CONFIG.MEDIUM_RISK_THRESHOLD)
|
|
44
|
+
return "MEDIUM";
|
|
45
|
+
return "LOW";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create an approval request for a high-risk operation.
|
|
49
|
+
* Returns the approval request and a message for the AI/developer.
|
|
50
|
+
*/
|
|
51
|
+
export function createApprovalRequest(traceId, toolName, agent, riskScore, reason, args) {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const request = {
|
|
54
|
+
traceId,
|
|
55
|
+
toolName,
|
|
56
|
+
agent,
|
|
57
|
+
riskScore,
|
|
58
|
+
riskLevel: getRiskLevel(riskScore),
|
|
59
|
+
reason,
|
|
60
|
+
args,
|
|
61
|
+
createdAt: now,
|
|
62
|
+
expiresAt: now + CONFIG.APPROVAL_TIMEOUT_SECONDS * 1000,
|
|
63
|
+
status: "PENDING",
|
|
64
|
+
};
|
|
65
|
+
activeApprovals.set(traceId, request);
|
|
66
|
+
// Create a Hermes approval message
|
|
67
|
+
AtabeyStorage.saveMessage({
|
|
68
|
+
from: asAgentID(agent),
|
|
69
|
+
to: asAgentID("@manager"),
|
|
70
|
+
category: "ALERT",
|
|
71
|
+
content: JSON.stringify({
|
|
72
|
+
type: "APPROVAL_REQUIRED",
|
|
73
|
+
traceId,
|
|
74
|
+
tool: toolName,
|
|
75
|
+
riskScore,
|
|
76
|
+
reason,
|
|
77
|
+
}),
|
|
78
|
+
traceId: asTraceID(traceId),
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
status: "PENDING",
|
|
81
|
+
priority: "HIGH",
|
|
82
|
+
requiresApproval: true,
|
|
83
|
+
});
|
|
84
|
+
// Build the in-chat approval message
|
|
85
|
+
// This is what the developer sees in their AI CLI chat
|
|
86
|
+
const message = [
|
|
87
|
+
`[RISK GATE] Operation blocked: ${reason}`,
|
|
88
|
+
"",
|
|
89
|
+
` Tool: ${toolName}`,
|
|
90
|
+
` Agent: ${agent}`,
|
|
91
|
+
` Risk Score: ${riskScore}/100 (${request.riskLevel})`,
|
|
92
|
+
` Trace ID: ${traceId}`,
|
|
93
|
+
"",
|
|
94
|
+
"─── IN-CHAT APPROVAL ────────────────────────────────────────",
|
|
95
|
+
"Ask your AI assistant to call the approve_operation tool:",
|
|
96
|
+
"",
|
|
97
|
+
` To approve: approve_operation { "action": "approve", "traceId": "${traceId}" }`,
|
|
98
|
+
` To reject: approve_operation { "action": "reject", "traceId": "${traceId}" }`,
|
|
99
|
+
" To list: approve_operation { \"action\": \"list\" }",
|
|
100
|
+
"─────────────────────────────────────────────────────────────",
|
|
101
|
+
"",
|
|
102
|
+
`Alternative: atabey hitl answer "${traceId}:approve" in your terminal.`,
|
|
103
|
+
].join("\n");
|
|
104
|
+
return { request, message };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if an operation should be blocked based on risk score.
|
|
108
|
+
* Returns:
|
|
109
|
+
* - null if allowed (LOW risk or auto-approved)
|
|
110
|
+
* - { blocked: true, message } if blocked (HIGH risk)
|
|
111
|
+
* - { blocked: false, warning } if warning only (MEDIUM risk)
|
|
112
|
+
*/
|
|
113
|
+
export function checkRiskGate(traceId, toolName, agent, riskScore, reason, args) {
|
|
114
|
+
const level = getRiskLevel(riskScore);
|
|
115
|
+
// LOW risk: always pass
|
|
116
|
+
if (level === "LOW") {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
// MEDIUM risk: warning only
|
|
120
|
+
if (level === "MEDIUM") {
|
|
121
|
+
return {
|
|
122
|
+
blocked: false,
|
|
123
|
+
message: null,
|
|
124
|
+
warning: `[RISK WARNING] ${reason} (risk: ${riskScore}/100). Monitor this operation.`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// HIGH risk: block and create approval request
|
|
128
|
+
const { message } = createApprovalRequest(traceId, toolName, agent, riskScore, reason, args);
|
|
129
|
+
return {
|
|
130
|
+
blocked: true,
|
|
131
|
+
message,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if an approval request exists and is still valid.
|
|
136
|
+
*/
|
|
137
|
+
export function getApprovalStatus(traceId) {
|
|
138
|
+
const request = activeApprovals.get(traceId);
|
|
139
|
+
if (!request)
|
|
140
|
+
return null;
|
|
141
|
+
// Check if expired
|
|
142
|
+
if (Date.now() > request.expiresAt) {
|
|
143
|
+
request.status = "EXPIRED";
|
|
144
|
+
return request;
|
|
145
|
+
}
|
|
146
|
+
return request;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Approve a pending operation.
|
|
150
|
+
*/
|
|
151
|
+
export function approveOperation(traceId) {
|
|
152
|
+
const request = activeApprovals.get(traceId);
|
|
153
|
+
if (!request) {
|
|
154
|
+
return { success: false, message: `No pending approval found for trace: ${traceId}` };
|
|
155
|
+
}
|
|
156
|
+
if (request.status !== "PENDING") {
|
|
157
|
+
return { success: false, message: `Approval for ${traceId} is already ${request.status}` };
|
|
158
|
+
}
|
|
159
|
+
request.status = "APPROVED";
|
|
160
|
+
// Update Hermes message status
|
|
161
|
+
const messages = AtabeyStorage.getPendingMessages();
|
|
162
|
+
const pendingMsgs = messages.filter(m => m.traceId === traceId && m.category === "ALERT" && m.status === "PENDING");
|
|
163
|
+
for (const msg of pendingMsgs) {
|
|
164
|
+
if (msg.id !== undefined) {
|
|
165
|
+
AtabeyStorage.updateMessageStatus(msg.id, "APPROVED");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { success: true, message: `Operation ${traceId} approved. You may retry.` };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Reject a pending operation.
|
|
172
|
+
*/
|
|
173
|
+
export function rejectOperation(traceId) {
|
|
174
|
+
const request = activeApprovals.get(traceId);
|
|
175
|
+
if (!request) {
|
|
176
|
+
return { success: false, message: `No pending approval found for trace: ${traceId}` };
|
|
177
|
+
}
|
|
178
|
+
if (request.status !== "PENDING") {
|
|
179
|
+
return { success: false, message: `Approval for ${traceId} is already ${request.status}` };
|
|
180
|
+
}
|
|
181
|
+
request.status = "REJECTED";
|
|
182
|
+
// Update Hermes message status
|
|
183
|
+
const messages = AtabeyStorage.getPendingMessages();
|
|
184
|
+
const pendingMsgs = messages.filter(m => m.traceId === traceId && m.category === "ALERT" && m.status === "PENDING");
|
|
185
|
+
for (const msg of pendingMsgs) {
|
|
186
|
+
if (msg.id !== undefined) {
|
|
187
|
+
AtabeyStorage.updateMessageStatus(msg.id, "REJECTED");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { success: true, message: `Operation ${traceId} rejected.` };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get all pending approval requests.
|
|
194
|
+
*/
|
|
195
|
+
export function getPendingApprovals() {
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
const pending = [];
|
|
198
|
+
// Clean expired
|
|
199
|
+
for (const [, request] of activeApprovals) {
|
|
200
|
+
if (request.status === "PENDING" && now > request.expiresAt) {
|
|
201
|
+
request.status = "EXPIRED";
|
|
202
|
+
}
|
|
203
|
+
if (request.status === "PENDING") {
|
|
204
|
+
pending.push(request);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return pending;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get HITL config.
|
|
211
|
+
*/
|
|
212
|
+
export function getHumanInLoopConfig() {
|
|
213
|
+
return { ...CONFIG };
|
|
214
|
+
}
|
|
215
|
+
export { CONFIG as HumanInLoopConfig };
|
|
216
|
+
//# sourceMappingURL=human-in-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-in-loop.js","sourceRoot":"","sources":["../../../../framework-mcp/src/utils/human-in-loop.ts"],"names":[],"mappings":"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"}
|