0xray 3.4.8 → 3.4.10
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/AGENTS-consumer.md +1 -1
- package/AGENTS.md +1 -1
- package/README.md +15 -1
- package/SKILLS.md +1 -1
- package/dist/AGENTS.md +1 -1
- package/dist/CHANGELOG.md +28 -0
- package/dist/README.md +15 -1
- package/dist/SKILLS.md +1 -1
- package/dist/governance/llm-governance-provider.d.ts +2 -0
- package/dist/governance/llm-governance-provider.js +144 -158
- package/dist/mcps/knowledge-skills/code-review.server.js +1 -1
- package/dist/mcps/knowledge-skills/security-audit.server.js +1 -1
- package/dist/mcps/orchestrator/config/agent-capabilities.d.ts +1 -1
- package/dist/mcps/orchestrator/config/agent-capabilities.js +23 -1
- package/dist/mcps/orchestrator/config/memory-routing-bridge.js +2 -1
- package/dist/mcps/orchestrator/execution/execution-planner.d.ts +2 -0
- package/dist/mcps/orchestrator/execution/execution-planner.js +24 -13
- package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +13 -1
- package/dist/mcps/orchestrator/handlers/complexity-handler.js +40 -4
- package/dist/mcps/orchestrator/handlers/task-handler.js +13 -6
- package/dist/mcps/orchestrator/server.js +9 -1
- package/dist/mcps/orchestrator/types.d.ts +4 -1
- package/dist/mcps/researcher.server.js +1 -1
- package/dist/nucleus/autonomy-kernel.d.ts +5 -1
- package/dist/nucleus/autonomy-kernel.js +24 -17
- package/dist/skills/orchestrator/SKILL.md +3 -2
- package/package.json +4 -2
- package/scripts/mjs/verify-orchestrator-behavior.mjs +122 -0
- package/src/mcps/knowledge-skills/code-review.server.ts +1 -1
- package/src/mcps/knowledge-skills/security-audit.server.ts +1 -1
- package/src/mcps/orchestrator/config/agent-capabilities.ts +26 -0
- package/src/mcps/orchestrator/config/memory-routing-bridge.ts +3 -1
- package/src/mcps/orchestrator/execution/execution-planner.ts +28 -11
- package/src/mcps/orchestrator/handlers/complexity-handler.ts +63 -5
- package/src/mcps/orchestrator/handlers/task-handler.ts +22 -6
- package/src/mcps/orchestrator/server.ts +9 -1
- package/src/mcps/orchestrator/types.ts +4 -1
- package/src/mcps/researcher.server.ts +1 -1
- package/src/skills/orchestrator/SKILL.md +3 -2
|
@@ -17,6 +17,24 @@ import {
|
|
|
17
17
|
fromMemoryTask,
|
|
18
18
|
} from '../config/memory-routing-bridge.js';
|
|
19
19
|
|
|
20
|
+
/** Dependency count from task-ID list or numeric hint from analyze-complexity */
|
|
21
|
+
export function dependencyCount(
|
|
22
|
+
deps: OrchestrationTask['dependencies'] | undefined,
|
|
23
|
+
): number {
|
|
24
|
+
if (deps == null) return 0;
|
|
25
|
+
if (typeof deps === 'number' && Number.isFinite(deps)) {
|
|
26
|
+
return Math.max(0, Math.round(deps));
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(deps)) return deps.length;
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isDependencyIdList(
|
|
33
|
+
deps: OrchestrationTask['dependencies'] | undefined,
|
|
34
|
+
): deps is string[] {
|
|
35
|
+
return Array.isArray(deps) && deps.length > 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
20
38
|
/**
|
|
21
39
|
* Execution Planner
|
|
22
40
|
* Plans and optimizes task execution strategies
|
|
@@ -51,8 +69,8 @@ export class ExecutionPlanner {
|
|
|
51
69
|
errors.push(`Task ${i}: Missing required field 'type'`);
|
|
52
70
|
}
|
|
53
71
|
|
|
54
|
-
// Check dependencies exist
|
|
55
|
-
if (task.dependencies && task.id) {
|
|
72
|
+
// Check task-ID dependencies exist (numeric hints skip graph validation)
|
|
73
|
+
if (isDependencyIdList(task.dependencies) && task.id) {
|
|
56
74
|
const taskIds = new Set(tasks.map(t => t.id).filter((id): id is string => !!id));
|
|
57
75
|
for (const depId of task.dependencies) {
|
|
58
76
|
if (!taskIds.has(depId)) {
|
|
@@ -162,10 +180,10 @@ export class ExecutionPlanner {
|
|
|
162
180
|
const dependentTasks: OrchestrationTask[] = [];
|
|
163
181
|
|
|
164
182
|
for (const task of sortedTasks) {
|
|
165
|
-
if (
|
|
166
|
-
independentTasks.push(task);
|
|
167
|
-
} else {
|
|
183
|
+
if (isDependencyIdList(task.dependencies)) {
|
|
168
184
|
dependentTasks.push(task);
|
|
185
|
+
} else {
|
|
186
|
+
independentTasks.push(task);
|
|
169
187
|
}
|
|
170
188
|
}
|
|
171
189
|
|
|
@@ -188,7 +206,7 @@ export class ExecutionPlanner {
|
|
|
188
206
|
if (visited.has(task.id)) return;
|
|
189
207
|
visited.add(task.id);
|
|
190
208
|
|
|
191
|
-
if (task.dependencies) {
|
|
209
|
+
if (isDependencyIdList(task.dependencies)) {
|
|
192
210
|
for (const depId of task.dependencies) {
|
|
193
211
|
const depTask = taskMap.get(depId);
|
|
194
212
|
if (depTask) visit(depTask);
|
|
@@ -225,6 +243,7 @@ export class ExecutionPlanner {
|
|
|
225
243
|
requiredCaps,
|
|
226
244
|
complexity,
|
|
227
245
|
operationDescription,
|
|
246
|
+
task.type,
|
|
228
247
|
) || 'orchestrator';
|
|
229
248
|
|
|
230
249
|
if (!agentAssignments.has(agent)) {
|
|
@@ -280,6 +299,7 @@ export class ExecutionPlanner {
|
|
|
280
299
|
requiredCaps,
|
|
281
300
|
complexity,
|
|
282
301
|
task.description,
|
|
302
|
+
task.type,
|
|
283
303
|
) || 'orchestrator';
|
|
284
304
|
|
|
285
305
|
if (!assignments.has(agent)) {
|
|
@@ -344,10 +364,7 @@ export class ExecutionPlanner {
|
|
|
344
364
|
complexity = (complexity + task.estimatedComplexity) / 2;
|
|
345
365
|
}
|
|
346
366
|
|
|
347
|
-
|
|
348
|
-
if (task.dependencies) {
|
|
349
|
-
complexity += task.dependencies.length * 5;
|
|
350
|
-
}
|
|
367
|
+
complexity += dependencyCount(task.dependencies) * 5;
|
|
351
368
|
|
|
352
369
|
if (task.metadata?.memoryComplexityBoost !== undefined) {
|
|
353
370
|
complexity += task.metadata.memoryComplexityBoost;
|
|
@@ -376,7 +393,7 @@ export class ExecutionPlanner {
|
|
|
376
393
|
*/
|
|
377
394
|
private calculateParallelPotential(tasks: OrchestrationTask[]): number {
|
|
378
395
|
// Tasks without dependencies can run in parallel
|
|
379
|
-
const independent = tasks.filter(t => !t.dependencies
|
|
396
|
+
const independent = tasks.filter((t) => !isDependencyIdList(t.dependencies));
|
|
380
397
|
return independent.length / tasks.length;
|
|
381
398
|
}
|
|
382
399
|
}
|
|
@@ -14,6 +14,46 @@ import { getExecutionPlanner } from '../execution/execution-planner.js';
|
|
|
14
14
|
import { addObservations, extractComplexityObservations } from '../aside-context.js';
|
|
15
15
|
import type { OrchestrationTask, ComplexityAnalysis } from '../types.js';
|
|
16
16
|
|
|
17
|
+
type AnalyzeComplexityInput = {
|
|
18
|
+
id?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
files?: string[];
|
|
22
|
+
dependencyCount?: number;
|
|
23
|
+
dependencies?: string[] | number;
|
|
24
|
+
riskLevel?: string;
|
|
25
|
+
estimatedComplexity?: number;
|
|
26
|
+
priority?: OrchestrationTask['priority'];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function normalizeAnalyzeTasks(raw: AnalyzeComplexityInput[]): OrchestrationTask[] {
|
|
30
|
+
return raw.map((task, index) => {
|
|
31
|
+
let deps: OrchestrationTask['dependencies'];
|
|
32
|
+
if (Array.isArray(task.dependencies)) {
|
|
33
|
+
deps = task.dependencies;
|
|
34
|
+
} else if (typeof task.dependencies === 'number' && Number.isFinite(task.dependencies)) {
|
|
35
|
+
deps = task.dependencies;
|
|
36
|
+
} else if (
|
|
37
|
+
typeof task.dependencyCount === 'number' &&
|
|
38
|
+
Number.isFinite(task.dependencyCount)
|
|
39
|
+
) {
|
|
40
|
+
deps = task.dependencyCount;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const normalized: OrchestrationTask = {
|
|
44
|
+
id: task.id ?? `task-${index + 1}`,
|
|
45
|
+
description: task.description ?? '',
|
|
46
|
+
type: task.type ?? 'implement',
|
|
47
|
+
};
|
|
48
|
+
if (task.priority) normalized.priority = task.priority;
|
|
49
|
+
if (deps !== undefined) normalized.dependencies = deps;
|
|
50
|
+
if (task.estimatedComplexity !== undefined) {
|
|
51
|
+
normalized.estimatedComplexity = task.estimatedComplexity;
|
|
52
|
+
}
|
|
53
|
+
return normalized;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
17
57
|
/**
|
|
18
58
|
* Complexity Handler
|
|
19
59
|
* Processes analyze-complexity requests
|
|
@@ -25,10 +65,10 @@ export class ComplexityHandler {
|
|
|
25
65
|
* Handle analyze-complexity request
|
|
26
66
|
*/
|
|
27
67
|
async handleAnalyzeComplexity(
|
|
28
|
-
args: { tasks:
|
|
68
|
+
args: { tasks: AnalyzeComplexityInput[] },
|
|
29
69
|
asideId?: string,
|
|
30
70
|
): Promise<{ content: Array<{ type: string; text: string }> }> {
|
|
31
|
-
const
|
|
71
|
+
const tasks = normalizeAnalyzeTasks(args.tasks ?? []);
|
|
32
72
|
|
|
33
73
|
await frameworkLogger.log(
|
|
34
74
|
'orchestrator.server',
|
|
@@ -47,14 +87,32 @@ export class ComplexityHandler {
|
|
|
47
87
|
|
|
48
88
|
const primaryDescription = tasks.map((t) => t.description).filter(Boolean).join(' ');
|
|
49
89
|
const taskTypes = tasks.map((t) => t.type).filter(Boolean) as string[];
|
|
90
|
+
const planTaskInputs = tasks
|
|
91
|
+
.filter((t) => t.description?.trim())
|
|
92
|
+
.map((t) => ({ description: t.description, type: t.type }));
|
|
50
93
|
const leadDevPlan =
|
|
51
|
-
isLeadDevModeActive() && primaryDescription
|
|
52
|
-
? buildLeadDevPlan(
|
|
94
|
+
isLeadDevModeActive() && (primaryDescription || planTaskInputs.length > 0)
|
|
95
|
+
? buildLeadDevPlan(
|
|
96
|
+
primaryDescription,
|
|
97
|
+
taskTypes.length ? taskTypes : ['implement'],
|
|
98
|
+
planTaskInputs,
|
|
99
|
+
analysis.overallComplexity,
|
|
100
|
+
)
|
|
53
101
|
: null;
|
|
54
102
|
|
|
55
103
|
if (leadDevPlan) {
|
|
56
104
|
try {
|
|
57
|
-
persistLeadDevPlan(leadDevPlan);
|
|
105
|
+
const planPath = persistLeadDevPlan(leadDevPlan);
|
|
106
|
+
await frameworkLogger.log(
|
|
107
|
+
'orchestrator.server',
|
|
108
|
+
'lead-dev-plan-persisted',
|
|
109
|
+
'info',
|
|
110
|
+
{
|
|
111
|
+
planPath,
|
|
112
|
+
phaseCount: leadDevPlan.phases.length,
|
|
113
|
+
todoCount: leadDevPlan.phases.reduce((n, p) => n + p.todos.length, 0),
|
|
114
|
+
},
|
|
115
|
+
);
|
|
58
116
|
} catch (err) {
|
|
59
117
|
await frameworkLogger.log(
|
|
60
118
|
'orchestrator.server',
|
|
@@ -88,7 +88,6 @@ export class TaskHandler {
|
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// Execute orchestration (simulated for MCP server)
|
|
92
91
|
const results = await this.executePlan(executionPlan, sessionId, timeout);
|
|
93
92
|
|
|
94
93
|
// Feed per-task outcomes back to memory provider (not aggregate session stats)
|
|
@@ -118,6 +117,8 @@ export class TaskHandler {
|
|
|
118
117
|
orchestrationResult.success = results.success;
|
|
119
118
|
orchestrationResult.completedTasks = results.completedTasks;
|
|
120
119
|
orchestrationResult.failedTasks = results.failedTasks;
|
|
120
|
+
orchestrationResult.deferredTasks = results.deferredTasks;
|
|
121
|
+
orchestrationResult.recommendations.push(...results.recommendations);
|
|
121
122
|
orchestrationResult.agentUtilization = results.agentUtilization;
|
|
122
123
|
orchestrationResult.bottlenecks = results.bottlenecks;
|
|
123
124
|
orchestrationResult.recommendations = results.recommendations;
|
|
@@ -167,6 +168,7 @@ export class TaskHandler {
|
|
|
167
168
|
success: boolean;
|
|
168
169
|
completedTasks: number;
|
|
169
170
|
failedTasks: number;
|
|
171
|
+
deferredTasks: number;
|
|
170
172
|
agentUtilization: Record<string, number>;
|
|
171
173
|
bottlenecks: string[];
|
|
172
174
|
recommendations: string[];
|
|
@@ -181,6 +183,7 @@ export class TaskHandler {
|
|
|
181
183
|
const agentUtilization: Record<string, number> = {};
|
|
182
184
|
let completedTasks = 0;
|
|
183
185
|
let failedTasks = 0;
|
|
186
|
+
let deferredTasks = 0;
|
|
184
187
|
const agentOutputs: Record<string, string> = {};
|
|
185
188
|
const taskOutcomes: Array<{
|
|
186
189
|
taskId: string;
|
|
@@ -240,15 +243,20 @@ export class TaskHandler {
|
|
|
240
243
|
}
|
|
241
244
|
}
|
|
242
245
|
} else {
|
|
243
|
-
// For non-MCP agents we still count them as "assigned" (they will be handled by the caller or legacy path)
|
|
244
246
|
for (const task of tasks) {
|
|
245
|
-
|
|
247
|
+
deferredTasks++;
|
|
246
248
|
taskOutcomes.push({
|
|
247
249
|
taskId: task.id,
|
|
248
250
|
agent,
|
|
249
|
-
success:
|
|
251
|
+
success: false,
|
|
250
252
|
durationMs: 0,
|
|
251
253
|
});
|
|
254
|
+
await frameworkLogger.log(
|
|
255
|
+
'orchestrator.task-handler',
|
|
256
|
+
'delegate-deferred',
|
|
257
|
+
'info',
|
|
258
|
+
{ agent, taskId: task.id, sessionId },
|
|
259
|
+
);
|
|
252
260
|
}
|
|
253
261
|
}
|
|
254
262
|
}
|
|
@@ -256,6 +264,12 @@ export class TaskHandler {
|
|
|
256
264
|
const recommendations: string[] = [];
|
|
257
265
|
const bottlenecks: string[] = [];
|
|
258
266
|
|
|
267
|
+
if (deferredTasks > 0) {
|
|
268
|
+
recommendations.push(
|
|
269
|
+
`${deferredTasks} task(s) require host delegation (Task/spawn_subagent per lead-dev-plan) — orchestrate-task only invokes MCP consult skills directly`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
259
273
|
if (plan.agentAssignments.size > 5) {
|
|
260
274
|
recommendations.push('Consider reducing the number of agents for better coordination');
|
|
261
275
|
}
|
|
@@ -266,9 +280,10 @@ export class TaskHandler {
|
|
|
266
280
|
}
|
|
267
281
|
|
|
268
282
|
return {
|
|
269
|
-
success: failedTasks === 0,
|
|
283
|
+
success: failedTasks === 0 && deferredTasks === 0,
|
|
270
284
|
completedTasks,
|
|
271
285
|
failedTasks,
|
|
286
|
+
deferredTasks,
|
|
272
287
|
agentUtilization,
|
|
273
288
|
bottlenecks,
|
|
274
289
|
recommendations,
|
|
@@ -321,9 +336,10 @@ export class TaskHandler {
|
|
|
321
336
|
**Session ID:** ${result.sessionId}
|
|
322
337
|
**Success:** ${result.success ? '✅ COMPLETED' : '❌ FAILED'}
|
|
323
338
|
**Duration:** ${result.duration}ms
|
|
324
|
-
**Tasks:** ${result.completedTasks + result.failedTasks} total
|
|
339
|
+
**Tasks:** ${result.completedTasks + result.failedTasks + (result.deferredTasks ?? 0)} total
|
|
325
340
|
- ✅ Completed: ${result.completedTasks}
|
|
326
341
|
- ❌ Failed: ${result.failedTasks}
|
|
342
|
+
- ⏸ Deferred (host delegation required): ${result.deferredTasks ?? 0}
|
|
327
343
|
|
|
328
344
|
**Agent Utilization:**
|
|
329
345
|
${Object.entries(result.agentUtilization)
|
|
@@ -152,7 +152,15 @@ export class OrchestratorServer {
|
|
|
152
152
|
description: { type: 'string' },
|
|
153
153
|
type: { type: 'string' },
|
|
154
154
|
files: { type: 'array', items: { type: 'string' } },
|
|
155
|
-
|
|
155
|
+
dependencyCount: {
|
|
156
|
+
type: 'number',
|
|
157
|
+
description: 'Upstream dependency count (complexity hint only)',
|
|
158
|
+
},
|
|
159
|
+
dependencies: {
|
|
160
|
+
type: 'array',
|
|
161
|
+
items: { type: 'string' },
|
|
162
|
+
description: 'Task IDs this task depends on',
|
|
163
|
+
},
|
|
156
164
|
riskLevel: {
|
|
157
165
|
type: 'string',
|
|
158
166
|
enum: ['low', 'medium', 'high', 'critical'],
|
|
@@ -30,7 +30,8 @@ export interface OrchestrationTask {
|
|
|
30
30
|
description: string;
|
|
31
31
|
type: string;
|
|
32
32
|
priority?: 'critical' | 'high' | 'medium' | 'low';
|
|
33
|
-
|
|
33
|
+
/** Task IDs (orchestrate-task) or dependency count hint (analyze-complexity) */
|
|
34
|
+
dependencies?: string[] | number;
|
|
34
35
|
estimatedComplexity?: number;
|
|
35
36
|
metadata?: OrchestrationTaskMetadata;
|
|
36
37
|
}
|
|
@@ -48,6 +49,8 @@ export interface OrchestrationResult {
|
|
|
48
49
|
success: boolean;
|
|
49
50
|
completedTasks: number;
|
|
50
51
|
failedTasks: number;
|
|
52
|
+
/** Implementation agents not executed by orchestrate-task — lead must delegate via host Task */
|
|
53
|
+
deferredTasks?: number;
|
|
51
54
|
duration: number;
|
|
52
55
|
agentUtilization: Record<string, number>;
|
|
53
56
|
agentOutputs?: Record<string, string>;
|
|
@@ -488,7 +488,7 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
|
488
488
|
if (!vote) {
|
|
489
489
|
const abstainReason = memoryContext?.confidence.highConfidenceTrapPresent
|
|
490
490
|
? "No LLM governance provider configured. Repertoire detected a high-confidence ontological trap — route to the recommended agent before proceeding."
|
|
491
|
-
: "No LLM governance provider configured.
|
|
491
|
+
: "No LLM governance provider configured. Install Hermes on PATH (hermes -z) with xai-oauth, or set XRAY_GOVERNANCE_LLM_ENABLED=true + XRAY_LLM_ENDPOINT.";
|
|
492
492
|
|
|
493
493
|
return {
|
|
494
494
|
content: [
|
|
@@ -36,8 +36,9 @@ When `multi_agent_orchestration.lead_dev_mode` is true in `features.json` (defau
|
|
|
36
36
|
|
|
37
37
|
| Step | MCP tool | Notes |
|
|
38
38
|
|------|----------|-------|
|
|
39
|
-
| **Intake + classify** | `analyze-complexity` | Pass `tasks` array;
|
|
40
|
-
| Delegate | `orchestrate-task` |
|
|
39
|
+
| **Intake + classify** | `analyze-complexity` | Pass `tasks` array; persists `.xray/state/lead-dev-plan.json` when mode is on |
|
|
40
|
+
| Delegate (consult) | `orchestrate-task` | Invokes MCP consult skills (code-review, researcher, security-audit, etc.) |
|
|
41
|
+
| Delegate (implement) | **host `Task` / `spawn_subagent`** | `orchestrate-task` **defers** backend-engineer, frontend-engineer, bug-triage — lead must spawn |
|
|
41
42
|
| Monitor | `get-orchestration-status` | Lead updates todos |
|
|
42
43
|
| Major work | researcher + architect-tools + code-review | Auto-listed in plan when complexity > threshold |
|
|
43
44
|
|