agent-enderun 0.7.2 → 0.8.0

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.
Files changed (181) hide show
  1. package/.enderun/BRAIN_DASHBOARD.md +46 -0
  2. package/.enderun/PROJECT_MEMORY.md +46 -0
  3. package/.enderun/STATUS.md +62 -0
  4. package/.enderun/agents/agent_army_schema.json +85 -0
  5. package/.enderun/agents/analyst/agent.json +23 -0
  6. package/.enderun/agents/analyst/prompt.md +213 -0
  7. package/.enderun/agents/backend/agent.json +24 -0
  8. package/.enderun/agents/backend/prompt.md +327 -0
  9. package/.enderun/agents/devops/agent.json +61 -0
  10. package/.enderun/agents/devops/prompt.md +127 -0
  11. package/.enderun/agents/explorer/agent.json +22 -0
  12. package/.enderun/agents/explorer/prompt.md +115 -0
  13. package/.enderun/agents/frontend/agent.json +24 -0
  14. package/.enderun/agents/frontend/prompt.md +313 -0
  15. package/.enderun/agents/git/agent.json +22 -0
  16. package/.enderun/agents/git/prompt.md +99 -0
  17. package/.enderun/agents/manager/agent.json +48 -0
  18. package/.enderun/agents/manager/prompt.md +231 -0
  19. package/.enderun/agents/mobile/agent.json +21 -0
  20. package/.enderun/agents/mobile/prompt.md +81 -0
  21. package/.enderun/agents/native/agent.json +21 -0
  22. package/.enderun/agents/native/prompt.md +86 -0
  23. package/.enderun/agents/orchestrator/agent.json +53 -0
  24. package/.enderun/agents/orchestrator/prompt.md +124 -0
  25. package/.enderun/agents/qa/agent.json +57 -0
  26. package/.enderun/agents/qa/prompt.md +69 -0
  27. package/.enderun/agents/schema/agent-lifecycle-schema.json +65 -0
  28. package/.enderun/agents/security/agent.json +54 -0
  29. package/.enderun/agents/security/prompt.md +154 -0
  30. package/.enderun/claude.md +19 -0
  31. package/.enderun/cursor.md +19 -0
  32. package/.enderun/gemini.md +19 -0
  33. package/.enderun/knowledge/corporate-governance/analyst-contract-integrity-guide.md +92 -0
  34. package/.enderun/knowledge/corporate-governance/audit-logging-standard.md +59 -0
  35. package/.enderun/knowledge/corporate-governance/backend-high-risk-endpoint-guide.md +125 -0
  36. package/.enderun/knowledge/corporate-governance/branded-types-law.md +77 -0
  37. package/.enderun/knowledge/corporate-governance/contract-drift-detection-simulation.md +92 -0
  38. package/.enderun/knowledge/corporate-governance/corporate-governance-playbook.md +89 -0
  39. package/.enderun/knowledge/corporate-governance/corporate-project-kickoff-checklist.md +56 -0
  40. package/.enderun/knowledge/corporate-governance/corporate-project-kickoff-simulation.md +144 -0
  41. package/.enderun/knowledge/corporate-governance/corporate-project-scaffolding-sop.md +103 -0
  42. package/.enderun/knowledge/corporate-governance/domain-error-handling-standard.md +75 -0
  43. package/.enderun/knowledge/corporate-governance/frontend-high-risk-modal-guide.md +211 -0
  44. package/.enderun/knowledge/corporate-governance/governance-baseline-briefing-template.md +54 -0
  45. package/.enderun/knowledge/corporate-governance/high-risk-action-approval-flow-simulation.md +167 -0
  46. package/.enderun/knowledge/corporate-governance/high-risk-actions-managerapproval.md +76 -0
  47. package/.enderun/knowledge/corporate-governance/high-risk-security-modal-hermes-flow.md +49 -0
  48. package/.enderun/knowledge/corporate-governance/manager-first-30-days-guide.md +112 -0
  49. package/.enderun/knowledge/corporate-governance/manager-first-three-briefings.md +79 -0
  50. package/.enderun/knowledge/deployment_checklist.md +7 -0
  51. package/{.antigravitycli → .enderun}/knowledge/framework_vs_user_project_boundary.md +3 -3
  52. package/{.antigravitycli → .enderun}/knowledge/frontend_professionalization_guidelines.md +17 -20
  53. package/{.antigravitycli → .enderun}/knowledge/hermes_live_test_guidelines.md +9 -9
  54. package/{.antigravitycli → .enderun}/knowledge/hermes_protocol.md +2 -2
  55. package/{.antigravitycli → .enderun}/knowledge/manager_authority_audit_enforcement.md +11 -11
  56. package/{.antigravitycli → .enderun}/knowledge/project_scaffold_guidelines.md +8 -8
  57. package/{.antigravitycli → .enderun}/knowledge/reference_application_guidelines.md +7 -7
  58. package/.enderun/logs/analyst.json +1 -0
  59. package/.enderun/logs/backend.json +1 -0
  60. package/.enderun/logs/devops.json +1 -0
  61. package/.enderun/logs/explorer.json +1 -0
  62. package/.enderun/logs/frontend.json +1 -0
  63. package/.enderun/logs/git.json +1 -0
  64. package/.enderun/logs/manager.json +1 -0
  65. package/.enderun/logs/mobile.json +1 -0
  66. package/.enderun/logs/native.json +1 -0
  67. package/.enderun/logs/orchestrator.json +1 -0
  68. package/.enderun/logs/qa.json +1 -0
  69. package/.enderun/logs/security.json +1 -0
  70. package/.enderun/memory-graph/agent-contexts/analyst.json +1 -0
  71. package/.enderun/memory-graph/agent-contexts/backend.json +1 -0
  72. package/.enderun/memory-graph/agent-contexts/devops.json +1 -0
  73. package/.enderun/memory-graph/agent-contexts/explorer.json +1 -0
  74. package/.enderun/memory-graph/agent-contexts/frontend.json +1 -0
  75. package/.enderun/memory-graph/agent-contexts/git.json +1 -0
  76. package/.enderun/memory-graph/agent-contexts/manager.json +1 -0
  77. package/.enderun/memory-graph/agent-contexts/mobile.json +1 -0
  78. package/.enderun/memory-graph/agent-contexts/native.json +1 -0
  79. package/.enderun/memory-graph/agent-contexts/orchestrator.json +1 -0
  80. package/.enderun/memory-graph/agent-contexts/qa.json +1 -0
  81. package/.enderun/memory-graph/agent-contexts/security.json +1 -0
  82. package/.enderun/memory-graph/graph.json +26 -0
  83. package/.enderun/memory-graph/shared-facts.json +52 -0
  84. package/.enderun/monitoring/.gitkeep +0 -0
  85. package/.enderun/queue/README.md +36 -0
  86. package/ENDERUN.md +4 -4
  87. package/README.md +233 -109
  88. package/bin/cli.js +13 -2138
  89. package/bin/hermes-sandbox.js +0 -0
  90. package/bin/validate-agent-army.js +3 -3
  91. package/claude.md +19 -0
  92. package/cursor.md +19 -0
  93. package/docs/architecture/approval-flows.md +2 -37
  94. package/docs/backend/error-handling.md +2 -51
  95. package/docs/frontend/component-patterns.md +2 -47
  96. package/docs/getting-started.md +7 -15
  97. package/docs/roadmap.md +2 -2
  98. package/framework-mcp/README.md +3 -9
  99. package/framework-mcp/dist/index.js +0 -0
  100. package/framework-mcp/dist/schemas.js +3 -3
  101. package/framework-mcp/dist/tools/framework.js +17 -10
  102. package/framework-mcp/dist/tools/governance.js +696 -0
  103. package/framework-mcp/dist/tools/index.js +14 -12
  104. package/framework-mcp/dist/tools/memory.js +106 -0
  105. package/framework-mcp/dist/tools/monitoring.js +294 -0
  106. package/framework-mcp/dist/tools/orchestration.js +368 -0
  107. package/framework-mcp/dist/tools/pipeline.js +267 -0
  108. package/framework-mcp/dist/utils.js +25 -3
  109. package/framework-mcp/package.json +3 -3
  110. package/framework-mcp/src/schemas.ts +3 -45
  111. package/framework-mcp/src/tools/framework.ts +18 -10
  112. package/framework-mcp/src/tools/governance.ts +891 -0
  113. package/framework-mcp/src/tools/index.ts +14 -12
  114. package/framework-mcp/src/tools/memory.ts +103 -0
  115. package/framework-mcp/src/tools/monitoring.ts +351 -0
  116. package/framework-mcp/src/tools/orchestration.ts +439 -0
  117. package/framework-mcp/src/tools/pipeline.ts +353 -0
  118. package/framework-mcp/src/utils.ts +24 -3
  119. package/framework-mcp/tsconfig.json +2 -1
  120. package/gemini.md +19 -0
  121. package/package.json +16 -20
  122. package/.antigravitycli/BRAIN_DASHBOARD.md +0 -43
  123. package/.antigravitycli/ENDERUN.md +0 -253
  124. package/.antigravitycli/PROJECT_MEMORY.md +0 -78
  125. package/.antigravitycli/STATUS.md +0 -21
  126. package/.antigravitycli/agents/agent_army_schema.json +0 -79
  127. package/.antigravitycli/agents/analyst/agent.json +0 -55
  128. package/.antigravitycli/agents/backend/agent.json +0 -58
  129. package/.antigravitycli/agents/explorer/agent.json +0 -50
  130. package/.antigravitycli/agents/frontend/agent.json +0 -56
  131. package/.antigravitycli/agents/git/agent.json +0 -53
  132. package/.antigravitycli/agents/manager/agent.json +0 -82
  133. package/.antigravitycli/agents/mobile/agent.json +0 -52
  134. package/.antigravitycli/agents/native/agent.json +0 -56
  135. package/.antigravitycli/assets/manager.png +0 -0
  136. package/.antigravitycli/blueprints/README.md +0 -82
  137. package/.antigravitycli/blueprints/backend/errors/config/meta.json +0 -16
  138. package/.antigravitycli/blueprints/backend/errors/domain-error.ts +0 -64
  139. package/.antigravitycli/knowledge/README.md +0 -22
  140. package/.antigravitycli/knowledge/database_governance_guidelines.md +0 -118
  141. package/.antigravitycli/knowledge/deployment_checklist.md +0 -133
  142. package/.antigravitycli/knowledge/documentation_ownership.md +0 -122
  143. package/.antigravitycli/knowledge/documentation_ownership_status.md +0 -122
  144. package/.antigravitycli/knowledge/enterprise_capabilities_reference.md +0 -149
  145. package/.antigravitycli/knowledge/enterprise_frontend_adaptation.md +0 -232
  146. package/.antigravitycli/knowledge/enterprise_project_adaptation.md +0 -168
  147. package/.antigravitycli/knowledge/frontend_real_battle_test_protocol.md +0 -162
  148. package/antigravity.md +0 -15
  149. package/docs/architecture/decisions/README.md +0 -31
  150. package/docs/architecture/notification-strategy.md +0 -38
  151. package/docs/backend/audit-logging.md +0 -34
  152. package/docs/frontend/forms.md +0 -40
  153. package/docs/frontend/notifications.md +0 -37
  154. package/framework-mcp/dist/tools/academy.js +0 -182
  155. package/framework-mcp/dist/tools/database.js +0 -57
  156. package/framework-mcp/dist/tools/repository.js +0 -101
  157. package/framework-mcp/dist/tools/scaffold.js +0 -132
  158. package/framework-mcp/src/tools/academy.ts +0 -180
  159. package/framework-mcp/src/tools/database.ts +0 -54
  160. package/framework-mcp/src/tools/repository.ts +0 -101
  161. package/framework-mcp/src/tools/scaffold.ts +0 -150
  162. /package/{.antigravitycli/messages → .enderun/benchmarks}/.gitkeep +0 -0
  163. /package/{.antigravitycli → .enderun}/cli-commands.json +0 -0
  164. /package/{.antigravitycli → .enderun}/config.json +0 -0
  165. /package/{.antigravitycli → .enderun}/knowledge/api_design_rules.md +0 -0
  166. /package/{.antigravitycli → .enderun}/knowledge/async_error_handling.md +0 -0
  167. /package/{.antigravitycli → .enderun}/knowledge/blueprint_driven_construction.md +0 -0
  168. /package/{.antigravitycli → .enderun}/knowledge/branded_types_pattern.md +0 -0
  169. /package/{.antigravitycli → .enderun}/knowledge/code_review_checklist.md +0 -0
  170. /package/{.antigravitycli → .enderun}/knowledge/contract_versioning.md +0 -0
  171. /package/{.antigravitycli → .enderun}/knowledge/database_migration.md +0 -0
  172. /package/{.antigravitycli → .enderun}/knowledge/git_commit_strategy.md +0 -0
  173. /package/{.antigravitycli → .enderun}/knowledge/legacy_onboarding.md +0 -0
  174. /package/{.antigravitycli → .enderun}/knowledge/monitoring_setup.md +0 -0
  175. /package/{.antigravitycli → .enderun}/knowledge/performance_guidelines.md +0 -0
  176. /package/{.antigravitycli → .enderun}/knowledge/repository_patterns.md +0 -0
  177. /package/{.antigravitycli → .enderun}/knowledge/responsive_design_standards.md +0 -0
  178. /package/{.antigravitycli → .enderun}/knowledge/security_scanning.md +0 -0
  179. /package/{.antigravitycli → .enderun}/knowledge/testing_standards.md +0 -0
  180. /package/{.antigravitycli → .enderun}/knowledge/troubleshooting_guide.md +0 -0
  181. /package/{.antigravitycli → .enderun}/knowledge/zero_ui_library_policy.md +0 -0
@@ -0,0 +1,439 @@
1
+ import { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ import { promises as fs } from "fs";
3
+ import path from "path";
4
+
5
+ // ─── Tool Definitions ─────────────────────────────────────────────────────────
6
+
7
+ export const orchestrationTools: Tool[] = [
8
+ {
9
+ name: "create_task_dag",
10
+ description:
11
+ "Creates a Directed Acyclic Graph (DAG) of tasks with dependencies. Used by @orchestrator to plan parallel execution. Returns both a JSON structure and a Mermaid diagram for visualization.",
12
+ inputSchema: {
13
+ type: "object",
14
+ properties: {
15
+ traceId: {
16
+ type: "string",
17
+ description: "Trace ID inherited from @manager briefing. Required.",
18
+ },
19
+ tasks: {
20
+ type: "array",
21
+ description: "Array of task definitions to build the DAG from.",
22
+ items: {
23
+ type: "object",
24
+ required: ["id", "agent", "briefing"],
25
+ properties: {
26
+ id: { type: "string", description: "Unique task ID (e.g. T001)" },
27
+ agent: { type: "string", description: "Assigned agent (e.g. @backend)" },
28
+ briefing: { type: "string", description: "Task description" },
29
+ dependsOn: {
30
+ type: "array",
31
+ items: { type: "string" },
32
+ description: "Task IDs that must complete before this task starts",
33
+ },
34
+ priority: {
35
+ type: "string",
36
+ enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"],
37
+ default: "MEDIUM",
38
+ },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ required: ["traceId", "tasks"],
44
+ },
45
+ },
46
+ {
47
+ name: "assign_agent_to_task",
48
+ description:
49
+ "Assigns the most capable available agent to a task based on domain and capability score. Checks STATUS.md for agent availability before assigning.",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ traceId: { type: "string", description: "Active Trace ID" },
54
+ taskId: { type: "string", description: "Task ID to assign" },
55
+ domain: {
56
+ type: "string",
57
+ enum: ["backend", "frontend", "security", "qa", "devops", "mobile", "native", "analysis", "git", "exploration"],
58
+ description: "Domain of the task — used to filter eligible agents",
59
+ },
60
+ preferredAgent: {
61
+ type: "string",
62
+ description: "Optional preferred agent. If not available, falls back to capability-based selection.",
63
+ },
64
+ },
65
+ required: ["traceId", "taskId", "domain"],
66
+ },
67
+ },
68
+ {
69
+ name: "get_agent_workload",
70
+ description:
71
+ "Returns the current workload for all agents: active task count, state, current task description, and health score. Used by @orchestrator to make load-aware assignment decisions.",
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ projectRoot: { type: "string", description: "Root directory of the project." },
76
+ },
77
+ required: [],
78
+ },
79
+ },
80
+ {
81
+ name: "resolve_agent_blockage",
82
+ description:
83
+ "Handles a BLOCKED agent by documenting the blockage, escalating to @manager, and optionally reassigning the task to another eligible agent.",
84
+ inputSchema: {
85
+ type: "object",
86
+ properties: {
87
+ blockedAgent: { type: "string", description: "The agent identifier that is BLOCKED, e.g. @frontend" },
88
+ traceId: { type: "string", description: "Active Trace ID" },
89
+ blockReason: { type: "string", description: "Mandatory description of why the agent is blocked" },
90
+ reassign: {
91
+ type: "boolean",
92
+ description: "If true, attempt to find and assign an alternative agent",
93
+ default: false,
94
+ },
95
+ },
96
+ required: ["blockedAgent", "traceId", "blockReason"],
97
+ },
98
+ },
99
+ {
100
+ name: "broadcast_army_directive",
101
+ description:
102
+ "Broadcasts a directive message to all agents simultaneously via the Hermes messaging system. Used for war-room mode, policy changes, or global state transitions.",
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: {
106
+ traceId: { type: "string", description: "Active Trace ID" },
107
+ directiveType: {
108
+ type: "string",
109
+ enum: [
110
+ "WAR_ROOM_ACTIVATE",
111
+ "WAR_ROOM_DEACTIVATE",
112
+ "CONTRACT_CHANGED",
113
+ "PHASE_ROLLBACK",
114
+ "POLICY_UPDATE",
115
+ "ARMY_PAUSE",
116
+ "ARMY_RESUME",
117
+ ],
118
+ description: "Type of directive being broadcast",
119
+ },
120
+ message: { type: "string", description: "Human-readable directive message" },
121
+ urgency: {
122
+ type: "string",
123
+ enum: ["IMMEDIATE", "HIGH", "NORMAL"],
124
+ default: "NORMAL",
125
+ },
126
+ },
127
+ required: ["traceId", "directiveType", "message"],
128
+ },
129
+ },
130
+ ];
131
+
132
+ // ─── Handlers ─────────────────────────────────────────────────────────────────
133
+
134
+ export const orchestrationHandlers: Record<
135
+ string,
136
+ (args: unknown, projectRoot: string) => Promise<{ content: Array<{ type: string; text: string }> }>
137
+ > = {
138
+ async create_task_dag(args, projectRoot) {
139
+ const { traceId, tasks } = args as {
140
+ traceId: string;
141
+ tasks: Array<{
142
+ id: string;
143
+ agent: string;
144
+ briefing: string;
145
+ dependsOn?: string[];
146
+ priority?: string;
147
+ }>;
148
+ };
149
+
150
+ // Build adjacency structure
151
+ const dagNodes = tasks.map((t) => ({
152
+ id: t.id,
153
+ agent: t.agent,
154
+ briefing: t.briefing,
155
+ status: "PENDING",
156
+ priority: t.priority ?? "MEDIUM",
157
+ dependsOn: t.dependsOn ?? [],
158
+ }));
159
+
160
+ // Topological sort to find execution order
161
+ const executionWaves: string[][] = [];
162
+ const resolved = new Set<string>();
163
+ const remaining = [...dagNodes];
164
+
165
+ while (remaining.length > 0) {
166
+ const wave = remaining.filter(
167
+ (n) => n.dependsOn.every((dep) => resolved.has(dep))
168
+ );
169
+ if (wave.length === 0) {
170
+ return {
171
+ content: [
172
+ {
173
+ type: "text",
174
+ text: JSON.stringify({
175
+ error: "CIRCULAR_DEPENDENCY",
176
+ message: "Circular dependency detected in task DAG. Check dependsOn fields.",
177
+ }),
178
+ },
179
+ ],
180
+ };
181
+ }
182
+ executionWaves.push(wave.map((n) => n.id));
183
+ wave.forEach((n) => {
184
+ resolved.add(n.id);
185
+ remaining.splice(remaining.indexOf(n), 1);
186
+ });
187
+ }
188
+
189
+ // Generate Mermaid diagram
190
+ const mermaidLines = ["graph TD"];
191
+ for (const node of dagNodes) {
192
+ const label = `${node.id}["${node.agent}: ${node.briefing.slice(0, 40)}..."]`;
193
+ mermaidLines.push(` ${node.id}${label}`);
194
+ }
195
+ for (const node of dagNodes) {
196
+ for (const dep of node.dependsOn) {
197
+ mermaidLines.push(` ${dep} --> ${node.id}`);
198
+ }
199
+ }
200
+
201
+ const dag = {
202
+ traceId,
203
+ createdAt: new Date().toISOString(),
204
+ nodes: dagNodes,
205
+ executionWaves,
206
+ mermaid: mermaidLines.join("\n"),
207
+ parallelOpportunities: executionWaves.filter((w) => w.length > 1).length,
208
+ };
209
+
210
+ // Persist to queue
211
+ const queueDir = path.join(projectRoot, ".enderun", "queue", "pending");
212
+ await fs.mkdir(queueDir, { recursive: true });
213
+ await fs.writeFile(
214
+ path.join(queueDir, `${traceId}-dag.json`),
215
+ JSON.stringify(dag, null, 2),
216
+ "utf-8"
217
+ );
218
+
219
+ return { content: [{ type: "text", text: JSON.stringify(dag, null, 2) }] };
220
+ },
221
+
222
+ async assign_agent_to_task(args, projectRoot) {
223
+ const { traceId, taskId, domain, preferredAgent } = args as {
224
+ traceId: string;
225
+ taskId: string;
226
+ domain: string;
227
+ preferredAgent?: string;
228
+ };
229
+
230
+ // Try to load capability roster from shared-facts.json (dynamic — fixes KRITIK-3)
231
+ let agentCapabilityRoster: Array<{ agent: string; primaryDomain: string; capability: number }> = [];
232
+ try {
233
+ const sharedFactsPath = path.join(projectRoot, ".enderun", "memory-graph", "shared-facts.json");
234
+ const content = await fs.readFile(sharedFactsPath, "utf-8");
235
+ const facts = JSON.parse(content) as { agentCapabilityRoster?: typeof agentCapabilityRoster };
236
+ agentCapabilityRoster = facts.agentCapabilityRoster ?? [];
237
+ } catch {
238
+ // Fallback to hardcoded map if shared-facts not available
239
+ }
240
+
241
+ // Build domain → agents map from shared-facts roster
242
+ // Exclude @manager — @manager does NOT execute specialist tasks
243
+ const domainMap: Record<string, { agent: string; capability: number }[]> = {};
244
+ for (const entry of agentCapabilityRoster) {
245
+ if (entry.agent === "@manager" || entry.agent === "@orchestrator") continue;
246
+ const d = entry.primaryDomain;
247
+ if (!domainMap[d]) domainMap[d] = [];
248
+ domainMap[d].push({ agent: entry.agent, capability: entry.capability });
249
+ }
250
+
251
+ // Hardcoded fallback (used only if shared-facts unavailable)
252
+ const fallbackDomainMap: Record<string, { agent: string; capability: number }[]> = {
253
+ backend: [{ agent: "@backend", capability: 9.2 }],
254
+ frontend: [{ agent: "@frontend", capability: 9.0 }],
255
+ security: [{ agent: "@security", capability: 9.3 }],
256
+ qa: [{ agent: "@qa", capability: 9.1 }],
257
+ devops: [{ agent: "@devops", capability: 9.0 }],
258
+ mobile: [{ agent: "@mobile", capability: 8.8 }],
259
+ native: [{ agent: "@native", capability: 8.9 }],
260
+ analysis: [{ agent: "@analyst", capability: 9.6 }],
261
+ git: [{ agent: "@git", capability: 9.1 }],
262
+ exploration: [{ agent: "@explorer", capability: 9.4 }],
263
+ };
264
+
265
+ const effectiveMap = Object.keys(domainMap).length > 0 ? domainMap : fallbackDomainMap;
266
+ const candidates = effectiveMap[domain] ?? [];
267
+ let assigned = [...candidates].sort((a, b) => b.capability - a.capability)[0];
268
+
269
+ if (preferredAgent && preferredAgent !== "@manager" && preferredAgent !== "@orchestrator") {
270
+ const preferred = candidates.find((c) => c.agent === preferredAgent);
271
+ if (preferred) assigned = preferred;
272
+ }
273
+
274
+ if (!assigned) {
275
+ return {
276
+ content: [
277
+ {
278
+ type: "text",
279
+ text: JSON.stringify({ error: "NO_ELIGIBLE_AGENT", domain, taskId,
280
+ note: "@manager and @orchestrator are excluded from task assignment" }),
281
+ },
282
+ ],
283
+ };
284
+ }
285
+
286
+ const assignment = {
287
+ traceId,
288
+ taskId,
289
+ assignedAgent: assigned.agent,
290
+ capabilityScore: assigned.capability,
291
+ assignedAt: new Date().toISOString(),
292
+ domain,
293
+ sourceMap: Object.keys(domainMap).length > 0 ? "shared-facts" : "fallback",
294
+ };
295
+
296
+ // Write assignment log
297
+ const logPath = path.join(projectRoot, ".enderun", "logs", "orchestrator.json");
298
+ let log: { assignments: unknown[] } = { assignments: [] };
299
+ try {
300
+ const existing = await fs.readFile(logPath, "utf-8");
301
+ log = JSON.parse(existing) as { assignments: unknown[] };
302
+ } catch {
303
+ // File doesn't exist yet — start fresh
304
+ }
305
+ log.assignments.push(assignment);
306
+ await fs.mkdir(path.dirname(logPath), { recursive: true });
307
+ await fs.writeFile(logPath, JSON.stringify(log, null, 2), "utf-8");
308
+
309
+ return { content: [{ type: "text", text: JSON.stringify(assignment, null, 2) }] };
310
+ },
311
+
312
+ async get_agent_workload(_args, projectRoot) {
313
+ const statusPath = path.join(projectRoot, ".enderun", "STATUS.md");
314
+ let statusContent: string;
315
+ try {
316
+ statusContent = await fs.readFile(statusPath, "utf-8");
317
+ } catch {
318
+ statusContent = "";
319
+ }
320
+
321
+ // Parse the markdown table
322
+ const lines = statusContent.length > 0
323
+ ? statusContent.split("\n").filter((l) => l.includes("|") && !l.includes("---"))
324
+ : [];
325
+ const agents = lines.length > 1
326
+ ? lines
327
+ .slice(1) // skip header
328
+ .map((line) => {
329
+ const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
330
+ return cells.length >= 4
331
+ ? { agent: cells[0], state: cells[1], currentTask: cells[2], healthScore: cells[4] }
332
+ : null;
333
+ })
334
+ .filter(Boolean)
335
+ : [];
336
+
337
+ return { content: [{ type: "text", text: JSON.stringify({ agents, generatedAt: new Date().toISOString() }, null, 2) }] };
338
+ },
339
+
340
+ async resolve_agent_blockage(args, projectRoot) {
341
+ const { blockedAgent, traceId, blockReason, reassign } = args as {
342
+ blockedAgent: string;
343
+ traceId: string;
344
+ blockReason: string;
345
+ reassign?: boolean;
346
+ };
347
+
348
+ const resolution = {
349
+ traceId,
350
+ blockedAgent,
351
+ blockReason,
352
+ detectedAt: new Date().toISOString(),
353
+ escalatedToManager: true,
354
+ reassignAttempted: reassign ?? false,
355
+ status: "ESCALATED_TO_MANAGER",
356
+ };
357
+
358
+ // Log blockage
359
+ const logPath = path.join(projectRoot, ".enderun", "logs", "orchestrator.json");
360
+ let log: { assignments: unknown[]; blockages: unknown[] } = { assignments: [], blockages: [] };
361
+ try {
362
+ const existing = await fs.readFile(logPath, "utf-8");
363
+ log = JSON.parse(existing) as { assignments: unknown[]; blockages: unknown[] };
364
+ if (!log.blockages) log.blockages = [];
365
+ } catch {
366
+ // Start fresh
367
+ }
368
+ log.blockages.push(resolution);
369
+ await fs.mkdir(path.dirname(logPath), { recursive: true });
370
+ await fs.writeFile(logPath, JSON.stringify(log, null, 2), "utf-8");
371
+
372
+ // Write a message to @manager's inbox via messages folder
373
+ const messagesDir = path.join(projectRoot, ".enderun", "messages");
374
+ await fs.mkdir(messagesDir, { recursive: true });
375
+ const messageFile = path.join(messagesDir, `BLOCKAGE-${traceId}-${Date.now()}.json`);
376
+ await fs.writeFile(
377
+ messageFile,
378
+ JSON.stringify({
379
+ from: "@orchestrator",
380
+ to: "@manager",
381
+ type: "AGENT_BLOCKED",
382
+ traceId,
383
+ payload: { blockedAgent, blockReason, resolution },
384
+ timestamp: new Date().toISOString(),
385
+ }),
386
+ "utf-8"
387
+ );
388
+
389
+ return { content: [{ type: "text", text: JSON.stringify(resolution, null, 2) }] };
390
+ },
391
+
392
+ async broadcast_army_directive(args, projectRoot) {
393
+ const { traceId, directiveType, message, urgency } = args as {
394
+ traceId: string;
395
+ directiveType: string;
396
+ message: string;
397
+ urgency?: string;
398
+ };
399
+
400
+ const broadcast = {
401
+ id: `BROADCAST-${traceId}-${Date.now()}`,
402
+ from: "@orchestrator",
403
+ to: "*",
404
+ traceId,
405
+ directiveType,
406
+ message,
407
+ urgency: urgency ?? "NORMAL",
408
+ timestamp: new Date().toISOString(),
409
+ };
410
+
411
+ // Write to messages folder for all agents
412
+ const messagesDir = path.join(projectRoot, ".enderun", "messages");
413
+ await fs.mkdir(messagesDir, { recursive: true });
414
+ await fs.writeFile(
415
+ path.join(messagesDir, `${broadcast.id}.json`),
416
+ JSON.stringify(broadcast, null, 2),
417
+ "utf-8"
418
+ );
419
+
420
+ // Log broadcast
421
+ const logPath = path.join(projectRoot, ".enderun", "logs", "orchestrator.json");
422
+ let log: { assignments: unknown[]; blockages: unknown[]; broadcasts: unknown[] } = {
423
+ assignments: [],
424
+ blockages: [],
425
+ broadcasts: [],
426
+ };
427
+ try {
428
+ const existing = await fs.readFile(logPath, "utf-8");
429
+ log = JSON.parse(existing) as typeof log;
430
+ if (!log.broadcasts) log.broadcasts = [];
431
+ } catch {
432
+ // Start fresh
433
+ }
434
+ log.broadcasts.push(broadcast);
435
+ await fs.writeFile(logPath, JSON.stringify(log, null, 2), "utf-8");
436
+
437
+ return { content: [{ type: "text", text: JSON.stringify(broadcast, null, 2) }] };
438
+ },
439
+ };