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
|
@@ -173,11 +173,12 @@ export function updateDocumentStore(type, data, id, frameworkDir) {
|
|
|
173
173
|
break;
|
|
174
174
|
}
|
|
175
175
|
case "history": {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
Storage.saveLog({
|
|
177
|
+
agent: "@manager",
|
|
178
|
+
action: "HISTORY_UPDATE",
|
|
179
|
+
status: "SUCCESS",
|
|
180
|
+
summary: data.content,
|
|
181
|
+
});
|
|
181
182
|
break;
|
|
182
183
|
}
|
|
183
184
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { scanProjectCompliance } from "../../cli/utils/compliance.js";
|
|
4
|
+
import { getFrameworkDir } from "../../cli/utils/memory.js";
|
|
5
|
+
import { appendFile, ensureDir, runCommandQuiet, writeTextFile } from "../../shared/fs.js";
|
|
6
|
+
import { logger } from "../../shared/logger.js";
|
|
7
|
+
import { asTraceID } from "../../shared/types.js";
|
|
8
|
+
export class EvaluationEngine {
|
|
9
|
+
/**
|
|
10
|
+
* Evaluates a completed task, calculates a score, and updates Specialty Memory.
|
|
11
|
+
* Saves lessons from both failures AND successes.
|
|
12
|
+
*/
|
|
13
|
+
static evaluateTask(traceIdStr, agentName, durationMs, taskDescription) {
|
|
14
|
+
const traceId = asTraceID(traceIdStr);
|
|
15
|
+
logger.debug(`Starting evaluation for Trace ${traceId} by ${agentName}`);
|
|
16
|
+
let score = 100;
|
|
17
|
+
const metrics = {
|
|
18
|
+
compilation: true,
|
|
19
|
+
lint: true,
|
|
20
|
+
tests: true,
|
|
21
|
+
compliance: 0
|
|
22
|
+
};
|
|
23
|
+
const projectRoot = process.cwd();
|
|
24
|
+
const lessons = [];
|
|
25
|
+
// 1. Compliance Check (AST-based discipline check)
|
|
26
|
+
try {
|
|
27
|
+
const issues = scanProjectCompliance("src");
|
|
28
|
+
metrics.compliance = issues.length;
|
|
29
|
+
if (issues.length > 0) {
|
|
30
|
+
score -= Math.min(issues.length * 5, 40);
|
|
31
|
+
lessons.push("Compliance Violations Detected: Ensure strict adherence to zero-any, zero-console.log policies.");
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
lessons.push("Compliance check passed: No any types or console.log violations found.");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
logger.debug("Compliance check failed during evaluation", e);
|
|
39
|
+
}
|
|
40
|
+
// 2. Compilation Check
|
|
41
|
+
try {
|
|
42
|
+
if (fs.existsSync(path.join(projectRoot, "tsconfig.json"))) {
|
|
43
|
+
runCommandQuiet("npx", ["tsc", "--noEmit"], projectRoot);
|
|
44
|
+
lessons.push("TypeScript compilation succeeded. Project is type-safe.");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
metrics.compilation = false;
|
|
49
|
+
score -= 20;
|
|
50
|
+
lessons.push("Compilation Error: Always run 'npx tsc --noEmit' to verify type safety before completing a task.");
|
|
51
|
+
}
|
|
52
|
+
// 3. Lint Check
|
|
53
|
+
try {
|
|
54
|
+
if (fs.existsSync(path.join(projectRoot, "eslint.config.js"))) {
|
|
55
|
+
runCommandQuiet("npx", ["eslint", ".", "--max-warnings", "0"], projectRoot);
|
|
56
|
+
lessons.push("Lint check passed. Code follows project style guidelines.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
metrics.lint = false;
|
|
61
|
+
score -= 10;
|
|
62
|
+
}
|
|
63
|
+
// 4. Test Check
|
|
64
|
+
try {
|
|
65
|
+
if (fs.existsSync(path.join(projectRoot, "vitest.config.ts"))) {
|
|
66
|
+
runCommandQuiet("npx", ["vitest", "run", "--passWithNoTests"], projectRoot);
|
|
67
|
+
lessons.push("All tests passed. Changes are verified.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
metrics.tests = false;
|
|
72
|
+
score -= 20;
|
|
73
|
+
lessons.push("Test Failure: Ensure all unit tests pass before marking a task as COMPLETED.");
|
|
74
|
+
}
|
|
75
|
+
// 5. Efficiency Check
|
|
76
|
+
if (durationMs > 120000) {
|
|
77
|
+
score -= 10;
|
|
78
|
+
lessons.push("Task took longer than 2 minutes. Consider breaking large tasks into smaller steps.");
|
|
79
|
+
}
|
|
80
|
+
else if (score >= 80) {
|
|
81
|
+
lessons.push("Task completed efficiently within expected timeframe.");
|
|
82
|
+
}
|
|
83
|
+
// 6. Success Lesson — if score is high and no critical errors
|
|
84
|
+
if (score >= 80 && taskDescription) {
|
|
85
|
+
const successLesson = this.extractSuccessLesson(agentName, taskDescription);
|
|
86
|
+
if (successLesson) {
|
|
87
|
+
lessons.push(successLesson);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Save all lessons to specialty memory
|
|
91
|
+
for (const lesson of lessons) {
|
|
92
|
+
this.updateSpecialtyMemory(agentName, lesson);
|
|
93
|
+
}
|
|
94
|
+
const result = {
|
|
95
|
+
traceId,
|
|
96
|
+
agent: agentName.replace("@", ""),
|
|
97
|
+
score: Math.max(0, score),
|
|
98
|
+
metrics,
|
|
99
|
+
durationMs,
|
|
100
|
+
timestamp: new Date().toISOString()
|
|
101
|
+
};
|
|
102
|
+
logger.debug(`Evaluation completed for ${agentName}: Score ${result.score}/100`);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
static extractSuccessLesson(agentName, taskDescription) {
|
|
106
|
+
const patterns = [
|
|
107
|
+
{ keywords: ["api", "endpoint", "route", "rest"], category: "REST API pattern" },
|
|
108
|
+
{ keywords: ["auth", "login", "jwt", "token"], category: "Authentication/authorization/JWT pattern" },
|
|
109
|
+
{ keywords: ["test", "spec", "vitest"], category: "Test-driven development approach" },
|
|
110
|
+
{ keywords: ["migration", "schema", "database", "sql"], category: "Database schema/migration pattern" },
|
|
111
|
+
{ keywords: ["component", "react", "ui", "page"], category: "UI component design pattern" },
|
|
112
|
+
{ keywords: ["middleware", "guard", "interceptor"], category: "Middleware/guard pipeline pattern" },
|
|
113
|
+
{ keywords: ["service", "repository", "inject"], category: "Service/repository architecture pattern" },
|
|
114
|
+
{ keywords: ["error", "exception", "catch", "try"], category: "Robust error handling pattern" },
|
|
115
|
+
{ keywords: ["config", "env", "environment"], category: "Configuration and environment control pattern" },
|
|
116
|
+
];
|
|
117
|
+
const lower = taskDescription.toLowerCase();
|
|
118
|
+
let categoryStr = "";
|
|
119
|
+
for (const { keywords, category } of patterns) {
|
|
120
|
+
if (keywords.some(k => lower.includes(k))) {
|
|
121
|
+
categoryStr = ` using ${category}`;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const cleanedTask = taskDescription.replace(/\r?\n/g, " ").trim();
|
|
126
|
+
const truncatedTask = cleanedTask.length > 120 ? cleanedTask.substring(0, 117) + "..." : cleanedTask;
|
|
127
|
+
const capitalizedTask = truncatedTask.charAt(0).toUpperCase() + truncatedTask.slice(1);
|
|
128
|
+
return `Successfully completed task: "${capitalizedTask}"${categoryStr}.`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Reads learned conventions from an agent's specialty memory.
|
|
132
|
+
* Used by silent-router to inject context into AI calls.
|
|
133
|
+
*/
|
|
134
|
+
static readLearnedConventions(agentName) {
|
|
135
|
+
const cleanName = agentName.replace("@", "");
|
|
136
|
+
const fwDir = getFrameworkDir();
|
|
137
|
+
const filePath = path.join(fwDir, "memory", "specialties", `${cleanName}.md`);
|
|
138
|
+
try {
|
|
139
|
+
if (fs.existsSync(filePath)) {
|
|
140
|
+
const content = fs.readFileSync(filePath, "utf8").trim();
|
|
141
|
+
if (content)
|
|
142
|
+
return content;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// File might not exist yet
|
|
147
|
+
}
|
|
148
|
+
return "";
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Appends learned lessons to the agent's specialty memory.
|
|
152
|
+
*/
|
|
153
|
+
static updateSpecialtyMemory(agentName, lesson) {
|
|
154
|
+
const cleanName = agentName.replace("@", "");
|
|
155
|
+
const fwDir = getFrameworkDir();
|
|
156
|
+
const specialtiesDir = path.join(fwDir, "memory", "specialties");
|
|
157
|
+
ensureDir(specialtiesDir);
|
|
158
|
+
const filePath = path.join(specialtiesDir, `${cleanName}.md`);
|
|
159
|
+
const timestamp = new Date().toISOString().split("T")[0];
|
|
160
|
+
const entry = `- **[${timestamp}]**: ${lesson}\n`;
|
|
161
|
+
if (!fs.existsSync(filePath)) {
|
|
162
|
+
writeTextFile(filePath, `# Learned Conventions for @${cleanName}\n\n`);
|
|
163
|
+
}
|
|
164
|
+
// Avoid duplicate lessons on the same day
|
|
165
|
+
const currentContent = fs.readFileSync(filePath, "utf8");
|
|
166
|
+
if (!currentContent.includes(lesson)) {
|
|
167
|
+
appendFile(filePath, entry);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -73,10 +73,12 @@ export class QualityGate {
|
|
|
73
73
|
if (!output || output.trim().length < 10) {
|
|
74
74
|
return { passed: false, reason: "Output is too short or empty." };
|
|
75
75
|
}
|
|
76
|
-
// Check for hard error indicators
|
|
76
|
+
// Check for hard error indicators (word-boundary match to avoid false positives
|
|
77
|
+
// on valid identifiers such as 'ErrorBoundary', 'handleError', 'TIMEOUT_MS',
|
|
78
|
+
// 'FAILED_VALIDATION', etc.).
|
|
77
79
|
const errorIndicators = ["ERROR", "FAILED", "CRASHED", "TIMEOUT"];
|
|
78
80
|
for (const indicator of errorIndicators) {
|
|
79
|
-
if (output.toUpperCase()
|
|
81
|
+
if (new RegExp(`\\b${indicator}\\b`).test(output.toUpperCase())) {
|
|
80
82
|
return { passed: false, reason: `Output contains error indicator: ${indicator}` };
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* [SECURITY] Risk Engine (The Guardian)
|
|
3
3
|
* Calculates the danger level of a proposed task or operation.
|
|
4
|
+
*
|
|
5
|
+
* Scoring model (v2 — contextual behavioral analysis):
|
|
6
|
+
* - Keyword signals: intent words in natural-language task descriptions
|
|
7
|
+
* - Path signals: sensitivity of file paths / env patterns
|
|
8
|
+
* - Behavioral signals: blast-radius proxies — file count, line deletions, bulk patterns
|
|
9
|
+
* - Complexity signal: task description length as misunderstanding proxy
|
|
10
|
+
*
|
|
11
|
+
* requiresApproval threshold: totalScore >= 60
|
|
4
12
|
*/
|
|
5
13
|
export class RiskEngine {
|
|
6
14
|
static HIGH_RISK_KEYWORDS = [
|
|
@@ -18,10 +26,18 @@ export class RiskEngine {
|
|
|
18
26
|
{ pattern: /database/i, weight: 30 },
|
|
19
27
|
{ pattern: /auth/i, weight: 30 },
|
|
20
28
|
{ pattern: /security/i, weight: 30 },
|
|
21
|
-
{ pattern: /atabey/i, weight: 40 }, // Framework protection
|
|
29
|
+
{ pattern: /atabey/i, weight: 40 }, // Framework self-protection
|
|
30
|
+
];
|
|
31
|
+
// ─── Behavioral Signal Patterns ──────────────────────────────────────────
|
|
32
|
+
// Glob / wildcard patterns in paths that indicate bulk/mass-scope operations.
|
|
33
|
+
static BULK_SCOPE_PATTERNS = [
|
|
34
|
+
/\*\*/, // recursive glob
|
|
35
|
+
/\*\.[a-z]{2,4}/, // wildcard extension (*.ts, *.js)
|
|
36
|
+
/\/\*$/, // directory wildcard
|
|
22
37
|
];
|
|
23
38
|
/**
|
|
24
39
|
* Assesses the risk of a natural language task or command string.
|
|
40
|
+
* Combines keyword analysis, path sensitivity, and complexity signals.
|
|
25
41
|
*/
|
|
26
42
|
static assessTaskRisk(task) {
|
|
27
43
|
const factors = [];
|
|
@@ -48,7 +64,31 @@ export class RiskEngine {
|
|
|
48
64
|
totalScore += weight;
|
|
49
65
|
}
|
|
50
66
|
}
|
|
51
|
-
// 3.
|
|
67
|
+
// 3. Behavioral — Bulk scope detection in task text
|
|
68
|
+
for (const pattern of this.BULK_SCOPE_PATTERNS) {
|
|
69
|
+
if (pattern.test(task)) {
|
|
70
|
+
const score = 25;
|
|
71
|
+
factors.push({
|
|
72
|
+
factor: "Bulk Scope Pattern",
|
|
73
|
+
score,
|
|
74
|
+
description: `Task references a wildcard/glob pattern (${pattern.source}), indicating a potentially mass-scope operation.`
|
|
75
|
+
});
|
|
76
|
+
totalScore += score;
|
|
77
|
+
break; // count once even if multiple glob patterns match
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 4. Behavioral — Line deletion volume proxy
|
|
81
|
+
// Detect phrases like "delete all", "remove all", "wipe", "clear all"
|
|
82
|
+
if (/\b(delete|remove|wipe|clear)\s+all\b/i.test(task)) {
|
|
83
|
+
const score = 30;
|
|
84
|
+
factors.push({
|
|
85
|
+
factor: "Mass Deletion Intent",
|
|
86
|
+
score,
|
|
87
|
+
description: "Task uses mass-deletion language ('delete all', 'remove all', 'wipe', 'clear all') suggesting bulk data destruction."
|
|
88
|
+
});
|
|
89
|
+
totalScore += score;
|
|
90
|
+
}
|
|
91
|
+
// 5. Complexity Risk (Length as misunderstanding proxy)
|
|
52
92
|
if (task.length > 300) {
|
|
53
93
|
const score = 10;
|
|
54
94
|
factors.push({
|
|
@@ -62,8 +102,9 @@ export class RiskEngine {
|
|
|
62
102
|
}
|
|
63
103
|
/**
|
|
64
104
|
* Assesses risk based on a proposed file change.
|
|
105
|
+
* Adds behavioral signals: file count impact and bulk-scope path patterns.
|
|
65
106
|
*/
|
|
66
|
-
static assessChangeRisk(filePath, operation) {
|
|
107
|
+
static assessChangeRisk(filePath, operation, options) {
|
|
67
108
|
const factors = [];
|
|
68
109
|
let totalScore = 0;
|
|
69
110
|
// 1. Operation Weight
|
|
@@ -85,6 +126,60 @@ export class RiskEngine {
|
|
|
85
126
|
totalScore += weight;
|
|
86
127
|
}
|
|
87
128
|
}
|
|
129
|
+
// 3. Behavioral — Bulk scope path (glob/wildcard in filePath)
|
|
130
|
+
for (const pattern of this.BULK_SCOPE_PATTERNS) {
|
|
131
|
+
if (pattern.test(filePath)) {
|
|
132
|
+
const score = 25;
|
|
133
|
+
factors.push({
|
|
134
|
+
factor: "Bulk Scope Path",
|
|
135
|
+
score,
|
|
136
|
+
description: `File path '${filePath}' contains a wildcard/glob pattern, suggesting a mass-scope write operation.`
|
|
137
|
+
});
|
|
138
|
+
totalScore += score;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// 4. Behavioral — File count impact
|
|
143
|
+
// Writing to many files at once multiplies blast radius.
|
|
144
|
+
const { affectedFileCount = 1, deletedLineCount = 0 } = options ?? {};
|
|
145
|
+
if (affectedFileCount > 10) {
|
|
146
|
+
const score = Math.min(affectedFileCount * 2, 40);
|
|
147
|
+
factors.push({
|
|
148
|
+
factor: `High File Count: ${affectedFileCount} files`,
|
|
149
|
+
score,
|
|
150
|
+
description: `Operation touches ${affectedFileCount} files — high blast radius.`
|
|
151
|
+
});
|
|
152
|
+
totalScore += score;
|
|
153
|
+
}
|
|
154
|
+
else if (affectedFileCount > 3) {
|
|
155
|
+
const score = 15;
|
|
156
|
+
factors.push({
|
|
157
|
+
factor: `Multi-file: ${affectedFileCount} files`,
|
|
158
|
+
score,
|
|
159
|
+
description: `Operation touches ${affectedFileCount} files — moderate blast radius.`
|
|
160
|
+
});
|
|
161
|
+
totalScore += score;
|
|
162
|
+
}
|
|
163
|
+
// 5. Behavioral — Line deletion volume
|
|
164
|
+
// Bulk line deletions (>100 lines) indicate destructive operations.
|
|
165
|
+
if (deletedLineCount > 500) {
|
|
166
|
+
const score = 35;
|
|
167
|
+
factors.push({
|
|
168
|
+
factor: `Mass Line Deletion: ${deletedLineCount} lines`,
|
|
169
|
+
score,
|
|
170
|
+
description: `Operation deletes ${deletedLineCount} lines — very high data destruction risk.`
|
|
171
|
+
});
|
|
172
|
+
totalScore += score;
|
|
173
|
+
}
|
|
174
|
+
else if (deletedLineCount > 100) {
|
|
175
|
+
const score = 20;
|
|
176
|
+
factors.push({
|
|
177
|
+
factor: `Large Line Deletion: ${deletedLineCount} lines`,
|
|
178
|
+
score,
|
|
179
|
+
description: `Operation deletes ${deletedLineCount} lines — elevated data destruction risk.`
|
|
180
|
+
});
|
|
181
|
+
totalScore += score;
|
|
182
|
+
}
|
|
88
183
|
return this.finalizeAssessment(totalScore, factors);
|
|
89
184
|
}
|
|
90
185
|
static finalizeAssessment(totalScore, factors) {
|
|
@@ -5,6 +5,7 @@ import { logger } from "../../shared/logger.js";
|
|
|
5
5
|
import { AtabeyStorage } from "../../shared/storage.js";
|
|
6
6
|
import { asAgentID, asTraceID } from "../../shared/types.js";
|
|
7
7
|
import { ALL_AGENTS } from "../agents/definitions.js";
|
|
8
|
+
import { CoreMemory } from "../memory/core.js";
|
|
8
9
|
export class RoutingEngine {
|
|
9
10
|
/**
|
|
10
11
|
* Advanced Routing Engine — analyzes task descriptions,
|
|
@@ -12,6 +13,10 @@ export class RoutingEngine {
|
|
|
12
13
|
*
|
|
13
14
|
* Uses real TF-IDF scoring: term frequency in the task weighted by
|
|
14
15
|
* inverse document frequency across the full agent candidate corpus.
|
|
16
|
+
*
|
|
17
|
+
* Semantic routing via `resolveWithEmbeddings()` blends TF-IDF scores
|
|
18
|
+
* with cosine similarity over local TF-IDF embeddings (or OpenAI embeddings
|
|
19
|
+
* when OPENAI_API_KEY is set). Use the async variant for higher accuracy.
|
|
15
20
|
*/
|
|
16
21
|
static resolveAgent(taskDescription) {
|
|
17
22
|
return this.resolveWithDetails(taskDescription).agent;
|
|
@@ -85,9 +90,100 @@ export class RoutingEngine {
|
|
|
85
90
|
subTasks: bestSubTasks,
|
|
86
91
|
};
|
|
87
92
|
}
|
|
93
|
+
// ─── Semantic Routing (Embedding-based) ─────────────────────────────────
|
|
94
|
+
/**
|
|
95
|
+
* Semantic routing via embedding cosine similarity.
|
|
96
|
+
*
|
|
97
|
+
* Blends TF-IDF score (60%) with embedding cosine similarity (40%).
|
|
98
|
+
* Falls back gracefully to pure TF-IDF when embeddings are unavailable.
|
|
99
|
+
*
|
|
100
|
+
* When OPENAI_API_KEY is set, uses text-embedding-3-small (1536-dim).
|
|
101
|
+
* Otherwise falls back to the local TF-IDF vectorizer (384-dim) from
|
|
102
|
+
* CoreMemory.generateEmbedding(), which is already used by the memory system.
|
|
103
|
+
*
|
|
104
|
+
* @param taskDescription - Natural language task to route
|
|
105
|
+
* @returns Routing result with semantic score blend
|
|
106
|
+
*/
|
|
107
|
+
static async resolveWithEmbeddings(taskDescription) {
|
|
108
|
+
// Start TF-IDF routing in parallel with embedding generation
|
|
109
|
+
const tfidfResult = this.resolveWithDetails(taskDescription);
|
|
110
|
+
try {
|
|
111
|
+
const taskVector = CoreMemory.generateEmbedding(taskDescription);
|
|
112
|
+
const candidates = this.getCandidates();
|
|
113
|
+
let bestAgent = tfidfResult.agent;
|
|
114
|
+
let bestBlendedScore = -Infinity;
|
|
115
|
+
let bestReasoning = tfidfResult.reasoning;
|
|
116
|
+
for (const candidate of candidates) {
|
|
117
|
+
// Build a combined specialty text for this agent to embed
|
|
118
|
+
const specialtyText = Object.keys(candidate.specialties).join(" ") +
|
|
119
|
+
" " + candidate.role + " " + candidate.displayName;
|
|
120
|
+
const agentVector = CoreMemory.generateEmbedding(specialtyText);
|
|
121
|
+
const similarity = this.cosineSimilarity(taskVector, agentVector);
|
|
122
|
+
// Retrieve TF-IDF score for this specific candidate
|
|
123
|
+
const candidateTfIdfScore = this.scoreSingleCandidate(taskDescription, candidate, candidates);
|
|
124
|
+
// Blend: 60% TF-IDF score (normalized 0-1) + 40% cosine similarity
|
|
125
|
+
const normalizedTfIdf = Math.min(candidateTfIdfScore / 30, 1);
|
|
126
|
+
const blendedScore = normalizedTfIdf * 0.6 + similarity * 0.4;
|
|
127
|
+
if (blendedScore > bestBlendedScore) {
|
|
128
|
+
bestBlendedScore = blendedScore;
|
|
129
|
+
bestAgent = candidate.agent;
|
|
130
|
+
bestReasoning = `Semantic blend (TF-IDF: ${(normalizedTfIdf * 100).toFixed(0)}%, cosine: ${(similarity * 100).toFixed(0)}%) → ${candidate.role}`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const confidence = bestBlendedScore > 0.6 ? "high"
|
|
134
|
+
: bestBlendedScore > 0.35 ? "medium"
|
|
135
|
+
: "low";
|
|
136
|
+
logger.debug(`[ROUTING] Semantic routing selected ${bestAgent} (blend score: ${bestBlendedScore.toFixed(3)})`);
|
|
137
|
+
return {
|
|
138
|
+
agent: bestAgent,
|
|
139
|
+
score: Math.round(bestBlendedScore * 100) / 100,
|
|
140
|
+
confidence,
|
|
141
|
+
reasoning: bestReasoning,
|
|
142
|
+
subTasks: this.generateSubTasks(bestAgent, taskDescription),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
// Graceful fallback to TF-IDF if embedding fails
|
|
147
|
+
logger.warn(`[ROUTING] Embedding-based routing failed, falling back to TF-IDF: ${err.message}`);
|
|
148
|
+
return tfidfResult;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Scores a single candidate against a task using TF-IDF.
|
|
153
|
+
* Extracted from resolveWithDetails to support semantic blend calculation.
|
|
154
|
+
*/
|
|
155
|
+
static scoreSingleCandidate(taskDescription, candidate, allCandidates) {
|
|
156
|
+
const textLower = taskDescription.toLowerCase();
|
|
157
|
+
const tokens = this.tokenize(textLower);
|
|
158
|
+
let score = 0;
|
|
159
|
+
for (const [specialty, weight] of Object.entries(candidate.specialties)) {
|
|
160
|
+
const specTokens = this.tokenize(specialty.toLowerCase());
|
|
161
|
+
for (const token of tokens) {
|
|
162
|
+
if (specTokens.some(st => token.includes(st) || st.includes(token))) {
|
|
163
|
+
score += weight * this.calculateTfIdf(token, tokens, allCandidates);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
score += (candidate.capability - 5) * 0.5;
|
|
168
|
+
return score;
|
|
169
|
+
}
|
|
88
170
|
/**
|
|
89
|
-
*
|
|
171
|
+
* Cosine similarity between two numeric vectors.
|
|
172
|
+
* Returns a value in [0, 1] where 1 = identical direction.
|
|
90
173
|
*/
|
|
174
|
+
static cosineSimilarity(a, b) {
|
|
175
|
+
const len = Math.min(a.length, b.length);
|
|
176
|
+
let dot = 0;
|
|
177
|
+
let normA = 0;
|
|
178
|
+
let normB = 0;
|
|
179
|
+
for (let i = 0; i < len; i++) {
|
|
180
|
+
dot += a[i] * b[i];
|
|
181
|
+
normA += a[i] * a[i];
|
|
182
|
+
normB += b[i] * b[i];
|
|
183
|
+
}
|
|
184
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
185
|
+
return denom === 0 ? 0 : Math.max(0, dot / denom);
|
|
186
|
+
}
|
|
91
187
|
static planTask(taskDescription) {
|
|
92
188
|
const result = this.resolveWithDetails(taskDescription);
|
|
93
189
|
const plan = [
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import os from "os";
|
|
4
|
-
import {
|
|
5
|
-
import { writeJsonFile, writeTextFile } from "../../shared/fs.js";
|
|
4
|
+
import { runCommandQuiet, writeJsonFile, writeTextFile } from "../../shared/fs.js";
|
|
6
5
|
import { ALL_AGENTS, toAntigravityJson } from "../agents/definitions.js";
|
|
7
6
|
import { CORE_SKILLS } from "../skills/definitions.js";
|
|
8
7
|
import { getPackageRoot } from "../../cli/utils/pkg.js";
|
|
@@ -90,7 +89,7 @@ ${ag.instructions.rules.map(r => `- ${r}`).join("\n")}
|
|
|
90
89
|
writeJsonFile(path.join(globalPluginDir, "hooks.json"), {});
|
|
91
90
|
logger.info(`Antigravity Plugin registered → ${globalPluginDir}/`);
|
|
92
91
|
try {
|
|
93
|
-
|
|
92
|
+
runCommandQuiet("agy", ["plugin", "install", globalPluginDir], process.cwd());
|
|
94
93
|
logger.info("Antigravity Plugin installed in CLI.");
|
|
95
94
|
}
|
|
96
95
|
catch {
|
|
@@ -150,7 +150,7 @@ export function containsPIIInObject(obj, depth = 0) {
|
|
|
150
150
|
if (Array.isArray(obj)) {
|
|
151
151
|
return obj.some(item => containsPIIInObject(item, depth + 1));
|
|
152
152
|
}
|
|
153
|
-
for (const [
|
|
153
|
+
for (const [, value] of Object.entries(obj)) {
|
|
154
154
|
if (typeof value === "string") {
|
|
155
155
|
if (containsPII(value))
|
|
156
156
|
return true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Database from "better-sqlite3";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import crypto from "crypto";
|
|
4
5
|
import { getFrameworkDir } from "../cli/utils/memory.js";
|
|
5
6
|
import { logger } from "./logger.js";
|
|
6
7
|
import { maskText } from "./pii.js";
|
|
@@ -18,7 +19,7 @@ export class AtabeyStorage {
|
|
|
18
19
|
fs.mkdirSync(frameworkDir, { recursive: true });
|
|
19
20
|
}
|
|
20
21
|
const dbPath = path.join(frameworkDir, "atabey.db");
|
|
21
|
-
this.db = new Database(dbPath);
|
|
22
|
+
this.db = new Database(dbPath, { timeout: 5000 });
|
|
22
23
|
this.db.pragma("journal_mode = WAL");
|
|
23
24
|
this.initializeSchema();
|
|
24
25
|
}
|
|
@@ -74,9 +75,25 @@ export class AtabeyStorage {
|
|
|
74
75
|
trace_id TEXT,
|
|
75
76
|
status TEXT,
|
|
76
77
|
summary TEXT,
|
|
77
|
-
findings TEXT
|
|
78
|
+
findings TEXT,
|
|
79
|
+
prev_hash TEXT,
|
|
80
|
+
hash TEXT
|
|
78
81
|
)
|
|
79
82
|
`);
|
|
83
|
+
// self-healing schema check/migration for older logs table
|
|
84
|
+
try {
|
|
85
|
+
const tableInfo = db.prepare("PRAGMA table_info(logs)").all();
|
|
86
|
+
const columnNames = tableInfo.map(c => c.name);
|
|
87
|
+
if (!columnNames.includes("prev_hash")) {
|
|
88
|
+
db.exec("ALTER TABLE logs ADD COLUMN prev_hash TEXT");
|
|
89
|
+
}
|
|
90
|
+
if (!columnNames.includes("hash")) {
|
|
91
|
+
db.exec("ALTER TABLE logs ADD COLUMN hash TEXT");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
logger.error(`Failed to migrate logs table schema: ${err.message}`);
|
|
96
|
+
}
|
|
80
97
|
// Metadata (State) Table
|
|
81
98
|
db.exec(`
|
|
82
99
|
CREATE TABLE IF NOT EXISTS metadata (
|
|
@@ -98,6 +115,61 @@ export class AtabeyStorage {
|
|
|
98
115
|
)
|
|
99
116
|
`);
|
|
100
117
|
}
|
|
118
|
+
static saveLog(log) {
|
|
119
|
+
const db = this.getDB();
|
|
120
|
+
const timestamp = new Date().toISOString();
|
|
121
|
+
const agent = log.agent.replace("@", "");
|
|
122
|
+
const maskedSummary = maskText(log.summary);
|
|
123
|
+
const maskedFindings = log.findings ? maskText(log.findings) : null;
|
|
124
|
+
// Get last log's hash
|
|
125
|
+
let prevHash = "GENESIS";
|
|
126
|
+
try {
|
|
127
|
+
const lastRow = db.prepare("SELECT hash FROM logs ORDER BY id DESC LIMIT 1").get();
|
|
128
|
+
if (lastRow && lastRow.hash) {
|
|
129
|
+
prevHash = lastRow.hash;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch { /* use default */ }
|
|
133
|
+
// Compute hash
|
|
134
|
+
const dataToHash = `${prevHash}|${agent}|${log.action}|${log.trace_id || ""}|${log.status}|${maskedSummary}|${timestamp}`;
|
|
135
|
+
const hash = crypto.createHash("sha256").update(dataToHash).digest("hex");
|
|
136
|
+
const result = db.prepare(`
|
|
137
|
+
INSERT INTO logs (timestamp, agent, action, trace_id, status, summary, findings, prev_hash, hash)
|
|
138
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
139
|
+
`).run(timestamp, agent, log.action, log.trace_id || null, log.status, maskedSummary, maskedFindings, prevHash, hash);
|
|
140
|
+
return result.lastInsertRowid;
|
|
141
|
+
}
|
|
142
|
+
static verifyLogIntegrity() {
|
|
143
|
+
const db = this.getDB();
|
|
144
|
+
try {
|
|
145
|
+
const rows = db.prepare("SELECT * FROM logs ORDER BY id ASC").all();
|
|
146
|
+
let expectedPrevHash = "GENESIS";
|
|
147
|
+
for (const row of rows) {
|
|
148
|
+
if (row.prev_hash !== expectedPrevHash) {
|
|
149
|
+
return {
|
|
150
|
+
valid: false,
|
|
151
|
+
failedLogId: row.id,
|
|
152
|
+
reason: `Hash mismatch at row ${row.id}: expected prev_hash to be '${expectedPrevHash}', but got '${row.prev_hash}'`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Recalculate hash
|
|
156
|
+
const dataToHash = `${row.prev_hash}|${row.agent}|${row.action}|${row.trace_id || ""}|${row.status}|${row.summary}|${row.timestamp}`;
|
|
157
|
+
const calculatedHash = crypto.createHash("sha256").update(dataToHash).digest("hex");
|
|
158
|
+
if (row.hash !== calculatedHash) {
|
|
159
|
+
return {
|
|
160
|
+
valid: false,
|
|
161
|
+
failedLogId: row.id,
|
|
162
|
+
reason: `Hash corruption at row ${row.id}: calculated hash '${calculatedHash}' does not match database hash '${row.hash}'`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
expectedPrevHash = row.hash || "";
|
|
166
|
+
}
|
|
167
|
+
return { valid: true };
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
return { valid: false, reason: `Verification error: ${err.message}` };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
101
173
|
// --- Cost Operations ---
|
|
102
174
|
static saveCost(cost) {
|
|
103
175
|
const db = this.getDB();
|
|
@@ -226,7 +298,9 @@ export class AtabeyStorage {
|
|
|
226
298
|
status: r.status,
|
|
227
299
|
// [KVKK/GDPR] Mask PII in log summaries before returning
|
|
228
300
|
summary: maskText(r.summary),
|
|
229
|
-
findings: r.findings ? maskText(r.findings) : undefined
|
|
301
|
+
findings: r.findings ? maskText(r.findings) : undefined,
|
|
302
|
+
prev_hash: r.prev_hash || undefined,
|
|
303
|
+
hash: r.hash || undefined
|
|
230
304
|
}));
|
|
231
305
|
}
|
|
232
306
|
// --- Metadata Operations ---
|