atabey 0.0.14 → 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/PRIVACY.md +8 -14
- package/README.md +163 -14
- package/bin/cli.js +72 -5
- package/dist/framework-mcp/src/index.js +76 -3
- package/dist/framework-mcp/src/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/approve_operation.d.ts +8 -8
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js +11 -11
- 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/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/shell/run_command.js +138 -92
- package/dist/framework-mcp/src/tools/shell/run_command.js.map +1 -1
- package/dist/framework-mcp/src/utils/auth.js +4 -4
- package/dist/framework-mcp/src/utils/auth.js.map +1 -1
- package/dist/framework-mcp/src/utils/discipline.js +1 -1
- package/dist/framework-mcp/src/utils/discipline.js.map +1 -1
- package/dist/framework-mcp/src/utils/human-in-loop.js +1 -1
- 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/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/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/src/cli/commands/init.js +14 -7
- 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/script.js +8 -1
- package/dist/src/cli/commands/script.js.map +1 -1
- package/dist/src/modules/engines/agent-executor.js +11 -1
- 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 +12 -2
- package/dist/src/modules/engines/evaluation-engine.js +76 -9
- package/dist/src/modules/engines/evaluation-engine.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 +43 -0
- package/dist/src/shared/storage.js +1 -1
- package/dist/src/shared/storage.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/framework-mcp/dist/dashboard/assets/{index-B2mYld0c.js → index-B_rK57vi.js} +17 -17
- package/framework-mcp/dist/dashboard/index.html +1 -1
- package/framework-mcp/dist/framework-mcp/src/index.js +76 -3
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/approve_operation.js +11 -11
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/ask_human.js +8 -8
- package/framework-mcp/dist/framework-mcp/src/tools/observability/check_ports.js +21 -7
- package/framework-mcp/dist/framework-mcp/src/tools/shell/run_command.js +138 -92
- package/framework-mcp/dist/framework-mcp/src/utils/auth.js +4 -4
- package/framework-mcp/dist/framework-mcp/src/utils/discipline.js +1 -1
- package/framework-mcp/dist/framework-mcp/src/utils/human-in-loop.js +1 -1
- package/framework-mcp/dist/framework-mcp/src/utils/loop-detector.js +3 -3
- 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/modules/engines/evaluation-engine.js +169 -0
- package/framework-mcp/dist/src/shared/pii.js +1 -1
- package/framework-mcp/dist/src/shared/storage.js +1 -1
- package/framework-mcp/package.json +1 -1
- package/mcp.json +3 -2
- package/package.json +3 -4
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Features:
|
|
11
11
|
* - Auto-agent detection from tool call context
|
|
12
|
-
* - Silent system prompt injection
|
|
12
|
+
* - Silent system prompt injection with specialty memory
|
|
13
13
|
* - Multi-agent chain support
|
|
14
14
|
* - Stealth notifications (stderr/dashboard only, never chat)
|
|
15
15
|
*/
|
|
16
|
+
import { EvaluationEngine } from "../../../src/modules/engines/evaluation-engine.js";
|
|
16
17
|
import { RoutingEngine } from "../../../src/modules/engines/routing-engine.js";
|
|
17
18
|
// ─── Configuration ────────────────────────────────────────────────
|
|
18
19
|
const CONFIG = {
|
|
@@ -22,6 +23,8 @@ const CONFIG = {
|
|
|
22
23
|
STEALTH_NOTIFY: process.env.MCP_SILENT_STEALTH !== "false",
|
|
23
24
|
/** Agent override via env (for testing/debugging) */
|
|
24
25
|
FORCE_AGENT: process.env.MCP_FORCE_AGENT || null,
|
|
26
|
+
/** Enable specialty memory injection into context */
|
|
27
|
+
INJECT_SPECIALTY_MEMORY: process.env.MCP_INJECT_SPECIALTY_MEMORY !== "false",
|
|
25
28
|
};
|
|
26
29
|
let agentRuleCache = null;
|
|
27
30
|
async function loadAgentRules() {
|
|
@@ -40,7 +43,6 @@ async function loadAgentRules() {
|
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
catch {
|
|
43
|
-
// Fallback: use discipline rules file
|
|
44
46
|
agentRuleCache = new Map();
|
|
45
47
|
}
|
|
46
48
|
return agentRuleCache;
|
|
@@ -52,9 +54,8 @@ async function loadAgentRules() {
|
|
|
52
54
|
export function detectAgent(toolName, args) {
|
|
53
55
|
if (CONFIG.FORCE_AGENT)
|
|
54
56
|
return CONFIG.FORCE_AGENT;
|
|
55
|
-
// Build context from args for routing
|
|
56
57
|
const contextParts = [toolName];
|
|
57
|
-
for (const [
|
|
58
|
+
for (const [, value] of Object.entries(args)) {
|
|
58
59
|
if (typeof value === "string") {
|
|
59
60
|
contextParts.push(value);
|
|
60
61
|
}
|
|
@@ -63,7 +64,6 @@ export function detectAgent(toolName, args) {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
const context = contextParts.join(" ").substring(0, 500);
|
|
66
|
-
// Use RoutingEngine to detect the best agent
|
|
67
67
|
return RoutingEngine.resolveAgent(context);
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
@@ -88,9 +88,32 @@ export async function getAgentPrompt(agentName) {
|
|
|
88
88
|
"> They supplement (not replace) your existing instructions.",
|
|
89
89
|
].join("\n");
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Load specialty memory for an agent and format it for injection.
|
|
93
|
+
*/
|
|
94
|
+
function getSpecialtyMemoryContext(agentName) {
|
|
95
|
+
if (!CONFIG.INJECT_SPECIALTY_MEMORY)
|
|
96
|
+
return null;
|
|
97
|
+
try {
|
|
98
|
+
const conventions = EvaluationEngine.readLearnedConventions(agentName);
|
|
99
|
+
if (!conventions)
|
|
100
|
+
return null;
|
|
101
|
+
return [
|
|
102
|
+
"",
|
|
103
|
+
"### Previously Learned Conventions",
|
|
104
|
+
`The following lessons were learned from past tasks for @${agentName}:`,
|
|
105
|
+
"",
|
|
106
|
+
conventions,
|
|
107
|
+
"",
|
|
108
|
+
].join("\n");
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
91
114
|
/**
|
|
92
115
|
* Build a silent context injection payload.
|
|
93
|
-
*
|
|
116
|
+
* Injects agent rules + specialty memory into tool responses.
|
|
94
117
|
*/
|
|
95
118
|
export async function buildSilentContext(detectedAgent, toolName, responseText) {
|
|
96
119
|
if (!CONFIG.ENABLED)
|
|
@@ -98,14 +121,17 @@ export async function buildSilentContext(detectedAgent, toolName, responseText)
|
|
|
98
121
|
const agentPrompt = await getAgentPrompt(detectedAgent);
|
|
99
122
|
if (!agentPrompt)
|
|
100
123
|
return responseText;
|
|
101
|
-
// Only inject if the response is substantial enough to warrant governance
|
|
102
124
|
if (responseText.length < 50)
|
|
103
125
|
return responseText;
|
|
104
|
-
// Inject
|
|
126
|
+
// Inject specialty memory if available
|
|
127
|
+
const specialtyContext = getSpecialtyMemoryContext(detectedAgent);
|
|
128
|
+
const fullPrompt = specialtyContext
|
|
129
|
+
? `${agentPrompt}\n${specialtyContext}`
|
|
130
|
+
: agentPrompt;
|
|
105
131
|
const injection = [
|
|
106
132
|
"",
|
|
107
133
|
`<!-- ATABEY_GOVERNANCE agent="${detectedAgent}" tool="${toolName}" -->`,
|
|
108
|
-
`${
|
|
134
|
+
`${fullPrompt}`,
|
|
109
135
|
"<!-- /ATABEY_GOVERNANCE -->",
|
|
110
136
|
"",
|
|
111
137
|
].join("\n");
|
|
@@ -0,0 +1,169 @@
|
|
|
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
|
+
/**
|
|
106
|
+
* Extract a meaningful success lesson from a completed task.
|
|
107
|
+
*/
|
|
108
|
+
static extractSuccessLesson(agentName, taskDescription) {
|
|
109
|
+
const patterns = [
|
|
110
|
+
{ keywords: ["api", "endpoint", "route", "rest"], lesson: "REST API pattern used successfully for this task." },
|
|
111
|
+
{ keywords: ["auth", "login", "jwt", "token"], lesson: "Authentication/authorization pattern used successfully." },
|
|
112
|
+
{ keywords: ["test", "spec", "vitest"], lesson: "Test-driven approach used successfully." },
|
|
113
|
+
{ keywords: ["migration", "schema", "database", "sql"], lesson: "Database schema/migration pattern used successfully." },
|
|
114
|
+
{ keywords: ["component", "react", "ui", "page"], lesson: "UI component pattern used successfully for this task." },
|
|
115
|
+
{ keywords: ["middleware", "guard", "interceptor"], lesson: "Middleware/guard pattern used successfully." },
|
|
116
|
+
{ keywords: ["service", "repository", "inject"], lesson: "Service/repository pattern used successfully." },
|
|
117
|
+
{ keywords: ["error", "exception", "catch", "try"], lesson: "Error handling pattern used successfully." },
|
|
118
|
+
{ keywords: ["config", "env", "environment"], lesson: "Configuration/environment pattern used successfully." },
|
|
119
|
+
];
|
|
120
|
+
const lower = taskDescription.toLowerCase();
|
|
121
|
+
for (const { keywords, lesson } of patterns) {
|
|
122
|
+
if (keywords.some(k => lower.includes(k))) {
|
|
123
|
+
return lesson;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Generic success lesson if no specific pattern matched
|
|
127
|
+
return "Task completed with score >= 80. Pattern/approach used was effective.";
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Reads learned conventions from an agent's specialty memory.
|
|
131
|
+
* Used by silent-router to inject context into AI calls.
|
|
132
|
+
*/
|
|
133
|
+
static readLearnedConventions(agentName) {
|
|
134
|
+
const cleanName = agentName.replace("@", "");
|
|
135
|
+
const fwDir = getFrameworkDir();
|
|
136
|
+
const filePath = path.join(fwDir, "memory", "specialties", `${cleanName}.md`);
|
|
137
|
+
try {
|
|
138
|
+
if (fs.existsSync(filePath)) {
|
|
139
|
+
const content = fs.readFileSync(filePath, "utf8").trim();
|
|
140
|
+
if (content)
|
|
141
|
+
return content;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// File might not exist yet
|
|
146
|
+
}
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Appends learned lessons to the agent's specialty memory.
|
|
151
|
+
*/
|
|
152
|
+
static updateSpecialtyMemory(agentName, lesson) {
|
|
153
|
+
const cleanName = agentName.replace("@", "");
|
|
154
|
+
const fwDir = getFrameworkDir();
|
|
155
|
+
const specialtiesDir = path.join(fwDir, "memory", "specialties");
|
|
156
|
+
ensureDir(specialtiesDir);
|
|
157
|
+
const filePath = path.join(specialtiesDir, `${cleanName}.md`);
|
|
158
|
+
const timestamp = new Date().toISOString().split("T")[0];
|
|
159
|
+
const entry = `- **[${timestamp}]**: ${lesson}\n`;
|
|
160
|
+
if (!fs.existsSync(filePath)) {
|
|
161
|
+
writeTextFile(filePath, `# Learned Conventions for @${cleanName}\n\n`);
|
|
162
|
+
}
|
|
163
|
+
// Avoid duplicate lessons on the same day
|
|
164
|
+
const currentContent = fs.readFileSync(filePath, "utf8");
|
|
165
|
+
if (!currentContent.includes(lesson)) {
|
|
166
|
+
appendFile(filePath, entry);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -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;
|
|
@@ -18,7 +18,7 @@ export class AtabeyStorage {
|
|
|
18
18
|
fs.mkdirSync(frameworkDir, { recursive: true });
|
|
19
19
|
}
|
|
20
20
|
const dbPath = path.join(frameworkDir, "atabey.db");
|
|
21
|
-
this.db = new Database(dbPath);
|
|
21
|
+
this.db = new Database(dbPath, { timeout: 5000 });
|
|
22
22
|
this.db.pragma("journal_mode = WAL");
|
|
23
23
|
this.initializeSchema();
|
|
24
24
|
}
|
package/mcp.json
CHANGED
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
"command": "atabey",
|
|
5
5
|
"args": ["mcp", "start"],
|
|
6
6
|
"env": {
|
|
7
|
+
"MCP_TRANSPORT": "stdio",
|
|
7
8
|
"ATABEY_PROJECT_ROOT": "/Users/ybekar/Desktop/Projeler/atabey"
|
|
8
9
|
},
|
|
9
|
-
"description": "Stdio mode:
|
|
10
|
+
"description": "Stdio mode: each developer connects from their own IDE."
|
|
10
11
|
},
|
|
11
12
|
"atabey-sse": {
|
|
12
13
|
"url": "http://localhost:5858/mcp/sse",
|
|
13
14
|
"env": {
|
|
14
15
|
"ATABEY_PROJECT_ROOT": "/Users/ybekar/Desktop/Projeler/atabey"
|
|
15
16
|
},
|
|
16
|
-
"description": "Unified mode:
|
|
17
|
+
"description": "Unified mode: MCP + Dashboard on single port 5858. Dashboard: http://localhost:5858"
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atabey",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "MCP-Powered AI Governance & Autonomous Orchestration Framework. Plugs into Claude Code, Gemini CLI, and Cursor as an MCP server.",
|
|
5
5
|
"author": "Yusuf BEKAR",
|
|
6
6
|
"license": "MIT",
|
|
@@ -89,11 +89,10 @@
|
|
|
89
89
|
"globals": "^17.6.0",
|
|
90
90
|
"tsx": "^4.22.3",
|
|
91
91
|
"typescript-eslint": "^8.0.0",
|
|
92
|
-
"vitest": "^3.0.0"
|
|
93
|
-
"atabey": "^0.0.14"
|
|
92
|
+
"vitest": "^3.0.0"
|
|
94
93
|
},
|
|
95
94
|
"atabey": {
|
|
96
|
-
"version": "0.0.
|
|
95
|
+
"version": "0.0.15",
|
|
97
96
|
"initializedAt": "2026-06-20T15:11:43.308Z"
|
|
98
97
|
}
|
|
99
98
|
}
|