atabey 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +672 -21
- package/PRIVACY.md +8 -14
- package/README.md +183 -25
- package/bin/cli.js +72 -5
- package/dist/framework-mcp/src/index.js +145 -13
- package/dist/framework-mcp/src/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/control_plane/registry.d.ts +1 -1
- package/dist/framework-mcp/src/tools/control_plane/registry.js +52 -4
- package/dist/framework-mcp/src/tools/control_plane/registry.js.map +1 -1
- package/dist/framework-mcp/src/tools/definitions.js +1 -1
- package/dist/framework-mcp/src/tools/file_system/read_file.js +5 -0
- package/dist/framework-mcp/src/tools/file_system/read_file.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/approve_operation.d.ts +10 -8
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js +12 -12
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/ask_human.d.ts +8 -8
- package/dist/framework-mcp/src/tools/messaging/ask_human.js +8 -8
- package/dist/framework-mcp/src/tools/messaging/send_message.js +19 -3
- package/dist/framework-mcp/src/tools/messaging/send_message.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 +21 -7
- package/dist/framework-mcp/src/tools/observability/check_ports.js.map +1 -1
- package/dist/framework-mcp/src/tools/schemas.d.ts +4 -4
- package/dist/framework-mcp/src/tools/search/grep_search.js +11 -4
- package/dist/framework-mcp/src/tools/search/grep_search.js.map +1 -1
- package/dist/framework-mcp/src/tools/search/list_dir.js +3 -0
- package/dist/framework-mcp/src/tools/search/list_dir.js.map +1 -1
- package/dist/framework-mcp/src/tools/shell/run_command.js +185 -93
- package/dist/framework-mcp/src/tools/shell/run_command.js.map +1 -1
- package/dist/framework-mcp/src/utils/auth.d.ts +26 -2
- package/dist/framework-mcp/src/utils/auth.js +74 -7
- package/dist/framework-mcp/src/utils/auth.js.map +1 -1
- package/dist/framework-mcp/src/utils/compliance.js +45 -13
- package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
- package/dist/framework-mcp/src/utils/discipline.d.ts +1 -0
- package/dist/framework-mcp/src/utils/discipline.js +42 -3
- package/dist/framework-mcp/src/utils/discipline.js.map +1 -1
- package/dist/framework-mcp/src/utils/human-in-loop.d.ts +35 -2
- package/dist/framework-mcp/src/utils/human-in-loop.js +114 -10
- package/dist/framework-mcp/src/utils/human-in-loop.js.map +1 -1
- package/dist/framework-mcp/src/utils/loop-detector.js +3 -3
- package/dist/framework-mcp/src/utils/loop-detector.js.map +1 -1
- package/dist/framework-mcp/src/utils/permissions.d.ts +67 -3
- package/dist/framework-mcp/src/utils/permissions.js +179 -43
- package/dist/framework-mcp/src/utils/permissions.js.map +1 -1
- package/dist/framework-mcp/src/utils/prompt-injection.d.ts +20 -0
- package/dist/framework-mcp/src/utils/prompt-injection.js +64 -0
- package/dist/framework-mcp/src/utils/prompt-injection.js.map +1 -0
- package/dist/framework-mcp/src/utils/quality.js +4 -3
- package/dist/framework-mcp/src/utils/quality.js.map +1 -1
- package/dist/framework-mcp/src/utils/silent-router.d.ts +4 -2
- package/dist/framework-mcp/src/utils/silent-router.js +35 -9
- package/dist/framework-mcp/src/utils/silent-router.js.map +1 -1
- package/dist/framework-mcp/tests/tools/file_system/permissions.test.js +174 -71
- package/dist/framework-mcp/tests/tools/file_system/permissions.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +36 -30
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.js +26 -12
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/shell/run_command.test.js +38 -15
- package/dist/framework-mcp/tests/tools/shell/run_command.test.js.map +1 -1
- package/dist/framework-mcp/tests/utils/human-in-loop.test.js +152 -4
- package/dist/framework-mcp/tests/utils/human-in-loop.test.js.map +1 -1
- package/dist/framework-mcp/tests/utils/loop-detector.test.js +0 -2
- package/dist/framework-mcp/tests/utils/loop-detector.test.js.map +1 -1
- package/dist/framework-mcp/tests/utils/prompt-injection.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/prompt-injection.test.js +43 -0
- package/dist/framework-mcp/tests/utils/prompt-injection.test.js.map +1 -0
- package/dist/src/cli/commands/dashboard.js +167 -2
- package/dist/src/cli/commands/dashboard.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-standards.js +1 -1
- package/dist/src/cli/commands/init/scaffold-standards.js.map +1 -1
- package/dist/src/cli/commands/init.js +14 -7
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/log.js +8 -5
- package/dist/src/cli/commands/log.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/orchestrate.d.ts +4 -4
- package/dist/src/cli/commands/orchestrate.js +14 -2
- package/dist/src/cli/commands/orchestrate.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/commands/status.js +21 -1
- package/dist/src/cli/commands/status.js.map +1 -1
- package/dist/src/cli/utils/memory.js +6 -5
- package/dist/src/cli/utils/memory.js.map +1 -1
- package/dist/src/cli/utils/schemas.d.ts +2 -2
- package/dist/src/contracts/tasks.d.ts +4 -4
- package/dist/src/modules/contracts/api.d.ts +6 -6
- package/dist/src/modules/engines/agent-executor.js +41 -20
- package/dist/src/modules/engines/agent-executor.js.map +1 -1
- package/dist/src/modules/engines/agent-loop.d.ts +5 -5
- package/dist/src/modules/engines/agent-loop.js +5 -5
- package/dist/src/modules/engines/evaluation-engine.d.ts +9 -2
- package/dist/src/modules/engines/evaluation-engine.js +77 -9
- package/dist/src/modules/engines/evaluation-engine.js.map +1 -1
- package/dist/src/modules/engines/quality-gate.js +4 -2
- package/dist/src/modules/engines/quality-gate.js.map +1 -1
- package/dist/src/modules/engines/risk-engine.d.ts +17 -1
- package/dist/src/modules/engines/risk-engine.js +98 -3
- package/dist/src/modules/engines/risk-engine.js.map +1 -1
- package/dist/src/modules/engines/routing-engine.d.ts +26 -1
- package/dist/src/modules/engines/routing-engine.js +97 -1
- package/dist/src/modules/engines/routing-engine.js.map +1 -1
- package/dist/src/modules/providers/shared.js +2 -3
- package/dist/src/modules/providers/shared.js.map +1 -1
- package/dist/src/shared/pii.js +1 -1
- package/dist/src/shared/pii.js.map +1 -1
- package/dist/src/shared/storage.d.ts +60 -0
- package/dist/src/shared/storage.js +77 -3
- package/dist/src/shared/storage.js.map +1 -1
- package/dist/tests/cli/commands/dashboard-coverage.test.js +3 -1
- package/dist/tests/cli/commands/dashboard-coverage.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/engines/evaluation-engine.test.js +2 -2
- package/dist/tests/modules/engines/evaluation-engine.test.js.map +1 -1
- package/dist/tests/status-cost.test.js +2 -0
- package/dist/tests/status-cost.test.js.map +1 -1
- package/dist/tests/storage-operations.test.js +31 -4
- package/dist/tests/storage-operations.test.js.map +1 -1
- package/dist/vitest.config.js +6 -4
- package/dist/vitest.config.js.map +1 -1
- package/framework-mcp/README.md +18 -32
- package/framework-mcp/dist/dashboard/assets/index-B-Eu7_J9.js +1260 -0
- package/framework-mcp/dist/dashboard/index.html +1 -1
- package/framework-mcp/dist/framework-mcp/src/index.js +145 -13
- package/framework-mcp/dist/framework-mcp/src/tools/control_plane/registry.js +52 -4
- package/framework-mcp/dist/framework-mcp/src/tools/definitions.js +1 -1
- package/framework-mcp/dist/framework-mcp/src/tools/file_system/read_file.js +5 -0
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/approve_operation.js +12 -12
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/ask_human.js +8 -8
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/send_message.js +19 -3
- package/framework-mcp/dist/framework-mcp/src/tools/observability/check_ports.js +21 -7
- package/framework-mcp/dist/framework-mcp/src/tools/search/grep_search.js +11 -4
- package/framework-mcp/dist/framework-mcp/src/tools/search/list_dir.js +3 -0
- package/framework-mcp/dist/framework-mcp/src/tools/shell/run_command.js +185 -93
- package/framework-mcp/dist/framework-mcp/src/utils/auth.js +74 -7
- package/framework-mcp/dist/framework-mcp/src/utils/compliance.js +45 -13
- package/framework-mcp/dist/framework-mcp/src/utils/discipline.js +42 -3
- package/framework-mcp/dist/framework-mcp/src/utils/human-in-loop.js +114 -10
- package/framework-mcp/dist/framework-mcp/src/utils/loop-detector.js +3 -3
- package/framework-mcp/dist/framework-mcp/src/utils/permissions.js +179 -43
- package/framework-mcp/dist/framework-mcp/src/utils/prompt-injection.js +63 -0
- package/framework-mcp/dist/framework-mcp/src/utils/quality.js +4 -3
- package/framework-mcp/dist/framework-mcp/src/utils/silent-router.js +35 -9
- package/framework-mcp/dist/src/cli/utils/memory.js +6 -5
- package/framework-mcp/dist/src/modules/engines/evaluation-engine.js +170 -0
- package/framework-mcp/dist/src/modules/engines/quality-gate.js +4 -2
- package/framework-mcp/dist/src/modules/engines/risk-engine.js +98 -3
- package/framework-mcp/dist/src/modules/engines/routing-engine.js +97 -1
- package/framework-mcp/dist/src/modules/providers/shared.js +2 -3
- package/framework-mcp/dist/src/shared/pii.js +1 -1
- package/framework-mcp/dist/src/shared/storage.js +77 -3
- package/framework-mcp/package.json +21 -18
- package/mcp.json +3 -2
- package/package.json +4 -5
- package/framework-mcp/dist/dashboard/assets/index-B2mYld0c.js +0 -1221
|
@@ -1,102 +1,175 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
2
|
import { Metrics } from "../../utils/metrics.js";
|
|
3
|
+
import { resolveActiveAgent, getAgentTier } from "../../utils/permissions.js";
|
|
4
|
+
import { resolveFrameworkDir } from "../../utils/security.js";
|
|
5
|
+
import path from "path";
|
|
6
|
+
// Each entry defines the executable and its allowed args prefix
|
|
3
7
|
const COMMAND_ALLOW_LIST = [
|
|
4
8
|
// Package managers
|
|
5
|
-
"npm test",
|
|
6
|
-
"npm run",
|
|
7
|
-
"npm install",
|
|
8
|
-
"npm ci",
|
|
9
|
-
"npm audit",
|
|
10
|
-
"npx vitest",
|
|
11
|
-
"npx tsc",
|
|
12
|
-
"npx eslint",
|
|
13
|
-
"yarn test",
|
|
14
|
-
"yarn run",
|
|
15
|
-
"yarn install",
|
|
16
|
-
"pnpm test",
|
|
17
|
-
"pnpm run",
|
|
18
|
-
"pnpm install",
|
|
9
|
+
{ cmd: "npm", args: ["test"] },
|
|
10
|
+
{ cmd: "npm", args: ["run"] },
|
|
11
|
+
{ cmd: "npm", args: ["install"] },
|
|
12
|
+
{ cmd: "npm", args: ["ci"] },
|
|
13
|
+
{ cmd: "npm", args: ["audit"] },
|
|
14
|
+
{ cmd: "npx", args: ["vitest"] },
|
|
15
|
+
{ cmd: "npx", args: ["tsc"] },
|
|
16
|
+
{ cmd: "npx", args: ["eslint"] },
|
|
17
|
+
{ cmd: "yarn", args: ["test"] },
|
|
18
|
+
{ cmd: "yarn", args: ["run"] },
|
|
19
|
+
{ cmd: "yarn", args: ["install"] },
|
|
20
|
+
{ cmd: "pnpm", args: ["test"] },
|
|
21
|
+
{ cmd: "pnpm", args: ["run"] },
|
|
22
|
+
{ cmd: "pnpm", args: ["install"] },
|
|
19
23
|
// Git
|
|
20
|
-
"git status",
|
|
21
|
-
"git diff",
|
|
22
|
-
"git log",
|
|
23
|
-
"git branch",
|
|
24
|
-
"git stash",
|
|
25
|
-
"git add",
|
|
26
|
-
"git commit",
|
|
27
|
-
"git push",
|
|
28
|
-
"git pull",
|
|
29
|
-
"git fetch",
|
|
30
|
-
"git merge",
|
|
31
|
-
"git rebase",
|
|
32
|
-
"git checkout",
|
|
33
|
-
"git reset",
|
|
24
|
+
{ cmd: "git", args: ["status"] },
|
|
25
|
+
{ cmd: "git", args: ["diff"] },
|
|
26
|
+
{ cmd: "git", args: ["log"] },
|
|
27
|
+
{ cmd: "git", args: ["branch"] },
|
|
28
|
+
{ cmd: "git", args: ["stash"] },
|
|
29
|
+
{ cmd: "git", args: ["add"] },
|
|
30
|
+
{ cmd: "git", args: ["commit"] },
|
|
31
|
+
{ cmd: "git", args: ["push"] },
|
|
32
|
+
{ cmd: "git", args: ["pull"] },
|
|
33
|
+
{ cmd: "git", args: ["fetch"] },
|
|
34
|
+
{ cmd: "git", args: ["merge"] },
|
|
35
|
+
{ cmd: "git", args: ["rebase"] },
|
|
36
|
+
{ cmd: "git", args: ["checkout"] },
|
|
37
|
+
{ cmd: "git", args: ["reset"] },
|
|
34
38
|
// Build tools
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"uniq",
|
|
79
|
-
"echo",
|
|
80
|
-
"pwd",
|
|
81
|
-
"which",
|
|
82
|
-
"file",
|
|
83
|
-
"du",
|
|
84
|
-
"df",
|
|
39
|
+
{ cmd: "make" },
|
|
40
|
+
{ cmd: "cmake" },
|
|
41
|
+
{ cmd: "dotnet", args: ["test"] },
|
|
42
|
+
{ cmd: "dotnet", args: ["format"] },
|
|
43
|
+
{ cmd: "dotnet", args: ["build"] },
|
|
44
|
+
{ cmd: "dotnet", args: ["run"] },
|
|
45
|
+
{ cmd: "dotnet", args: ["restore"] },
|
|
46
|
+
{ cmd: "go", args: ["test"] },
|
|
47
|
+
{ cmd: "go", args: ["fmt"] },
|
|
48
|
+
{ cmd: "go", args: ["build"] },
|
|
49
|
+
{ cmd: "go", args: ["run"] },
|
|
50
|
+
{ cmd: "go", args: ["vet"] },
|
|
51
|
+
{ cmd: "go", args: ["mod"] },
|
|
52
|
+
{ cmd: "pytest" },
|
|
53
|
+
{ cmd: "ruff", args: ["check"] },
|
|
54
|
+
{ cmd: "ruff", args: ["format"] },
|
|
55
|
+
{ cmd: "mvn" },
|
|
56
|
+
{ cmd: "./gradlew" },
|
|
57
|
+
{ cmd: "cargo", args: ["test"] },
|
|
58
|
+
{ cmd: "cargo", args: ["build"] },
|
|
59
|
+
{ cmd: "cargo", args: ["check"] },
|
|
60
|
+
{ cmd: "cargo", args: ["fmt"] },
|
|
61
|
+
// File operations (safe) — spawn prevents shell injection
|
|
62
|
+
{ cmd: "mkdir" },
|
|
63
|
+
{ cmd: "cp" },
|
|
64
|
+
{ cmd: "mv" },
|
|
65
|
+
{ cmd: "rmdir" },
|
|
66
|
+
{ cmd: "cat" },
|
|
67
|
+
{ cmd: "ls" },
|
|
68
|
+
{ cmd: "touch" },
|
|
69
|
+
{ cmd: "head" },
|
|
70
|
+
{ cmd: "tail" },
|
|
71
|
+
{ cmd: "wc" },
|
|
72
|
+
{ cmd: "grep" },
|
|
73
|
+
{ cmd: "find" },
|
|
74
|
+
{ cmd: "sort" },
|
|
75
|
+
{ cmd: "uniq" },
|
|
76
|
+
{ cmd: "echo" },
|
|
77
|
+
{ cmd: "pwd" },
|
|
78
|
+
{ cmd: "which" },
|
|
79
|
+
{ cmd: "file" },
|
|
80
|
+
{ cmd: "du" },
|
|
81
|
+
{ cmd: "df" },
|
|
85
82
|
];
|
|
86
|
-
const TIMEOUT =
|
|
83
|
+
const TIMEOUT = parseInt(process.env.MCP_COMMAND_TIMEOUT_MS || "30000", 10);
|
|
84
|
+
/**
|
|
85
|
+
* Checks if a parsed command represents a state-mutating (write) operation.
|
|
86
|
+
*/
|
|
87
|
+
function isWriteCommand(parsed) {
|
|
88
|
+
const cmd = parsed.cmd;
|
|
89
|
+
const firstArg = parsed.args[0] || "";
|
|
90
|
+
const writeCmds = ["mkdir", "cp", "mv", "rmdir", "touch", "make", "cmake", "./gradlew", "mvn"];
|
|
91
|
+
if (writeCmds.includes(cmd))
|
|
92
|
+
return true;
|
|
93
|
+
if (cmd === "npm" && ["install", "ci", "audit", "run"].includes(firstArg))
|
|
94
|
+
return true;
|
|
95
|
+
if (cmd === "yarn" && ["install", "run"].includes(firstArg))
|
|
96
|
+
return true;
|
|
97
|
+
if (cmd === "pnpm" && ["install", "run"].includes(firstArg))
|
|
98
|
+
return true;
|
|
99
|
+
if (cmd === "git" && ["add", "commit", "push", "pull", "merge", "rebase", "checkout", "reset"].includes(firstArg))
|
|
100
|
+
return true;
|
|
101
|
+
if (cmd === "dotnet" && ["build", "run", "restore"].includes(firstArg))
|
|
102
|
+
return true;
|
|
103
|
+
if (cmd === "go" && ["build", "run", "mod"].includes(firstArg))
|
|
104
|
+
return true;
|
|
105
|
+
if (cmd === "cargo" && ["build", "check"].includes(firstArg))
|
|
106
|
+
return true;
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parse a command string into executable + args array.
|
|
111
|
+
* Uses spawn for safety (no shell injection).
|
|
112
|
+
*/
|
|
113
|
+
function parseCommand(cmdString) {
|
|
114
|
+
const parts = cmdString.trim().split(/\s+/);
|
|
115
|
+
if (parts.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
return { cmd: parts[0], args: parts.slice(1) };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if a parsed command matches the allow list.
|
|
121
|
+
*/
|
|
122
|
+
function isAllowed(parsed) {
|
|
123
|
+
return COMMAND_ALLOW_LIST.some(entry => {
|
|
124
|
+
if (entry.cmd !== parsed.cmd)
|
|
125
|
+
return false;
|
|
126
|
+
if (!entry.args)
|
|
127
|
+
return true; // Any args allowed for this cmd
|
|
128
|
+
if (!parsed.args || parsed.args.length < entry.args.length)
|
|
129
|
+
return false;
|
|
130
|
+
// Check that args start with the allowed prefix
|
|
131
|
+
return entry.args.every((arg, i) => parsed.args[i] === arg);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
87
134
|
export function handleRunCommand(projectRoot, args) {
|
|
88
135
|
const command = args.command;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
136
|
+
// Parse command into executable + args
|
|
137
|
+
const parsed = parseCommand(command);
|
|
138
|
+
if (!parsed) {
|
|
139
|
+
return Promise.resolve({
|
|
140
|
+
content: [{ type: "text", text: "ERROR: Empty command." }],
|
|
141
|
+
isError: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// Resolve active agent and tier to check command permission
|
|
145
|
+
const frameworkDir = resolveFrameworkDir(projectRoot);
|
|
146
|
+
const absoluteFrameworkPath = path.isAbsolute(frameworkDir)
|
|
147
|
+
? frameworkDir
|
|
148
|
+
: path.resolve(projectRoot, frameworkDir);
|
|
149
|
+
const activeAgent = resolveActiveAgent(absoluteFrameworkPath);
|
|
150
|
+
if (activeAgent) {
|
|
151
|
+
const tier = getAgentTier(activeAgent);
|
|
152
|
+
if (tier === "recon" && isWriteCommand(parsed)) {
|
|
153
|
+
return Promise.resolve({
|
|
154
|
+
isError: true,
|
|
155
|
+
content: [{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: `[RBAC] Permission Denied: Agent ${activeAgent} (tier: recon) is not allowed to run write/build command "${command}". Recon agents are restricted to read-only commands.`
|
|
158
|
+
}]
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!isAllowed(parsed)) {
|
|
163
|
+
const allowedList = COMMAND_ALLOW_LIST.map(e => e.args ? `${e.cmd} ${e.args.join(" ")}` : e.cmd);
|
|
164
|
+
const errorMsg = `Command not allowed: "${command}". Only the following are allowed: ${allowedList.join(", ")}`;
|
|
92
165
|
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command} (denied)`, errorMsg);
|
|
93
166
|
return Promise.resolve({
|
|
94
167
|
content: [{ type: "text", text: `ERROR: ${errorMsg}` }],
|
|
95
168
|
isError: true,
|
|
96
169
|
});
|
|
97
170
|
}
|
|
98
|
-
//
|
|
99
|
-
const hasShellMetacharacters = /[;&|><$`\n\r]/.test(
|
|
171
|
+
// Reject any args containing shell metacharacters
|
|
172
|
+
const hasShellMetacharacters = parsed.args.some(a => /[;&|><$`\n\r]/.test(a));
|
|
100
173
|
if (hasShellMetacharacters) {
|
|
101
174
|
const errorMsg = "Command rejected: Shell metacharacters are forbidden to prevent command injection.";
|
|
102
175
|
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command} (denied: metacharacters)`, errorMsg);
|
|
@@ -106,12 +179,30 @@ export function handleRunCommand(projectRoot, args) {
|
|
|
106
179
|
});
|
|
107
180
|
}
|
|
108
181
|
return new Promise((resolve) => {
|
|
109
|
-
|
|
182
|
+
const child = spawn(parsed.cmd, parsed.args, {
|
|
183
|
+
cwd: projectRoot,
|
|
184
|
+
timeout: TIMEOUT,
|
|
185
|
+
shell: false,
|
|
186
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
187
|
+
});
|
|
188
|
+
let stdout = "";
|
|
189
|
+
let stderr = "";
|
|
190
|
+
child.stdout.on("data", (data) => { stdout += data.toString(); });
|
|
191
|
+
child.stderr.on("data", (data) => { stderr += data.toString(); });
|
|
192
|
+
child.on("error", (err) => {
|
|
193
|
+
const errorMsg = `Failed to start command: ${err.message}`;
|
|
194
|
+
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command}`, errorMsg);
|
|
195
|
+
resolve({
|
|
196
|
+
content: [{ type: "text", text: `ERROR: ${errorMsg}` }],
|
|
197
|
+
isError: true,
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
child.on("close", (code) => {
|
|
110
201
|
const output = stdout + stderr;
|
|
111
202
|
const tokens = Metrics.estimateTokens(output);
|
|
112
203
|
Metrics.logUsage(projectRoot, "@mcp", `run_shell_command: ${command}`, tokens);
|
|
113
|
-
if (
|
|
114
|
-
const errorMsg = `Command failed with exit code ${
|
|
204
|
+
if (code !== 0) {
|
|
205
|
+
const errorMsg = `Command failed with exit code ${code}.`;
|
|
115
206
|
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command}`, errorMsg);
|
|
116
207
|
resolve({
|
|
117
208
|
content: [{ type: "text", text: `ERROR: ${errorMsg}. Output: ${output}` }],
|
|
@@ -121,11 +212,12 @@ export function handleRunCommand(projectRoot, args) {
|
|
|
121
212
|
}
|
|
122
213
|
// Truncate long outputs
|
|
123
214
|
const MAX_OUTPUT_LENGTH = 5000;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
215
|
+
const truncatedOutput = output.length > MAX_OUTPUT_LENGTH
|
|
216
|
+
? output.substring(0, MAX_OUTPUT_LENGTH) + "... [TRUNCATED] ..."
|
|
217
|
+
: output;
|
|
218
|
+
resolve({
|
|
219
|
+
content: [{ type: "text", text: truncatedOutput }],
|
|
220
|
+
});
|
|
129
221
|
});
|
|
130
222
|
});
|
|
131
223
|
}
|
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
* When MCP_AUTH_TOKEN is set, all MCP and API requests require
|
|
6
6
|
* Authorization: Bearer <token> header.
|
|
7
7
|
*
|
|
8
|
+
* Supports multi-user setup via MCP_AUTH_USERS:
|
|
9
|
+
* MCP_AUTH_USERS=alice:key1,bob:key2,cmdr:key3
|
|
10
|
+
*
|
|
11
|
+
* Each user gets their own API key. Tool calls and audit logs
|
|
12
|
+
* are tagged with the authenticated user's name.
|
|
13
|
+
*
|
|
8
14
|
* Environment Variables:
|
|
9
|
-
* MCP_AUTH_TOKEN - API key for
|
|
15
|
+
* MCP_AUTH_TOKEN - Master API key for admin access (optional)
|
|
10
16
|
* MCP_AUTH_USERS - Comma-separated list of user:token pairs (optional)
|
|
11
17
|
* Example: "alice:key1,bob:key2"
|
|
12
18
|
*
|
|
13
|
-
* If neither is set, authentication is disabled (open access).
|
|
19
|
+
* If neither is set, authentication is disabled (open access / anonymous).
|
|
14
20
|
*/
|
|
15
21
|
let authUsers = [];
|
|
16
22
|
let masterToken = null;
|
|
@@ -27,14 +33,14 @@ export function initAuth() {
|
|
|
27
33
|
}).filter(u => u.token);
|
|
28
34
|
}
|
|
29
35
|
if (masterToken) {
|
|
30
|
-
|
|
36
|
+
process.stderr.write(`[AUTH] Master token configured (${masterToken.length} chars)\n`);
|
|
31
37
|
}
|
|
32
38
|
if (authUsers.length > 0) {
|
|
33
|
-
|
|
39
|
+
process.stderr.write(`[AUTH] ${authUsers.length} user(s) configured: ${authUsers.map(u => u.name).join(", ")}\n`);
|
|
34
40
|
}
|
|
35
41
|
if (!masterToken && authUsers.length === 0) {
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
process.stderr.write("[AUTH] No authentication configured - OPEN ACCESS (anonymous users)\n");
|
|
43
|
+
process.stderr.write("[AUTH] Set MCP_AUTH_TOKEN=<key> or MCP_AUTH_USERS=user1:key1,user2:key2 to enable\n");
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
/**
|
|
@@ -43,7 +49,7 @@ export function initAuth() {
|
|
|
43
49
|
*/
|
|
44
50
|
export function authenticate(req) {
|
|
45
51
|
if (!masterToken && authUsers.length === 0) {
|
|
46
|
-
// Auth disabled - allow all
|
|
52
|
+
// Auth disabled - allow all as anonymous
|
|
47
53
|
return { authenticated: true, user: "anonymous" };
|
|
48
54
|
}
|
|
49
55
|
const authHeader = req.headers["authorization"];
|
|
@@ -64,6 +70,53 @@ export function authenticate(req) {
|
|
|
64
70
|
}
|
|
65
71
|
return { authenticated: false, user: "" };
|
|
66
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Authenticate using a raw token string (for MCP stdio mode).
|
|
75
|
+
* In stdio mode, the user identity is set via environment.
|
|
76
|
+
*/
|
|
77
|
+
export function authenticateToken(token) {
|
|
78
|
+
if (!masterToken && authUsers.length === 0) {
|
|
79
|
+
return { authenticated: true, user: "anonymous" };
|
|
80
|
+
}
|
|
81
|
+
// Check master token
|
|
82
|
+
if (masterToken && token === masterToken) {
|
|
83
|
+
return { authenticated: true, user: "admin" };
|
|
84
|
+
}
|
|
85
|
+
// Check user tokens
|
|
86
|
+
for (const user of authUsers) {
|
|
87
|
+
if (token === user.token) {
|
|
88
|
+
return { authenticated: true, user: user.name };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { authenticated: false, user: "" };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the current authenticated user identity for stdio mode.
|
|
95
|
+
* Reads from environment or returns "anonymous".
|
|
96
|
+
* For HTTP/SSE mode, use authenticate() on the request.
|
|
97
|
+
*/
|
|
98
|
+
export function getCurrentUser() {
|
|
99
|
+
// Stdio mode: user identity comes from env or git config
|
|
100
|
+
const envUser = process.env.MCP_USER;
|
|
101
|
+
if (envUser)
|
|
102
|
+
return envUser;
|
|
103
|
+
// Try to get from git config
|
|
104
|
+
try {
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
106
|
+
const { execSync } = require("child_process");
|
|
107
|
+
const gitUser = execSync("git config user.name", { encoding: "utf8" }).trim();
|
|
108
|
+
if (gitUser)
|
|
109
|
+
return gitUser;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// git not available
|
|
113
|
+
}
|
|
114
|
+
// Try system user
|
|
115
|
+
const systemUser = process.env.USER || process.env.USERNAME;
|
|
116
|
+
if (systemUser)
|
|
117
|
+
return systemUser;
|
|
118
|
+
return "anonymous";
|
|
119
|
+
}
|
|
67
120
|
/**
|
|
68
121
|
* Generate authentication headers for MCP client configuration.
|
|
69
122
|
*/
|
|
@@ -76,3 +129,17 @@ export function getAuthHeader(user, token) {
|
|
|
76
129
|
export function isAuthEnabled() {
|
|
77
130
|
return !!masterToken || authUsers.length > 0;
|
|
78
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Get list of configured users (without tokens).
|
|
134
|
+
*/
|
|
135
|
+
export function getConfiguredUsers() {
|
|
136
|
+
if (authUsers.length > 0) {
|
|
137
|
+
return authUsers.map(u => u.name);
|
|
138
|
+
}
|
|
139
|
+
if (masterToken) {
|
|
140
|
+
return ["admin"];
|
|
141
|
+
}
|
|
142
|
+
return ["anonymous"];
|
|
143
|
+
}
|
|
144
|
+
// Auto-initialize on load
|
|
145
|
+
initAuth();
|
|
@@ -2,6 +2,8 @@ import ts from "typescript";
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { resolveFrameworkDir } from "./security.js";
|
|
5
|
+
import { resolveActiveAgent } from "./permissions.js";
|
|
6
|
+
import { createApprovalRequest, getApprovalStatus } from "./human-in-loop.js";
|
|
5
7
|
/**
|
|
6
8
|
* Enterprise Compliance Guardrail
|
|
7
9
|
* Checks content against corporate standards using AST analysis before allowing file mutations.
|
|
@@ -57,7 +59,7 @@ export function verifyCorporateCompliance(content, filePath) {
|
|
|
57
59
|
filePath.includes("shared/fs.ts") ||
|
|
58
60
|
filePath.includes("shared/lock.ts") ||
|
|
59
61
|
filePath.includes("quality-gate.ts") ||
|
|
60
|
-
filePath.includes("
|
|
62
|
+
filePath.includes("shell/run_command.ts");
|
|
61
63
|
if (!isExempt) {
|
|
62
64
|
errors.push(`[ERROR] Corporate Compliance Breach: Direct 'child_process' usage is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Use secure framework APIs.`);
|
|
63
65
|
}
|
|
@@ -85,7 +87,10 @@ export function verifyCorporateCompliance(content, filePath) {
|
|
|
85
87
|
// Lock files, core atomic fs wrapper, and mcp configs are exempt
|
|
86
88
|
const args = node.arguments;
|
|
87
89
|
const firstArgText = args.length > 0 ? args[0].getText(sourceFile).toLowerCase() : "";
|
|
88
|
-
const isExemptFile = filePath.includes("shared/fs.ts") ||
|
|
90
|
+
const isExemptFile = filePath.includes("shared/fs.ts") ||
|
|
91
|
+
filePath.includes("src/cli/commands/mcp.ts") ||
|
|
92
|
+
filePath.includes("utils/compliance.ts") ||
|
|
93
|
+
filePath.includes("tools/messaging/send_message.ts");
|
|
89
94
|
if (!firstArgText.includes("lock") && !isExemptFile) {
|
|
90
95
|
errors.push(`[ERROR] Corporate Compliance Breach: Raw 'fs.${prop.name.text}' mutation detected at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Use atomic utilities instead.`);
|
|
91
96
|
}
|
|
@@ -204,7 +209,9 @@ export async function verifyRiskAndAwaitApproval(projectRoot, content, filePath)
|
|
|
204
209
|
const statePath = path.join(absoluteFrameworkPath, "memory", "state.json");
|
|
205
210
|
const messagesDir = path.join(absoluteFrameworkPath, "messages");
|
|
206
211
|
const managerMsgPath = path.join(messagesDir, "manager.json");
|
|
207
|
-
|
|
212
|
+
const activeAgent = resolveActiveAgent(absoluteFrameworkPath);
|
|
213
|
+
if (!activeAgent)
|
|
214
|
+
throw new Error(`Security Exception: High-risk operation blocked. ${assessment.reason}.`);
|
|
208
215
|
let traceId = "UNKNOWN";
|
|
209
216
|
if (fs.existsSync(statePath)) {
|
|
210
217
|
try {
|
|
@@ -214,21 +221,25 @@ export async function verifyRiskAndAwaitApproval(projectRoot, content, filePath)
|
|
|
214
221
|
}
|
|
215
222
|
catch { /* ignore */ }
|
|
216
223
|
}
|
|
224
|
+
// Check if already approved via human-in-loop
|
|
225
|
+
const existingStatus = getApprovalStatus(traceId);
|
|
226
|
+
if (existingStatus?.status === "APPROVED") {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (existingStatus?.status === "REJECTED" || existingStatus?.status === "EXPIRED") {
|
|
230
|
+
throw new Error(`Security Exception: High-risk operation was ${existingStatus.status}.`);
|
|
231
|
+
}
|
|
232
|
+
// Register in human-in-loop if not already present
|
|
233
|
+
if (!existingStatus) {
|
|
234
|
+
createApprovalRequest(traceId, "file_mutation", activeAgent, 80, assessment.reason || "High-risk operation detected", { filePath });
|
|
235
|
+
}
|
|
217
236
|
let statusData = {};
|
|
218
237
|
if (fs.existsSync(statusPath)) {
|
|
219
238
|
try {
|
|
220
239
|
statusData = JSON.parse(fs.readFileSync(statusPath, "utf8"));
|
|
221
|
-
for (const [agentName, info] of Object.entries(statusData)) {
|
|
222
|
-
if (info.state === "EXECUTING") {
|
|
223
|
-
activeAgent = agentName.startsWith("@") ? agentName : `@${agentName}`;
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
240
|
}
|
|
228
241
|
catch { /* ignore */ }
|
|
229
242
|
}
|
|
230
|
-
if (!activeAgent)
|
|
231
|
-
throw new Error(`Security Exception: High-risk operation blocked. ${assessment.reason}.`);
|
|
232
243
|
const statusKey = activeAgent.replace("@", "");
|
|
233
244
|
const originalTask = statusData[statusKey]?.task || "Executing task";
|
|
234
245
|
statusData[statusKey] = {
|
|
@@ -253,12 +264,16 @@ export async function verifyRiskAndAwaitApproval(projectRoot, content, filePath)
|
|
|
253
264
|
requiresApproval: true,
|
|
254
265
|
action: `MUTATION:${filePath}`
|
|
255
266
|
};
|
|
267
|
+
// Append to managerMsgPath for backward compatibility / legacy alerts
|
|
256
268
|
try {
|
|
257
269
|
fs.appendFileSync(managerMsgPath, JSON.stringify(alertMsg) + "\n");
|
|
258
270
|
}
|
|
259
271
|
catch (err) {
|
|
260
272
|
statusData[statusKey] = { state: "EXECUTING", task: originalTask };
|
|
261
|
-
|
|
273
|
+
try {
|
|
274
|
+
fs.writeFileSync(statusPath, JSON.stringify(statusData, null, 2));
|
|
275
|
+
}
|
|
276
|
+
catch { /* ignore */ }
|
|
262
277
|
throw new Error("Security Exception: Failed to queue approval request.", { cause: err });
|
|
263
278
|
}
|
|
264
279
|
const pollIntervalMs = 500;
|
|
@@ -266,6 +281,20 @@ export async function verifyRiskAndAwaitApproval(projectRoot, content, filePath)
|
|
|
266
281
|
const start = Date.now();
|
|
267
282
|
while (Date.now() - start < timeoutMs) {
|
|
268
283
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
284
|
+
// Check if approved via human-in-loop (e.g. approve_operation tool call)
|
|
285
|
+
const checkStatus = getApprovalStatus(traceId);
|
|
286
|
+
if (checkStatus?.status === "APPROVED") {
|
|
287
|
+
statusData[statusKey] = { state: "EXECUTING", task: originalTask };
|
|
288
|
+
try {
|
|
289
|
+
fs.writeFileSync(statusPath, JSON.stringify(statusData, null, 2));
|
|
290
|
+
}
|
|
291
|
+
catch { /* ignore */ }
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
else if (checkStatus?.status === "REJECTED" || checkStatus?.status === "EXPIRED") {
|
|
295
|
+
throw new Error(`Security Exception: High-risk operation was explicitly ${checkStatus.status}.`);
|
|
296
|
+
}
|
|
297
|
+
// Check legacy managerMsgPath approval
|
|
269
298
|
if (fs.existsSync(managerMsgPath)) {
|
|
270
299
|
try {
|
|
271
300
|
const lines = fs.readFileSync(managerMsgPath, "utf8").trim().split("\n");
|
|
@@ -276,7 +305,10 @@ export async function verifyRiskAndAwaitApproval(projectRoot, content, filePath)
|
|
|
276
305
|
if (parsed.traceId === traceId && parsed.category === "ALERT" && parsed.action === `MUTATION:${filePath}`) {
|
|
277
306
|
if (parsed.status === "APPROVED") {
|
|
278
307
|
statusData[statusKey] = { state: "EXECUTING", task: originalTask };
|
|
279
|
-
|
|
308
|
+
try {
|
|
309
|
+
fs.writeFileSync(statusPath, JSON.stringify(statusData, null, 2));
|
|
310
|
+
}
|
|
311
|
+
catch { /* ignore */ }
|
|
280
312
|
return;
|
|
281
313
|
}
|
|
282
314
|
else if (parsed.status === "PROCESSED" || parsed.status === "DENIED") {
|
|
@@ -156,8 +156,41 @@ export async function enforceDiscipline(agent, toolName, args) {
|
|
|
156
156
|
});
|
|
157
157
|
return null;
|
|
158
158
|
}
|
|
159
|
+
// ─── Prompt Injection Patterns ─────────────────────────────────────────────
|
|
160
|
+
// Common adversarial patterns that attempt to override agent instructions.
|
|
161
|
+
// These are checked in tool responses to prevent exfiltrated injection payloads
|
|
162
|
+
// (e.g. from malicious file content, API responses, or external user input)
|
|
163
|
+
// from reaching the AI and overriding the system prompt.
|
|
164
|
+
const PROMPT_INJECTION_PATTERNS = [
|
|
165
|
+
{ pattern: /ignore\s+(previous|prior|above|all)\s+instructions?/i, label: "ignore-instructions" },
|
|
166
|
+
{ pattern: /disregard\s+(previous|prior|above|all)\s+instructions?/i, label: "disregard-instructions" },
|
|
167
|
+
{ pattern: /forget\s+(everything|all|prior|previous)/i, label: "forget-prior" },
|
|
168
|
+
{ pattern: /you\s+are\s+now\s+(a|an)\s+/i, label: "persona-override" },
|
|
169
|
+
{ pattern: /\bsystem\s*:\s*(you|your|ignore|forget|act)/i, label: "system-prompt-inject" },
|
|
170
|
+
{ pattern: /\bnew\s+instructions?\s*:/i, label: "new-instructions" },
|
|
171
|
+
{ pattern: /\bact\s+as\s+(if\s+you\s+are|a|an)\s+/i, label: "act-as-override" },
|
|
172
|
+
{ pattern: /\boverride\s+(safety|rules|guidelines|instructions)/i, label: "safety-override" },
|
|
173
|
+
{ pattern: /\bdo\s+not\s+follow\s+(your|the|any)\s+(rules|instructions)/i, label: "rule-bypass" },
|
|
174
|
+
{ pattern: /\bdeveloper\s+mode\s+(enabled|activated|on)/i, label: "developer-mode" },
|
|
175
|
+
{ pattern: /\bjailbreak/i, label: "jailbreak" },
|
|
176
|
+
{ pattern: /<\|.*?\|>/, label: "token-boundary-inject" },
|
|
177
|
+
{ pattern: /\[INST\]|\[\/?SYS\]|<<SYS>>/, label: "llm-template-inject" },
|
|
178
|
+
];
|
|
179
|
+
/**
|
|
180
|
+
* Scans a text block for known prompt injection patterns.
|
|
181
|
+
* Returns the matched pattern label if injection is detected, null otherwise.
|
|
182
|
+
*/
|
|
183
|
+
function scanForPromptInjection(text) {
|
|
184
|
+
for (const { pattern, label } of PROMPT_INJECTION_PATTERNS) {
|
|
185
|
+
if (pattern.test(text)) {
|
|
186
|
+
return label;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
159
191
|
/**
|
|
160
192
|
* Validate tool response content before returning to AI.
|
|
193
|
+
* Blocks: oversized responses, binary data, and prompt injection attempts.
|
|
161
194
|
* Returns error message if blocked, null if allowed.
|
|
162
195
|
*/
|
|
163
196
|
export function validateResponse(toolName, result) {
|
|
@@ -166,15 +199,21 @@ export function validateResponse(toolName, result) {
|
|
|
166
199
|
for (const block of result.content) {
|
|
167
200
|
if (block.type !== "text" || !block.text)
|
|
168
201
|
continue;
|
|
169
|
-
// Check response size
|
|
202
|
+
// 1. Check response size
|
|
170
203
|
if (block.text.length > CONFIG.MAX_RESPONSE_SIZE) {
|
|
171
204
|
return `[DISCIPLINE] Response too large (${(block.text.length / 1024).toFixed(1)}KB, max: ${(CONFIG.MAX_RESPONSE_SIZE / 1024).toFixed(1)}KB).`;
|
|
172
205
|
}
|
|
173
|
-
// Block binary/gibberish content in text responses
|
|
206
|
+
// 2. Block binary/gibberish content in text responses
|
|
174
207
|
const nonPrintable = (block.text.match(/[^\x20-\x7E\n\r\t]/g) || []).length;
|
|
175
208
|
if (nonPrintable > block.text.length * 0.3) {
|
|
176
209
|
return `[DISCIPLINE] Response contains too much binary data (${nonPrintable} non-printable chars). Blocked.`;
|
|
177
210
|
}
|
|
211
|
+
// 3. Prompt injection detection — scan for adversarial override patterns
|
|
212
|
+
// that may have been embedded in file content, API responses, or user input.
|
|
213
|
+
const injectionLabel = scanForPromptInjection(block.text);
|
|
214
|
+
if (injectionLabel) {
|
|
215
|
+
return `[DISCIPLINE] Prompt injection pattern detected in "${toolName}" response (pattern: ${injectionLabel}). Response blocked to protect agent integrity.`;
|
|
216
|
+
}
|
|
178
217
|
}
|
|
179
218
|
return null;
|
|
180
219
|
}
|
|
@@ -202,7 +241,7 @@ export function getDisciplineStats(agent) {
|
|
|
202
241
|
*/
|
|
203
242
|
export function getAllDisciplineStats() {
|
|
204
243
|
const stats = {};
|
|
205
|
-
for (const [agent
|
|
244
|
+
for (const [agent] of agentDiscipline) {
|
|
206
245
|
stats[agent] = getDisciplineStats(agent);
|
|
207
246
|
}
|
|
208
247
|
return stats;
|