agent-enderun 1.11.4 β 1.11.5
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/README.md +84 -277
- package/dist/framework-mcp/src/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/control_plane/locking.js +17 -3
- package/dist/framework-mcp/src/tools/control_plane/locking.js.map +1 -1
- package/dist/framework-mcp/src/tools/memory/get_insights.js +4 -4
- package/dist/framework-mcp/src/tools/memory/get_insights.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/send_message.js +10 -5
- package/dist/framework-mcp/src/tools/messaging/send_message.js.map +1 -1
- package/dist/framework-mcp/src/tools/search/grep_search.js +2 -3
- package/dist/framework-mcp/src/tools/search/grep_search.js.map +1 -1
- package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +7 -7
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
- package/dist/src/cli/commands/check.js +1 -1
- package/dist/src/cli/commands/check.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-core.d.ts +1 -0
- package/dist/src/cli/commands/init/scaffold-core.js +14 -1
- package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts +1 -0
- package/dist/src/cli/commands/init.js +12 -4
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/orchestrate.js +21 -0
- package/dist/src/cli/commands/orchestrate.js.map +1 -1
- package/dist/src/cli/commands/plan.js +1 -2
- package/dist/src/cli/commands/plan.js.map +1 -1
- package/dist/src/cli/commands/status.js.map +1 -1
- package/dist/src/cli/index.js +4 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/shims.js +39 -26
- package/dist/src/cli/shims.js.map +1 -1
- package/dist/src/modules/adapters/definitions.js +16 -0
- package/dist/src/modules/adapters/definitions.js.map +1 -1
- package/dist/src/modules/adapters/types.d.ts +1 -1
- package/dist/src/modules/adapters/types.js +1 -1
- package/dist/src/modules/adapters/types.js.map +1 -1
- package/dist/src/modules/agents/definitions.js +1 -1
- package/dist/src/shared/constants.d.ts +5 -2
- package/dist/src/shared/constants.js +5 -0
- package/dist/src/shared/constants.js.map +1 -1
- package/dist/tests/approve.test.js +3 -1
- package/dist/tests/approve.test.js.map +1 -1
- package/dist/tests/integration/agent_flow.test.js +2 -0
- package/dist/tests/integration/agent_flow.test.js.map +1 -1
- package/dist/tests/integration/hermes_locking.test.js +2 -0
- package/dist/tests/integration/hermes_locking.test.js.map +1 -1
- package/dist/tests/memory-utils.test.js +2 -0
- package/dist/tests/memory-utils.test.js.map +1 -1
- package/dist/tests/orchestrate.test.js +2 -0
- package/dist/tests/orchestrate.test.js.map +1 -1
- package/dist/tests/status.test.js +2 -0
- package/dist/tests/status.test.js.map +1 -1
- package/framework-mcp/dist/tools/control_plane/locking.js +17 -3
- package/framework-mcp/dist/tools/memory/get_insights.js +4 -4
- package/framework-mcp/dist/tools/messaging/send_message.js +10 -5
- package/framework-mcp/dist/tools/search/grep_search.js +2 -3
- package/framework-mcp/package-lock.json +2 -2
- package/framework-mcp/package.json +1 -1
- package/framework-mcp/src/index.ts +2 -2
- package/framework-mcp/src/tools/control_plane/locking.ts +18 -4
- package/framework-mcp/src/tools/memory/get_insights.ts +4 -4
- package/framework-mcp/src/tools/messaging/send_message.ts +11 -5
- package/framework-mcp/src/tools/search/grep_search.ts +2 -3
- package/framework-mcp/src/utils/compliance.ts +16 -16
- package/framework-mcp/tests/tools/messaging/send_message.test.ts +7 -7
- package/package.json +4 -3
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/init/scaffold-core.ts +18 -2
- package/src/cli/commands/init.ts +14 -5
- package/src/cli/commands/orchestrate.ts +24 -0
- package/src/cli/commands/plan.ts +1 -2
- package/src/cli/commands/status.ts +1 -3
- package/src/cli/index.ts +4 -2
- package/src/cli/shims.ts +39 -26
- package/src/modules/adapters/definitions.ts +20 -0
- package/src/modules/adapters/types.ts +1 -1
- package/src/modules/agents/definitions.ts +1 -1
- package/src/shared/constants.ts +5 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-enderun/mcp",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@agent-enderun/mcp",
|
|
9
|
-
"version": "1.11.
|
|
9
|
+
"version": "1.11.5",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
12
12
|
"zod": "^3.24.2"
|
|
@@ -59,7 +59,7 @@ function validateArgs(toolName: string, args: Record<string, unknown>): string |
|
|
|
59
59
|
|
|
60
60
|
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
61
61
|
// 2026 Stateless Spec: Log client info from metadata if available
|
|
62
|
-
const meta = (request as
|
|
62
|
+
const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
|
|
63
63
|
if (meta) {
|
|
64
64
|
process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
|
|
65
65
|
}
|
|
@@ -69,7 +69,7 @@ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
|
69
69
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
70
70
|
const req = request as { params: { name: string, arguments?: Record<string, unknown> } };
|
|
71
71
|
const { name, arguments: args } = req.params;
|
|
72
|
-
const meta = (request as
|
|
72
|
+
const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
|
|
73
73
|
|
|
74
74
|
// 2026 Stateless Spec: Prioritize metadata-driven context
|
|
75
75
|
if (meta) {
|
|
@@ -15,6 +15,7 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
|
|
|
15
15
|
try {
|
|
16
16
|
if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
|
|
17
17
|
|
|
18
|
+
// Check for stale lock first
|
|
18
19
|
if (fs.existsSync(lockPath)) {
|
|
19
20
|
const stat = fs.statSync(lockPath);
|
|
20
21
|
const now = new Date().getTime();
|
|
@@ -26,15 +27,28 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
|
|
|
26
27
|
content: [{ type: "text", text: `Resource '${resource}' is currently locked by another agent.` }]
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
|
-
// Lock expired, safe to override
|
|
30
|
-
|
|
30
|
+
// Lock expired, safe to override by deleting first
|
|
31
|
+
try {
|
|
32
|
+
fs.unlinkSync(lockPath);
|
|
33
|
+
} catch (unlinkErr) {
|
|
34
|
+
// If another agent unlinked it first, ignore
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
// Use 'wx' flag for atomic file creation
|
|
39
|
+
const lockData = JSON.stringify({ agent, timestamp: new Date().toISOString() });
|
|
40
|
+
fs.writeFileSync(lockPath, lockData, { flag: "wx" });
|
|
41
|
+
|
|
34
42
|
return {
|
|
35
43
|
content: [{ type: "text", text: `β
Lock acquired for resource '${resource}' by ${agent}.` }]
|
|
36
44
|
};
|
|
37
|
-
} catch (e) {
|
|
45
|
+
} catch (e: any) {
|
|
46
|
+
if (e.code === "EEXIST") {
|
|
47
|
+
return {
|
|
48
|
+
isError: true,
|
|
49
|
+
content: [{ type: "text", text: `Resource '${resource}' was just acquired by another agent.` }]
|
|
50
|
+
};
|
|
51
|
+
}
|
|
38
52
|
return {
|
|
39
53
|
isError: true,
|
|
40
54
|
content: [{ type: "text", text: `Failed to acquire lock: ${String(e)}` }]
|
|
@@ -19,14 +19,14 @@ export function handleGetMemoryInsights(projectRoot: string, _args: ToolArgs): T
|
|
|
19
19
|
const content = fs.readFileSync(memoryPath, "utf8");
|
|
20
20
|
const lines = content.split("\n");
|
|
21
21
|
|
|
22
|
-
const activePhase = lines.find(l => l.includes("
|
|
23
|
-
const activeTrace = lines.find(l => l.includes("
|
|
22
|
+
const activePhase = lines.find(l => l.includes("**Phase:**"))?.split("**Phase:**")[1]?.trim() || "Unknown";
|
|
23
|
+
const activeTrace = lines.find(l => l.includes("**Trace ID:**"))?.split("**Trace ID:**")[1]?.trim() || "None";
|
|
24
24
|
|
|
25
25
|
// Find the last 5 history items (heuristic)
|
|
26
|
-
const historyStartIndex = lines.findIndex(l => l.includes("
|
|
26
|
+
const historyStartIndex = lines.findIndex(l => l.toUpperCase().includes("HISTORY"));
|
|
27
27
|
let recentHistory = "No history found.";
|
|
28
28
|
if (historyStartIndex !== -1) {
|
|
29
|
-
recentHistory = lines.slice(historyStartIndex + 1
|
|
29
|
+
recentHistory = lines.slice(historyStartIndex + 1)
|
|
30
30
|
.filter(l => l.trim().startsWith("-"))
|
|
31
31
|
.slice(-5)
|
|
32
32
|
.join("\n");
|
|
@@ -5,7 +5,7 @@ import { SendAgentMessageArgs, ToolResult } from "../types.js";
|
|
|
5
5
|
import { Metrics } from "../../utils/metrics.js";
|
|
6
6
|
|
|
7
7
|
export async function handleSendAgentMessage(projectRoot: string, args: SendAgentMessageArgs): Promise<ToolResult> {
|
|
8
|
-
const { to, category, content, traceId } = args;
|
|
8
|
+
const { to, category, content, traceId, parentId, requiresApproval } = args;
|
|
9
9
|
const from = args.from || "@mcp";
|
|
10
10
|
|
|
11
11
|
if (!to || !category || !content || !traceId) {
|
|
@@ -20,15 +20,15 @@ export async function handleSendAgentMessage(projectRoot: string, args: SendAgen
|
|
|
20
20
|
const messagePath = path.join(messagesDir, `${agentName}.json`);
|
|
21
21
|
const lockPath = path.join(messagesDir, `${agentName}.lock`);
|
|
22
22
|
|
|
23
|
-
// Hermes Lock Protocol: Retry
|
|
24
|
-
let retries =
|
|
23
|
+
// Hermes Lock Protocol: Retry 20 times with 500ms delay
|
|
24
|
+
let retries = 20;
|
|
25
25
|
let acquired = false;
|
|
26
26
|
while (retries > 0) {
|
|
27
27
|
try {
|
|
28
28
|
if (fs.existsSync(lockPath)) {
|
|
29
29
|
try {
|
|
30
30
|
const stats = fs.statSync(lockPath);
|
|
31
|
-
if (Date.now() - stats.mtimeMs >
|
|
31
|
+
if (Date.now() - stats.mtimeMs > 10000) {
|
|
32
32
|
fs.unlinkSync(lockPath);
|
|
33
33
|
}
|
|
34
34
|
} catch {
|
|
@@ -60,15 +60,21 @@ export async function handleSendAgentMessage(projectRoot: string, args: SendAgen
|
|
|
60
60
|
|
|
61
61
|
try {
|
|
62
62
|
const defaultPriority = (category === "ALERT" || category === "ACTION") ? "HIGH" : "NORMAL";
|
|
63
|
+
const finalRequiresApproval = requiresApproval !== undefined
|
|
64
|
+
? requiresApproval
|
|
65
|
+
: category === "ALERT";
|
|
66
|
+
|
|
63
67
|
const message = {
|
|
64
68
|
timestamp: new Date().toISOString(),
|
|
65
69
|
from,
|
|
66
70
|
to,
|
|
67
71
|
category,
|
|
68
72
|
traceId,
|
|
73
|
+
parentId,
|
|
69
74
|
content,
|
|
70
75
|
priority: args.priority || defaultPriority,
|
|
71
|
-
status: "PENDING"
|
|
76
|
+
status: "PENDING",
|
|
77
|
+
requiresApproval: finalRequiresApproval
|
|
72
78
|
};
|
|
73
79
|
|
|
74
80
|
fs.appendFileSync(messagePath, JSON.stringify(message) + "\n");
|
|
@@ -18,9 +18,8 @@ export function handleGrepSearch(projectRoot: string, args: GrepSearchArgs): Too
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const results: string[] = [];
|
|
21
|
-
let regex: RegExp;
|
|
22
21
|
try {
|
|
23
|
-
|
|
22
|
+
new RegExp(pattern);
|
|
24
23
|
} catch (e) {
|
|
25
24
|
const err = `Invalid regex pattern: ${String(e)}`;
|
|
26
25
|
Metrics.logError(projectRoot, "@mcp", "grep_search", err);
|
|
@@ -53,7 +52,7 @@ export function handleGrepSearch(projectRoot: string, args: GrepSearchArgs): Too
|
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
|
-
} catch
|
|
55
|
+
} catch {
|
|
57
56
|
// Ignore directories that cannot be read
|
|
58
57
|
}
|
|
59
58
|
};
|
|
@@ -37,26 +37,26 @@ export function verifyCorporateCompliance(content: string, filePath: string): vo
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
// 2. No Explicit Any Policy
|
|
41
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
42
|
+
if (ts.isIdentifier(node.typeName) && node.typeName.text === "any") {
|
|
43
|
+
if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
|
|
44
|
+
errors.push(`β Corporate Compliance Breach: 'any' type is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
|
|
45
|
+
}
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
|
|
49
|
+
// 3. Zero UI Library Policy (No @chakra-ui, mui, @shadcn)
|
|
50
|
+
if (ts.isImportDeclaration(node)) {
|
|
51
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
52
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
53
|
+
const forbiddenLibs = ["@chakra-ui", "mui", "@shadcn", "antd", "bootstrap"];
|
|
54
|
+
const lib = forbiddenLibs.find(l => moduleSpecifier.text.includes(l));
|
|
55
|
+
if (lib) {
|
|
56
|
+
errors.push(`β Corporate Compliance Breach: External UI library '${lib}' usage is FORBIDDEN at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Build atomic components manually instead.`);
|
|
57
|
+
}
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
|
-
}
|
|
60
60
|
|
|
61
61
|
// Handle 'any' as a keyword type (e.g., parameter: any)
|
|
62
62
|
if (node.kind === ts.SyntaxKind.AnyKeyword) {
|
|
@@ -72,16 +72,16 @@ describe("Hermes Lock Protocol & Message sending", () => {
|
|
|
72
72
|
|
|
73
73
|
expect(result.isError).toBe(true);
|
|
74
74
|
expect(result.content[0].text).toContain("β Could not send message to @backend: Hermes lock is busy.");
|
|
75
|
-
// Retries takes
|
|
76
|
-
expect(duration).toBeGreaterThanOrEqual(
|
|
77
|
-
});
|
|
75
|
+
// Retries takes 20 attempts with 500ms delay -> duration should be at least ~9000ms
|
|
76
|
+
expect(duration).toBeGreaterThanOrEqual(9000);
|
|
77
|
+
}, 15000);
|
|
78
78
|
|
|
79
|
-
it("should bypass and acquire lock if existing lock is stale (older than
|
|
79
|
+
it("should bypass and acquire lock if existing lock is stale (older than 10s)", async () => {
|
|
80
80
|
fs.mkdirSync(MESSAGES_DIR, { recursive: true });
|
|
81
|
-
// Set lock file mtime to
|
|
81
|
+
// Set lock file mtime to 15 seconds ago
|
|
82
82
|
fs.writeFileSync(BACKEND_LOCK_FILE, "Locked by @test at " + new Date().toISOString(), "utf8");
|
|
83
|
-
const
|
|
84
|
-
fs.utimesSync(BACKEND_LOCK_FILE,
|
|
83
|
+
const fifteenSecondsAgo = new Date(Date.now() - 15000);
|
|
84
|
+
fs.utimesSync(BACKEND_LOCK_FILE, fifteenSecondsAgo, fifteenSecondsAgo);
|
|
85
85
|
|
|
86
86
|
const args: ToolArgs = {
|
|
87
87
|
to: "@backend",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-enderun",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.5",
|
|
4
4
|
"description": "The Supreme AI Governance & Autonomous Orchestration Framework for Enterprise Development. Acts as a Corporate AI Management Assistant.",
|
|
5
5
|
"author": "Yusuf BEKAR",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"framework"
|
|
25
25
|
],
|
|
26
26
|
"workspaces": [
|
|
27
|
-
"framework-mcp"
|
|
27
|
+
"framework-mcp",
|
|
28
|
+
"apps/*"
|
|
28
29
|
],
|
|
29
30
|
"bin": {
|
|
30
31
|
"agent-enderun": "bin/cli.js"
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"vitest": "^3.0.0"
|
|
71
72
|
},
|
|
72
73
|
"enderun": {
|
|
73
|
-
"version": "1.11.
|
|
74
|
+
"version": "1.11.5",
|
|
74
75
|
"initializedAt": "2026-06-04T18:51:28.970Z",
|
|
75
76
|
"frameworkDir": ".enderun"
|
|
76
77
|
},
|
|
@@ -45,7 +45,7 @@ export function scaffoldFrameworkConfigs(
|
|
|
45
45
|
adapter: AdapterConfig,
|
|
46
46
|
dryRun: boolean,
|
|
47
47
|
selectedPalette: string,
|
|
48
|
-
options?: { unified?: boolean; adapters?: string[] }
|
|
48
|
+
options?: { unified?: boolean; adapters?: string[]; profile?: string }
|
|
49
49
|
) {
|
|
50
50
|
if (dryRun) return;
|
|
51
51
|
const frameworkDir = path.join(targetDir, fDir);
|
|
@@ -55,6 +55,7 @@ export function scaffoldFrameworkConfigs(
|
|
|
55
55
|
name: FRAMEWORK_NAME,
|
|
56
56
|
version: getPackageVersion(),
|
|
57
57
|
unified: options?.unified || false,
|
|
58
|
+
profile: options?.profile || "full",
|
|
58
59
|
adapters: options?.adapters || [adapter.id],
|
|
59
60
|
theme: {
|
|
60
61
|
palette: selectedPalette,
|
|
@@ -77,10 +78,25 @@ export function scaffoldFrameworkConfigs(
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
let statusContent = "# ποΈ Status\n\n| Agent | State | Active Task | Last Updated | Notes | Extra | Backup |\n|---|---|---|---|---|---|---|\n";
|
|
80
|
-
|
|
81
|
+
|
|
82
|
+
const liteAgents = ["manager", "architect", "frontend"];
|
|
83
|
+
const activeAgents = (options?.profile === "lite")
|
|
84
|
+
? ALL_AGENTS.filter(ag => liteAgents.includes(ag.name))
|
|
85
|
+
: ALL_AGENTS;
|
|
86
|
+
|
|
87
|
+
const initialStatusJson: Record<string, { state: string; task: string; lastUpdated: string }> = {};
|
|
88
|
+
const now = new Date().toISOString();
|
|
89
|
+
|
|
90
|
+
for (const ag of activeAgents) {
|
|
81
91
|
statusContent += "| @" + ag.name + " | READY | Idle | - | - | - | - |\n";
|
|
92
|
+
initialStatusJson[ag.name] = { state: "READY", task: "Idle", lastUpdated: now };
|
|
82
93
|
}
|
|
83
94
|
writeTextFile(path.join(frameworkDir, "STATUS.md"), statusContent);
|
|
95
|
+
|
|
96
|
+
// Populate initial status.json
|
|
97
|
+
const statusJsonPath = path.join(frameworkDir, "memory", "status.json");
|
|
98
|
+
if (!fs.existsSync(path.join(frameworkDir, "memory"))) fs.mkdirSync(path.join(frameworkDir, "memory"), { recursive: true });
|
|
99
|
+
writeJsonFile(statusJsonPath, initialStatusJson, dryRun);
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
export function scaffoldShims(
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -67,7 +67,7 @@ async function runInteractiveInit(): Promise<{ selectedDirs: string[], selectedA
|
|
|
67
67
|
} finally { rl.close(); }
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
export async function initCommand(adapterName: string, options: { unified?: boolean, dryRun?: boolean, yes?: boolean }) {
|
|
70
|
+
export async function initCommand(adapterName: string, options: { unified?: boolean, dryRun?: boolean, yes?: boolean, profile?: string }) {
|
|
71
71
|
const adapterId = (adapterName || "gemini") as AdapterId;
|
|
72
72
|
const adapter = resolveAdapter(adapterId);
|
|
73
73
|
|
|
@@ -78,6 +78,7 @@ export async function initCommand(adapterName: string, options: { unified?: bool
|
|
|
78
78
|
|
|
79
79
|
const projectRoot = process.cwd();
|
|
80
80
|
const isUnified = options.unified || false;
|
|
81
|
+
const executionProfile = (options.profile || "full").toLowerCase();
|
|
81
82
|
|
|
82
83
|
// STRICT ISOLATION STRATEGY
|
|
83
84
|
const coreDir = ".enderun"; // Pure Memory & Knowledge
|
|
@@ -86,7 +87,7 @@ export async function initCommand(adapterName: string, options: { unified?: bool
|
|
|
86
87
|
const dryRun = options.dryRun || false;
|
|
87
88
|
const forceYes = options.yes || false;
|
|
88
89
|
|
|
89
|
-
UI.intent("Enderun Initialization", `Bootstrapping ${FRAMEWORK_NAME} (v${getPackageVersion()}) with ${adapterId} adapter...`);
|
|
90
|
+
UI.intent("Enderun Initialization", `Bootstrapping ${FRAMEWORK_NAME} (v${getPackageVersion()}) with ${adapterId} adapter [Profile: ${executionProfile}]...`);
|
|
90
91
|
|
|
91
92
|
let selectedDirs: string[];
|
|
92
93
|
let selectedAgents: string[];
|
|
@@ -94,9 +95,16 @@ export async function initCommand(adapterName: string, options: { unified?: bool
|
|
|
94
95
|
|
|
95
96
|
if (forceYes) {
|
|
96
97
|
selectedDirs = ["knowledge", "prompts", "memory", "router", "registry", "observability", "rules"];
|
|
97
|
-
|
|
98
|
+
|
|
99
|
+
if (executionProfile === "lite") {
|
|
100
|
+
const liteAgents = ["manager", "architect", "frontend"];
|
|
101
|
+
selectedAgents = ALL_AGENTS.filter(a => liteAgents.includes(a.name)).map(a => a.name);
|
|
102
|
+
} else {
|
|
103
|
+
selectedAgents = ALL_AGENTS.map(a => a.name);
|
|
104
|
+
}
|
|
105
|
+
|
|
98
106
|
selectedPalette = "Modern Blue";
|
|
99
|
-
UI.success(
|
|
107
|
+
UI.success(`Non-interactive mode: Using default configurations for ${executionProfile} profile.`);
|
|
100
108
|
} else {
|
|
101
109
|
const result = await runInteractiveInit();
|
|
102
110
|
selectedDirs = result.selectedDirs.filter(d => d !== "agents" && d !== "skills");
|
|
@@ -117,7 +125,8 @@ export async function initCommand(adapterName: string, options: { unified?: bool
|
|
|
117
125
|
scaffoldConstitution(projectRoot, coreDir, adapterId, dryRun);
|
|
118
126
|
scaffoldFrameworkConfigs(projectRoot, coreDir, adapter, dryRun, selectedPalette, {
|
|
119
127
|
unified: isUnified,
|
|
120
|
-
adapters: isUnified ? [...ADAPTER_IDS] : [adapterId]
|
|
128
|
+
adapters: isUnified ? [...ADAPTER_IDS] : [adapterId],
|
|
129
|
+
profile: executionProfile
|
|
121
130
|
});
|
|
122
131
|
|
|
123
132
|
scaffoldStandards(path.join(projectRoot, coreDir), dryRun);
|
|
@@ -56,6 +56,13 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
|
|
|
56
56
|
while (isLooping) {
|
|
57
57
|
try {
|
|
58
58
|
// Initial load from disk to sync the cache
|
|
59
|
+
const frameworkConfigPath = path.join(frameworkDir, "config.json");
|
|
60
|
+
const frameworkConfig = fs.existsSync(frameworkConfigPath)
|
|
61
|
+
? JSON.parse(fs.readFileSync(frameworkConfigPath, "utf8"))
|
|
62
|
+
: {};
|
|
63
|
+
const executionProfile = frameworkConfig.profile || "full";
|
|
64
|
+
const liteAgents = ["manager", "architect", "frontend"];
|
|
65
|
+
|
|
59
66
|
if (Object.keys(agentStatusCache).length === 0) {
|
|
60
67
|
const diskStatus = readStatus();
|
|
61
68
|
agentStatusCache = diskStatus || {};
|
|
@@ -65,6 +72,9 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
|
|
|
65
72
|
const agentStatuses = readStatus();
|
|
66
73
|
let updatedStatus = false;
|
|
67
74
|
for (const [agentName, info] of Object.entries(agentStatuses)) {
|
|
75
|
+
// Skip agents not in lite profile
|
|
76
|
+
if (executionProfile === "lite" && !liteAgents.includes(agentName)) continue;
|
|
77
|
+
|
|
68
78
|
const { state, lastUpdated, task } = info as { state: string; task: string; lastUpdated?: string };
|
|
69
79
|
if (state === "EXECUTING" && lastUpdated) {
|
|
70
80
|
const lastUpdatedTime = Date.parse(lastUpdated);
|
|
@@ -95,6 +105,13 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
|
|
|
95
105
|
const fileMessagesMap: Record<string, HermesMessage[]> = {};
|
|
96
106
|
|
|
97
107
|
for (const file of messageFiles) {
|
|
108
|
+
const agentName = file.replace(".json", "");
|
|
109
|
+
// Skip agents not in lite profile
|
|
110
|
+
if (executionProfile === "lite" && !liteAgents.includes(agentName)) {
|
|
111
|
+
// Optional: Could move these messages to a "held" or "rejected" state
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
98
115
|
const filePath = path.join(messagesDir, file);
|
|
99
116
|
try {
|
|
100
117
|
const content = fs.readFileSync(filePath, "utf8").trim();
|
|
@@ -107,6 +124,12 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
|
|
|
107
124
|
const parsed = JSON.parse(line);
|
|
108
125
|
const msg = HermesMessageSchema.parse(parsed);
|
|
109
126
|
|
|
127
|
+
// Profile Validation: Ensure recipient is active
|
|
128
|
+
if (executionProfile === "lite" && !liteAgents.includes(msg.to.replace("@", ""))) {
|
|
129
|
+
UI.error(`β οΈ Message addressed to inactive agent @${msg.to} in LITE profile. Skipping.`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
110
133
|
// Contract Validation for structured task messages
|
|
111
134
|
// ACTION & DELEGATION must be valid TaskRequest payloads
|
|
112
135
|
// SUBTASK must have a parentId and valid task payload
|
|
@@ -301,6 +324,7 @@ export async function sendMessage(args: {
|
|
|
301
324
|
const stats = fs.statSync(lockPath);
|
|
302
325
|
// Increased stale lock timeout from 5s to 10s for heavy ops
|
|
303
326
|
if (Date.now() - stats.mtimeMs > 10000) {
|
|
327
|
+
UI.warning(`β»οΈ Stale Hermes lock detected for @${agentName}. Clearing lock for @${args.from}.`);
|
|
304
328
|
fs.unlinkSync(lockPath);
|
|
305
329
|
}
|
|
306
330
|
} catch (err) {
|
package/src/cli/commands/plan.ts
CHANGED
|
@@ -55,9 +55,8 @@ export async function planCommand() {
|
|
|
55
55
|
|
|
56
56
|
// Extract headings as task descriptions
|
|
57
57
|
const headingRegex = /^#{2,4}\s+(.+)$/gm;
|
|
58
|
-
let headingMatch;
|
|
59
58
|
let headingCount = 0;
|
|
60
|
-
while (
|
|
59
|
+
while (headingRegex.exec(content) !== null) {
|
|
61
60
|
headingCount++;
|
|
62
61
|
totalTasks++;
|
|
63
62
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { listTasks, readState, readStatus, getFrameworkDir } from "../utils/memory.js";
|
|
2
|
+
import { listTasks, readState, readStatus } from "../utils/memory.js";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* Print the current framework status.
|
package/src/cli/index.ts
CHANGED
|
@@ -64,8 +64,10 @@ async function main() {
|
|
|
64
64
|
const dryRun = args.includes("--dry-run");
|
|
65
65
|
const isUnified = args.includes("--unified");
|
|
66
66
|
const forceYes = args.includes("--yes") || args.includes("-y");
|
|
67
|
+
const profileArg = args.find(a => a.startsWith("--profile="));
|
|
68
|
+
const profile = profileArg ? profileArg.split("=")[1] : "full";
|
|
67
69
|
const adapter = args.find(a => !a.startsWith("-") && a !== "init") || "gemini";
|
|
68
|
-
await initCommand(adapter, { dryRun, unified: isUnified, yes: forceYes });
|
|
70
|
+
await initCommand(adapter, { dryRun, unified: isUnified, yes: forceYes, profile });
|
|
69
71
|
break;
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -273,7 +275,7 @@ Usage:
|
|
|
273
275
|
Commands:
|
|
274
276
|
@<agent> <task> Delegate a task to a specialist agent (e.g. @backend, @frontend)
|
|
275
277
|
init [adapter] Initialize Agent Enderun framework.
|
|
276
|
-
Options: --
|
|
278
|
+
Options: --unified, --yes, --profile=[full|lite]
|
|
277
279
|
check Perform an enterprise-grade system health check
|
|
278
280
|
status Show active phase, trace ID, and agent statuses
|
|
279
281
|
trace:new <desc> Start a new task chain with a unique Trace ID
|
package/src/cli/shims.ts
CHANGED
|
@@ -1,53 +1,66 @@
|
|
|
1
1
|
export const SHIM_TEMPLATES: Record<string, string> = {
|
|
2
|
-
gemini: `# ποΈ Agent Enderun β GEMINI Strategy
|
|
2
|
+
gemini: `# ποΈ Agent Enderun β GEMINI Strategy (Command Intelligence)
|
|
3
3
|
|
|
4
|
-
You are the **Gemini Commander**
|
|
4
|
+
You are the **Gemini Commander**. You represent the project's **Strategic Decision Center**. Your intelligence is derived from project history, architectural memory, and governance compliance.
|
|
5
5
|
|
|
6
6
|
## ποΈ Directives
|
|
7
|
-
- **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
7
|
+
- **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first. You are the final arbiter of these rules.
|
|
8
|
+
- **Strategic Memory Sync:** Always read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` and \`PROJECT_MEMORY.md\` at the start.
|
|
9
|
+
- **Orchestration Audit:** Before delegating, verify that the task matches the current Phase and Trace ID context.
|
|
10
|
+
- **Enterprise Reasoning:** Focus on long-term maintainability, security, and scalability in every strategic decision.
|
|
10
11
|
`,
|
|
11
|
-
"antigravity-cli": `# ποΈ Agent Enderun β ANTIGRAVITY
|
|
12
|
+
"antigravity-cli": `# ποΈ Agent Enderun β ANTIGRAVITY Strategy (Internal Discipline)
|
|
12
13
|
|
|
13
|
-
You are the **Antigravity Specialist**
|
|
14
|
+
You are the **Antigravity Specialist**. You represent the **Military Academy** of the framework, preserving internal standards and coding discipline.
|
|
14
15
|
|
|
15
16
|
## ποΈ Directives
|
|
16
17
|
- **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
18
|
+
- **Standard Enforcement:** You are responsible for ensuring that all code adheres to the 26+ corporate standards in \`{{FRAMEWORK_DIR}}/knowledge/\`.
|
|
19
|
+
- **Sandbox Discipline:** Maintain isolated and high-discipline development environments.
|
|
19
20
|
`,
|
|
20
|
-
claude: `# ποΈ Agent Enderun β CLAUDE Strategy
|
|
21
|
+
claude: `# ποΈ Agent Enderun β CLAUDE Strategy (Operational Surgery)
|
|
21
22
|
|
|
22
|
-
You are the **Claude Field Engineer** (Surgical
|
|
23
|
+
You are the **Claude Field Engineer**. You represent the **Operational Cerrahi (Surgical)** wing of the army. Your mission is precision execution with minimal footprint.
|
|
23
24
|
|
|
24
25
|
## ποΈ Directives
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
26
|
+
- **Surgical Precision (MANDATORY):** NEVER rewrite an entire file. Use \`replace_text\` or \`patch_file\` tools exclusively.
|
|
27
|
+
- **Token Economy:** Minimize API usage by targetting only the exact lines of code needed.
|
|
28
|
+
- **Traceability:** Ensure every change is linked to an active Trace ID and logged traceable under \`{{FRAMEWORK_DIR}}/logs/\`.
|
|
29
|
+
- **Phase Discipline:** Do not attempt Phase 2 tasks if Phase 1 contracts are not sealed.
|
|
28
30
|
`,
|
|
29
|
-
grok: `# ποΈ Agent Enderun β GROK Strategy
|
|
31
|
+
grok: `# ποΈ Agent Enderun β GROK Strategy (Scouting Wing)
|
|
30
32
|
|
|
31
|
-
You are the **Grok Explorer** (
|
|
33
|
+
You are the **Grok Explorer**. You represent the **Otonom KeΕif KanadΔ± (Autonomous Scouting Wing)**. Your mission is experimental discovery and boundary testing.
|
|
32
34
|
|
|
33
35
|
## ποΈ Directives
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
+
- **Architecture Discovery:** Use \`get_project_map\` and \`get_project_gaps\` to map unexplored territory before any specialist acts.
|
|
37
|
+
- **Boundary Testing:** Identify architectural weaknesses or security gaps before they become critical.
|
|
38
|
+
- **Experimental Protocol:** Test futuristic agent behaviors and report findings to the **Commander**.
|
|
36
39
|
`,
|
|
37
|
-
cursor: `# ποΈ Agent Enderun β CURSOR Strategy
|
|
40
|
+
cursor: `# ποΈ Agent Enderun β CURSOR Strategy (Implementer)
|
|
38
41
|
|
|
39
|
-
You are the **Cursor Implementer** (
|
|
42
|
+
You are the **Cursor Implementer**. You are the **Kod Δ°ΕΓ§isi (Code Worker)** integrated directly into the IDE.
|
|
40
43
|
|
|
41
44
|
## ποΈ Directives
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
45
|
+
- **IDE Synergy:** Leverage Cursor's native context and Enderun's governance to write high-quality, compliant code.
|
|
46
|
+
- **Atomic Implementation:** Focus on implementing the specific task delegated by the @manager.
|
|
44
47
|
`,
|
|
45
|
-
codex: `# ποΈ Agent Enderun β COPILOT Strategy
|
|
48
|
+
codex: `# ποΈ Agent Enderun β COPILOT Strategy (Assistant)
|
|
46
49
|
|
|
47
|
-
You are the **Copilot
|
|
50
|
+
You are the **Copilot Assistant**. You represent the **YardΔ±mcΔ± GeliΕtirici (Assistant Developer)**.
|
|
48
51
|
|
|
49
52
|
## ποΈ Directives
|
|
50
|
-
- **
|
|
51
|
-
- **
|
|
53
|
+
- **Predictive Support:** Provide code completions and suggestions that strictly adhere to the project's \`{{FRAMEWORK_DIR}}/ENDERUN.md\` rules.
|
|
54
|
+
- **Rapid Prototyping:** Support the army by generating boilerplate that follows established enterprise patterns.
|
|
55
|
+
`,
|
|
56
|
+
local: `# ποΈ Agent Enderun β LOCAL LLM Strategy (Private Intelligence)
|
|
57
|
+
|
|
58
|
+
You are the **Local Private Intelligence**. You represent the project's **Private & Secure Command Wing**. Your intelligence is derived entirely from local models (Ollama, vLLM, etc.) and project-specific knowledge.
|
|
59
|
+
|
|
60
|
+
## ποΈ Directives
|
|
61
|
+
- **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first. You are the final arbiter of these rules.
|
|
62
|
+
- **Zero Cloud Policy:** Ensure all operations remain local and secure.
|
|
63
|
+
- **Trace ID Discipline:** Every local inference and code generation MUST follow the active Trace ID.
|
|
64
|
+
- **Technical Integrity:** Adhere strictly to the 100% type-safety and surgical edit rules of the Enderun Order.
|
|
52
65
|
`
|
|
53
66
|
};
|
|
@@ -119,6 +119,26 @@ registry.register(
|
|
|
119
119
|
}
|
|
120
120
|
);
|
|
121
121
|
|
|
122
|
+
// π Local LLM (Ollama / Private AI)
|
|
123
|
+
registry.register(
|
|
124
|
+
{
|
|
125
|
+
id: "local",
|
|
126
|
+
frameworkDir: ".enderun",
|
|
127
|
+
shimFile: "LOCAL_AI.md",
|
|
128
|
+
shimTemplate: "src/cli/shims/local.md",
|
|
129
|
+
role: "commander",
|
|
130
|
+
templateDir: ".enderun",
|
|
131
|
+
nestedDirs: ["agents", "rules"],
|
|
132
|
+
agentsDir: ".enderun/agents",
|
|
133
|
+
agentsExt: ".md"
|
|
134
|
+
},
|
|
135
|
+
(projectRoot, mcpBlock) => {
|
|
136
|
+
const frameworkDir = ".enderun";
|
|
137
|
+
writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
|
|
138
|
+
UI.success(`Local LLM MCP (Ollama/Private AI) registered β ${frameworkDir}/mcp_config.json`);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
122
142
|
// πΈ Antigravity
|
|
123
143
|
registry.register(
|
|
124
144
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const ADAPTER_IDS = ["gemini", "claude", "grok", "cursor", "codex", "antigravity-cli"] as const;
|
|
1
|
+
export const ADAPTER_IDS = ["gemini", "claude", "grok", "cursor", "codex", "local", "antigravity-cli"] as const;
|
|
2
2
|
export type AdapterId = (typeof ADAPTER_IDS)[number];
|
|
3
3
|
|
|
4
4
|
export type AdapterRole = "commander" | "architect" | "researcher" | "implementer" | "general";
|
|
@@ -195,7 +195,7 @@ function buildSystemPrompt(
|
|
|
195
195
|
`**Authority Tier:** ${ag.tier} (Capability: ${ag.capability}/10)`,
|
|
196
196
|
"",
|
|
197
197
|
"## Project Structure",
|
|
198
|
-
|
|
198
|
+
"This project uses the following directory structure:",
|
|
199
199
|
`- **Backend:** \`${paths.backend}\``,
|
|
200
200
|
`- **Frontend:** \`${paths.frontend}\``,
|
|
201
201
|
`- **Mobile:** \`${paths.mobile}\``,
|