0xray 3.2.0 → 3.3.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.
- package/.opencode/codex.codex +1 -1
- package/dist/CHANGELOG.md +26 -0
- package/dist/core/features-config.js +3 -0
- package/dist/mcps/orchestrator/config/agent-capabilities.js +30 -11
- package/dist/mcps/orchestrator/config/memory-routing-bridge.js +70 -0
- package/dist/mcps/orchestrator/execution/execution-planner.js +25 -7
- package/dist/mcps/orchestrator/handlers/task-handler.js +55 -2
- package/dist/memory-routing/index.js +4 -0
- package/dist/memory-routing/null-provider.js +51 -0
- package/dist/memory-routing/provider-loader.js +91 -0
- package/dist/memory-routing/provider-registry.js +40 -0
- package/dist/memory-routing/types.js +7 -0
- package/dist/memory-routing/validate-config.js +32 -0
- package/dist/nucleus/thin-dispatch.js +29 -1
- package/package.json +1 -1
- package/src/mcps/orchestrator/config/agent-capabilities.ts +49 -14
- package/src/mcps/orchestrator/config/memory-routing-bridge.ts +80 -0
- package/src/mcps/orchestrator/execution/execution-planner.ts +36 -9
- package/src/mcps/orchestrator/handlers/task-handler.ts +71 -2
- package/src/mcps/orchestrator/types.ts +13 -0
- package/src/opencode/codex.codex +1 -1
package/.opencode/codex.codex
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.
|
|
2
|
+
"version": "3.3.0",
|
|
3
3
|
"terms": [
|
|
4
4
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
|
5
5
|
],
|
package/dist/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Conventional Commits](https://www.conventionalcommits.org/).
|
|
6
6
|
|
|
7
|
+
## [3.2.0] - 2026-06-17
|
|
8
|
+
|
|
9
|
+
### 🔄 Changes
|
|
10
|
+
|
|
11
|
+
### ✨ Features
|
|
12
|
+
- Orphan file resolution: 5 dead files deleted, ~39 files integrated under typecheck
|
|
13
|
+
- feat: 58 type errors fixed across 6 source files
|
|
14
|
+
- feat: pre-existing e2e failures fixed (OpenClaw 9/9, Grok CLI 62/0)
|
|
15
|
+
- feat: vitest config testTimeout added for processor-logger stability
|
|
16
|
+
- feat: pre-tool-use hook rewritten from skeleton to full hook with resonance/decision-matrix/JSON
|
|
17
|
+
- feat: SelfProposalEngine integrated under typecheck with metamorphosis type support
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug Fixes
|
|
20
|
+
- fix: postinstall e2e version assertions (3.1.1 → 0.5.2)
|
|
21
|
+
- fix: AGENTS.md session entry and test assertions cleaned
|
|
22
|
+
- fix: enforcement-gate fallback dynamic imports `as string` cast
|
|
23
|
+
|
|
24
|
+
### ♻️ Refactoring
|
|
25
|
+
- chore: removed ~180 stale @version JSDoc tags from source files
|
|
26
|
+
- chore: cleaned stale version badges from README, AGENTS.md, docs-site docs
|
|
27
|
+
- chore: removed "xray 2.0" from command docs (26 files)
|
|
28
|
+
- chore: release.mjs checks npm view 0xray version (was xray)
|
|
29
|
+
- chore: release.mjs push target master → main
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
7
33
|
## [3.1.1] - 2026-06-17
|
|
8
34
|
|
|
9
35
|
### 🔄 Changes
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Capabilities Configuration
|
|
3
3
|
*
|
|
4
|
-
* Centralized agent capability definitions for orchestration decisions
|
|
4
|
+
* Centralized agent capability definitions for orchestration decisions.
|
|
5
|
+
* Enriched at runtime by the active MemoryRoutingProvider (Repertoire, etc.).
|
|
5
6
|
*/
|
|
6
7
|
import { AGENT_REGISTRY } from "../../../agents/registry.js";
|
|
8
|
+
import { initializeMemoryRouting } from '../../../memory-routing/index.js';
|
|
9
|
+
import { getProvider, toMemoryCapabilityMap, fromMemoryCapabilityMap, } from './memory-routing-bridge.js';
|
|
7
10
|
const DEFAULT_AGENT_CAPABILITIES = Object.fromEntries(Object.entries(AGENT_REGISTRY).map(([name, entry]) => [
|
|
8
11
|
name,
|
|
9
12
|
{
|
|
@@ -12,17 +15,28 @@ const DEFAULT_AGENT_CAPABILITIES = Object.fromEntries(Object.entries(AGENT_REGIS
|
|
|
12
15
|
concurrentTasks: entry.concurrentTasks,
|
|
13
16
|
},
|
|
14
17
|
]));
|
|
18
|
+
initializeMemoryRouting();
|
|
15
19
|
/**
|
|
16
20
|
* Agent Capabilities Manager
|
|
17
21
|
* Manages agent capabilities for orchestration decisions
|
|
18
22
|
*/
|
|
19
23
|
export class AgentCapabilitiesManager {
|
|
20
24
|
capabilities = new Map();
|
|
25
|
+
enriched = false;
|
|
21
26
|
constructor() {
|
|
22
|
-
// Initialize with default capabilities
|
|
23
27
|
Object.entries(DEFAULT_AGENT_CAPABILITIES).forEach(([agent, config]) => {
|
|
24
28
|
this.capabilities.set(agent, config);
|
|
25
29
|
});
|
|
30
|
+
this.applyMemoryRoutingEnrichment();
|
|
31
|
+
}
|
|
32
|
+
applyMemoryRoutingEnrichment() {
|
|
33
|
+
const provider = getProvider();
|
|
34
|
+
if (provider.id === 'null')
|
|
35
|
+
return;
|
|
36
|
+
const memoryCaps = toMemoryCapabilityMap(this.capabilities);
|
|
37
|
+
const enriched = provider.enhanceAgentCapabilities(memoryCaps);
|
|
38
|
+
this.capabilities = fromMemoryCapabilityMap(enriched);
|
|
39
|
+
this.enriched = true;
|
|
26
40
|
}
|
|
27
41
|
/**
|
|
28
42
|
* Get capabilities for a specific agent
|
|
@@ -34,6 +48,8 @@ export class AgentCapabilitiesManager {
|
|
|
34
48
|
* Get all registered agent capabilities
|
|
35
49
|
*/
|
|
36
50
|
getAllCapabilities() {
|
|
51
|
+
if (!this.enriched)
|
|
52
|
+
this.applyMemoryRoutingEnrichment();
|
|
37
53
|
return new Map(this.capabilities);
|
|
38
54
|
}
|
|
39
55
|
/**
|
|
@@ -43,19 +59,20 @@ export class AgentCapabilitiesManager {
|
|
|
43
59
|
this.capabilities.set(agentType, capability);
|
|
44
60
|
}
|
|
45
61
|
/**
|
|
46
|
-
* Select the best agent for a task based on complexity and
|
|
62
|
+
* Select the best agent for a task based on complexity, capabilities, and memory routing.
|
|
47
63
|
*/
|
|
48
|
-
selectAgentForTask(requiredCapabilities, complexity) {
|
|
64
|
+
selectAgentForTask(requiredCapabilities, complexity, operationDescription = '') {
|
|
65
|
+
const provider = getProvider();
|
|
66
|
+
if (provider.id !== 'null' && operationDescription) {
|
|
67
|
+
const memoryCaps = toMemoryCapabilityMap(this.getAllCapabilities());
|
|
68
|
+
return provider.selectAgent(memoryCaps, requiredCapabilities, complexity, operationDescription);
|
|
69
|
+
}
|
|
49
70
|
let bestAgent = null;
|
|
50
71
|
let bestScore = -1;
|
|
51
72
|
for (const [agent, caps] of this.capabilities) {
|
|
52
|
-
|
|
53
|
-
if (complexity > caps.complexityThreshold) {
|
|
73
|
+
if (complexity > caps.complexityThreshold)
|
|
54
74
|
continue;
|
|
55
|
-
|
|
56
|
-
// Calculate capability match score
|
|
57
|
-
const matchCount = requiredCapabilities.filter(cap => caps.capabilities.includes(cap)).length;
|
|
58
|
-
// Prefer agents with more concurrent tasks available
|
|
75
|
+
const matchCount = requiredCapabilities.filter((cap) => caps.capabilities.includes(cap)).length;
|
|
59
76
|
const score = matchCount * 10 + caps.concurrentTasks;
|
|
60
77
|
if (score > bestScore) {
|
|
61
78
|
bestScore = score;
|
|
@@ -77,7 +94,6 @@ export class AgentCapabilitiesManager {
|
|
|
77
94
|
return available;
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
|
-
// Singleton instance
|
|
81
97
|
let capabilitiesManagerInstance = null;
|
|
82
98
|
export function getAgentCapabilitiesManager() {
|
|
83
99
|
if (!capabilitiesManagerInstance) {
|
|
@@ -85,3 +101,6 @@ export function getAgentCapabilitiesManager() {
|
|
|
85
101
|
}
|
|
86
102
|
return capabilitiesManagerInstance;
|
|
87
103
|
}
|
|
104
|
+
export function resetAgentCapabilitiesManager() {
|
|
105
|
+
capabilitiesManagerInstance = null;
|
|
106
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge between orchestrator types and the pluggable MemoryRoutingProvider.
|
|
3
|
+
*/
|
|
4
|
+
import { getMemoryRoutingProviderSync, } from '../../../memory-routing/index.js';
|
|
5
|
+
export function toMemoryCapability(caps) {
|
|
6
|
+
const result = {
|
|
7
|
+
capabilities: caps.capabilities,
|
|
8
|
+
complexityThreshold: caps.complexityThreshold,
|
|
9
|
+
concurrentTasks: caps.concurrentTasks,
|
|
10
|
+
};
|
|
11
|
+
if (caps.memorySignals)
|
|
12
|
+
result.memorySignals = caps.memorySignals;
|
|
13
|
+
if (caps.memoryTags)
|
|
14
|
+
result.memoryTags = caps.memoryTags;
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
export function fromMemoryCapability(caps) {
|
|
18
|
+
const result = {
|
|
19
|
+
capabilities: caps.capabilities,
|
|
20
|
+
complexityThreshold: caps.complexityThreshold,
|
|
21
|
+
concurrentTasks: caps.concurrentTasks,
|
|
22
|
+
};
|
|
23
|
+
if (caps.memorySignals)
|
|
24
|
+
result.memorySignals = caps.memorySignals;
|
|
25
|
+
if (caps.memoryTags)
|
|
26
|
+
result.memoryTags = caps.memoryTags;
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
export function toMemoryTask(task) {
|
|
30
|
+
const result = {
|
|
31
|
+
id: task.id,
|
|
32
|
+
description: task.description,
|
|
33
|
+
type: task.type,
|
|
34
|
+
};
|
|
35
|
+
if (task.priority)
|
|
36
|
+
result.priority = task.priority;
|
|
37
|
+
if (task.dependencies)
|
|
38
|
+
result.dependencies = task.dependencies;
|
|
39
|
+
if (task.estimatedComplexity !== undefined)
|
|
40
|
+
result.estimatedComplexity = task.estimatedComplexity;
|
|
41
|
+
if (task.metadata)
|
|
42
|
+
result.metadata = task.metadata;
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
export function fromMemoryTask(task) {
|
|
46
|
+
const result = {
|
|
47
|
+
id: task.id,
|
|
48
|
+
description: task.description,
|
|
49
|
+
type: task.type,
|
|
50
|
+
};
|
|
51
|
+
if (task.priority)
|
|
52
|
+
result.priority = task.priority;
|
|
53
|
+
if (task.dependencies)
|
|
54
|
+
result.dependencies = task.dependencies;
|
|
55
|
+
if (task.estimatedComplexity !== undefined)
|
|
56
|
+
result.estimatedComplexity = task.estimatedComplexity;
|
|
57
|
+
const metadata = task.metadata;
|
|
58
|
+
if (metadata)
|
|
59
|
+
result.metadata = metadata;
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
export function toMemoryCapabilityMap(caps) {
|
|
63
|
+
return new Map(Array.from(caps.entries()).map(([k, v]) => [k, toMemoryCapability(v)]));
|
|
64
|
+
}
|
|
65
|
+
export function fromMemoryCapabilityMap(caps) {
|
|
66
|
+
return new Map(Array.from(caps.entries()).map(([k, v]) => [k, fromMemoryCapability(v)]));
|
|
67
|
+
}
|
|
68
|
+
export function getProvider() {
|
|
69
|
+
return getMemoryRoutingProviderSync();
|
|
70
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Creates optimized execution plans for orchestration tasks
|
|
5
5
|
*/
|
|
6
6
|
import { getAgentCapabilitiesManager } from '../config/agent-capabilities.js';
|
|
7
|
+
import { getProvider, toMemoryTask, fromMemoryTask, } from '../config/memory-routing-bridge.js';
|
|
7
8
|
/**
|
|
8
9
|
* Execution Planner
|
|
9
10
|
* Plans and optimizes task execution strategies
|
|
@@ -48,26 +49,35 @@ export class ExecutionPlanner {
|
|
|
48
49
|
* Create an execution plan based on tasks and mode
|
|
49
50
|
*/
|
|
50
51
|
async createExecutionPlan(tasks, executionMode) {
|
|
52
|
+
const provider = getProvider();
|
|
53
|
+
const memoryTasks = provider.enrichTasks(tasks.map(toMemoryTask));
|
|
54
|
+
const enrichedTasks = memoryTasks.map(fromMemoryTask);
|
|
51
55
|
const agentAssignments = new Map();
|
|
52
56
|
switch (executionMode) {
|
|
53
57
|
case 'parallel':
|
|
54
|
-
this.createParallelPlan(
|
|
58
|
+
this.createParallelPlan(enrichedTasks, agentAssignments);
|
|
55
59
|
break;
|
|
56
60
|
case 'sequential':
|
|
57
|
-
this.createSequentialPlan(
|
|
61
|
+
this.createSequentialPlan(enrichedTasks, agentAssignments);
|
|
58
62
|
break;
|
|
59
63
|
case 'optimized':
|
|
60
64
|
default:
|
|
61
|
-
await this.createOptimizedPlan(
|
|
65
|
+
await this.createOptimizedPlan(enrichedTasks, agentAssignments);
|
|
62
66
|
break;
|
|
63
67
|
}
|
|
64
68
|
const estimatedDuration = this.estimateExecutionDuration(agentAssignments);
|
|
65
|
-
|
|
66
|
-
tasks,
|
|
69
|
+
const plan = {
|
|
70
|
+
tasks: enrichedTasks,
|
|
67
71
|
strategy: executionMode,
|
|
68
72
|
agentAssignments,
|
|
69
73
|
estimatedDuration,
|
|
70
74
|
};
|
|
75
|
+
if (provider.id !== 'null') {
|
|
76
|
+
plan.memoryContext = {
|
|
77
|
+
...provider.buildInheritedContext(memoryTasks),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return plan;
|
|
71
81
|
}
|
|
72
82
|
/**
|
|
73
83
|
* Create a parallel execution plan
|
|
@@ -150,7 +160,11 @@ export class ExecutionPlanner {
|
|
|
150
160
|
assignTasksToAgents(tasks, agentAssignments) {
|
|
151
161
|
for (const task of tasks) {
|
|
152
162
|
const complexity = task.estimatedComplexity || 30;
|
|
153
|
-
const
|
|
163
|
+
const requiredCaps = [
|
|
164
|
+
task.type,
|
|
165
|
+
...(task.metadata?.memorySignals ?? []),
|
|
166
|
+
];
|
|
167
|
+
const agent = this.capabilitiesManager.selectAgentForTask(requiredCaps, complexity, task.description) || 'orchestrator';
|
|
154
168
|
if (!agentAssignments.has(agent)) {
|
|
155
169
|
agentAssignments.set(agent, []);
|
|
156
170
|
}
|
|
@@ -188,7 +202,11 @@ export class ExecutionPlanner {
|
|
|
188
202
|
category: task.type,
|
|
189
203
|
});
|
|
190
204
|
// Assign to agent
|
|
191
|
-
const
|
|
205
|
+
const requiredCaps = [
|
|
206
|
+
task.type,
|
|
207
|
+
...(task.metadata?.memorySignals ?? []),
|
|
208
|
+
];
|
|
209
|
+
const agent = this.capabilitiesManager.selectAgentForTask(requiredCaps, complexity, task.description) || 'orchestrator';
|
|
192
210
|
if (!assignments.has(agent)) {
|
|
193
211
|
assignments.set(agent, []);
|
|
194
212
|
}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { frameworkLogger } from '../../../core/framework-logger.js';
|
|
7
7
|
import { getExecutionPlanner } from '../execution/execution-planner.js';
|
|
8
8
|
import { mcpClientManager } from '../../../mcps/mcp-client.js';
|
|
9
|
-
import { addObservations, extractOrchestrationObservations } from '../aside-context.js';
|
|
9
|
+
import { addObservations, extractOrchestrationObservations, spawnAside } from '../aside-context.js';
|
|
10
|
+
import { getProvider } from '../config/memory-routing-bridge.js';
|
|
10
11
|
/**
|
|
11
12
|
* Task Handler
|
|
12
13
|
* Processes orchestrate-task requests
|
|
@@ -38,8 +39,37 @@ export class TaskHandler {
|
|
|
38
39
|
}
|
|
39
40
|
// Analyze complexity and create execution plan
|
|
40
41
|
const executionPlan = await this.planner.createExecutionPlan(tasks, executionMode);
|
|
42
|
+
const memoryProvider = getProvider();
|
|
43
|
+
if (memoryProvider.id !== 'null' && executionPlan.memoryContext) {
|
|
44
|
+
await spawnAside({
|
|
45
|
+
description: `Orchestrate: ${description}`,
|
|
46
|
+
sessionId,
|
|
47
|
+
inheritedContext: {
|
|
48
|
+
memoryRouting: executionPlan.memoryContext,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
41
52
|
// Execute orchestration (simulated for MCP server)
|
|
42
53
|
const results = await this.executePlan(executionPlan, sessionId, timeout);
|
|
54
|
+
// Feed per-task outcomes back to memory provider (not aggregate session stats)
|
|
55
|
+
if (memoryProvider.ingestFeedback) {
|
|
56
|
+
const outcomeByKey = new Map(results.taskOutcomes.map((o) => [`${o.agent}:${o.taskId}`, o]));
|
|
57
|
+
for (const [agent, assignedTasks] of executionPlan.agentAssignments) {
|
|
58
|
+
for (const task of assignedTasks) {
|
|
59
|
+
const outcome = outcomeByKey.get(`${agent}:${task.id}`);
|
|
60
|
+
memoryProvider.ingestFeedback({
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
sessionId,
|
|
63
|
+
taskId: task.id,
|
|
64
|
+
assignedAgent: agent,
|
|
65
|
+
memorySignals: task.metadata?.memorySignals ?? [],
|
|
66
|
+
complexity: task.estimatedComplexity ?? 30,
|
|
67
|
+
success: outcome?.success ?? false,
|
|
68
|
+
durationMs: outcome?.durationMs ?? 0,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
43
73
|
// Update result
|
|
44
74
|
orchestrationResult.success = results.success;
|
|
45
75
|
orchestrationResult.completedTasks = results.completedTasks;
|
|
@@ -82,6 +112,7 @@ export class TaskHandler {
|
|
|
82
112
|
let completedTasks = 0;
|
|
83
113
|
let failedTasks = 0;
|
|
84
114
|
const agentOutputs = {};
|
|
115
|
+
const taskOutcomes = [];
|
|
85
116
|
const skillServers = new Set([
|
|
86
117
|
'code-review', 'code-reviewer',
|
|
87
118
|
'security-audit', 'security-auditor',
|
|
@@ -96,6 +127,7 @@ export class TaskHandler {
|
|
|
96
127
|
agentUtilization[agent] = tasks.length;
|
|
97
128
|
if (skillServers.has(agent)) {
|
|
98
129
|
for (const task of tasks) {
|
|
130
|
+
const taskStart = Date.now();
|
|
99
131
|
try {
|
|
100
132
|
const toolName = this.mapAgentToTool(agent);
|
|
101
133
|
const result = await mcpClientManager.callServerTool(agent, toolName, {
|
|
@@ -106,10 +138,22 @@ export class TaskHandler {
|
|
|
106
138
|
const outputText = this.extractTextFromMcpResult(result);
|
|
107
139
|
agentOutputs[`${agent}:${task.id}`] = outputText;
|
|
108
140
|
completedTasks++;
|
|
141
|
+
taskOutcomes.push({
|
|
142
|
+
taskId: task.id,
|
|
143
|
+
agent,
|
|
144
|
+
success: true,
|
|
145
|
+
durationMs: Date.now() - taskStart,
|
|
146
|
+
});
|
|
109
147
|
}
|
|
110
148
|
catch (err) {
|
|
111
149
|
failedTasks++;
|
|
112
150
|
agentOutputs[`${agent}:${task.id}`] = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
151
|
+
taskOutcomes.push({
|
|
152
|
+
taskId: task.id,
|
|
153
|
+
agent,
|
|
154
|
+
success: false,
|
|
155
|
+
durationMs: Date.now() - taskStart,
|
|
156
|
+
});
|
|
113
157
|
frameworkLogger.log('orchestrator.task-handler', 'task-execution-failed', 'warning', {
|
|
114
158
|
agent,
|
|
115
159
|
taskId: task.id,
|
|
@@ -120,7 +164,15 @@ export class TaskHandler {
|
|
|
120
164
|
}
|
|
121
165
|
else {
|
|
122
166
|
// For non-MCP agents we still count them as "assigned" (they will be handled by the caller or legacy path)
|
|
123
|
-
|
|
167
|
+
for (const task of tasks) {
|
|
168
|
+
completedTasks++;
|
|
169
|
+
taskOutcomes.push({
|
|
170
|
+
taskId: task.id,
|
|
171
|
+
agent,
|
|
172
|
+
success: true,
|
|
173
|
+
durationMs: 0,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
124
176
|
}
|
|
125
177
|
}
|
|
126
178
|
const recommendations = [];
|
|
@@ -141,6 +193,7 @@ export class TaskHandler {
|
|
|
141
193
|
bottlenecks,
|
|
142
194
|
recommendations,
|
|
143
195
|
agentOutputs,
|
|
196
|
+
taskOutcomes,
|
|
144
197
|
};
|
|
145
198
|
}
|
|
146
199
|
mapAgentToTool(agent) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getMemoryRoutingProvider, getMemoryRoutingProviderSync, getMemoryRoutingConfig, resetMemoryRoutingProvider, initializeMemoryRouting, } from './provider-registry.js';
|
|
2
|
+
export { NullMemoryRoutingProvider, createMemoryRoutingProvider } from './null-provider.js';
|
|
3
|
+
export { loadMemoryRoutingProvider } from './provider-loader.js';
|
|
4
|
+
export { validateMemoryRoutingConfig } from './validate-config.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const EMPTY_CONTEXT = {
|
|
2
|
+
providerId: 'null',
|
|
3
|
+
matchedSignals: [],
|
|
4
|
+
matchedTags: [],
|
|
5
|
+
flags: {},
|
|
6
|
+
synthesisAvailable: false,
|
|
7
|
+
};
|
|
8
|
+
export class NullMemoryRoutingProvider {
|
|
9
|
+
id = 'null';
|
|
10
|
+
name = 'Null (no memory routing)';
|
|
11
|
+
isAvailable() {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
buildRoutingContext() {
|
|
15
|
+
return { ...EMPTY_CONTEXT };
|
|
16
|
+
}
|
|
17
|
+
enhanceAgentCapabilities(base) {
|
|
18
|
+
return new Map(base);
|
|
19
|
+
}
|
|
20
|
+
enrichTasks(tasks) {
|
|
21
|
+
return tasks;
|
|
22
|
+
}
|
|
23
|
+
buildInheritedContext() {
|
|
24
|
+
return { providerId: 'null', matchedSignals: [], flags: {} };
|
|
25
|
+
}
|
|
26
|
+
selectAgent(capabilities, requiredCapabilities, complexity) {
|
|
27
|
+
let bestAgent = null;
|
|
28
|
+
let bestScore = -1;
|
|
29
|
+
for (const [agent, caps] of capabilities) {
|
|
30
|
+
if (complexity > caps.complexityThreshold)
|
|
31
|
+
continue;
|
|
32
|
+
const matchCount = requiredCapabilities.filter((c) => caps.capabilities.includes(c)).length;
|
|
33
|
+
const score = matchCount * 10 + caps.concurrentTasks;
|
|
34
|
+
if (score > bestScore) {
|
|
35
|
+
bestScore = score;
|
|
36
|
+
bestAgent = agent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return bestAgent;
|
|
40
|
+
}
|
|
41
|
+
resolveThinDispatch(baseAgent, _operation, complexityScore) {
|
|
42
|
+
return {
|
|
43
|
+
agent: baseAgent,
|
|
44
|
+
adjustedScore: complexityScore,
|
|
45
|
+
context: { ...EMPTY_CONTEXT },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function createMemoryRoutingProvider() {
|
|
50
|
+
return new NullMemoryRoutingProvider();
|
|
51
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { frameworkLogger } from '../core/framework-logger.js';
|
|
5
|
+
import { createMemoryRoutingProvider as createNullProvider } from './null-provider.js';
|
|
6
|
+
import { validateMemoryRoutingConfig } from './validate-config.js';
|
|
7
|
+
const REPERTOIRE_CANDIDATE_PATHS = [
|
|
8
|
+
'../repertoire/dist/provider/memory-routing-provider.js',
|
|
9
|
+
'../../repertoire/dist/provider/memory-routing-provider.js',
|
|
10
|
+
'../../../repertoire/dist/provider/memory-routing-provider.js',
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* SECURITY: features.json is a trusted configuration boundary.
|
|
14
|
+
* module_path is resolved from disk and dynamically imported without signature
|
|
15
|
+
* verification. Only grant write access to features.json to trusted operators.
|
|
16
|
+
*/
|
|
17
|
+
function resolveModulePath(configuredPath, cwd) {
|
|
18
|
+
if (!configuredPath)
|
|
19
|
+
return null;
|
|
20
|
+
const candidates = [
|
|
21
|
+
resolve(cwd, configuredPath),
|
|
22
|
+
configuredPath,
|
|
23
|
+
];
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
if (existsSync(candidate))
|
|
26
|
+
return candidate;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function defaultPathForProvider(provider, cwd) {
|
|
31
|
+
if (provider !== 'repertoire')
|
|
32
|
+
return null;
|
|
33
|
+
for (const rel of REPERTOIRE_CANDIDATE_PATHS) {
|
|
34
|
+
const abs = resolve(cwd, rel);
|
|
35
|
+
if (existsSync(abs))
|
|
36
|
+
return abs;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
export async function loadMemoryRoutingProvider(config, cwd = process.cwd()) {
|
|
41
|
+
const validation = validateMemoryRoutingConfig(config);
|
|
42
|
+
if (!validation.valid) {
|
|
43
|
+
await frameworkLogger.log('memory-routing', 'config-invalid', 'error', { errors: validation.errors, provider: config.provider });
|
|
44
|
+
return createNullProvider();
|
|
45
|
+
}
|
|
46
|
+
const effective = validation.normalized;
|
|
47
|
+
if (!effective.enabled || effective.provider === 'null') {
|
|
48
|
+
return createNullProvider();
|
|
49
|
+
}
|
|
50
|
+
const modulePath = resolveModulePath(effective.module_path, cwd) ??
|
|
51
|
+
defaultPathForProvider(effective.provider, cwd);
|
|
52
|
+
if (!modulePath) {
|
|
53
|
+
await frameworkLogger.log('memory-routing', 'provider-not-found', 'error', {
|
|
54
|
+
provider: effective.provider,
|
|
55
|
+
module_path: effective.module_path,
|
|
56
|
+
message: `Provider "${effective.provider}" is configured but module_path could not be resolved. Falling back to null provider.`,
|
|
57
|
+
});
|
|
58
|
+
return createNullProvider();
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const mod = await import(pathToFileURL(modulePath).href);
|
|
62
|
+
const factory = mod.createMemoryRoutingProvider ??
|
|
63
|
+
mod.default?.createMemoryRoutingProvider;
|
|
64
|
+
if (typeof factory !== 'function') {
|
|
65
|
+
throw new Error(`Module ${modulePath} must export createMemoryRoutingProvider()`);
|
|
66
|
+
}
|
|
67
|
+
const provider = factory((effective.config ?? {}));
|
|
68
|
+
if (!provider?.id || typeof provider.isAvailable !== 'function') {
|
|
69
|
+
throw new Error(`Invalid memory routing provider from ${modulePath}`);
|
|
70
|
+
}
|
|
71
|
+
if (!provider.isAvailable()) {
|
|
72
|
+
await frameworkLogger.log('memory-routing', 'provider-unavailable', 'error', {
|
|
73
|
+
providerId: provider.id,
|
|
74
|
+
modulePath,
|
|
75
|
+
message: `Provider "${provider.id}" loaded but isAvailable() returned false. Falling back to null provider.`,
|
|
76
|
+
});
|
|
77
|
+
return createNullProvider();
|
|
78
|
+
}
|
|
79
|
+
await frameworkLogger.log('memory-routing', 'provider-loaded', 'info', { providerId: provider.id, name: provider.name, modulePath });
|
|
80
|
+
return provider;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
await frameworkLogger.log('memory-routing', 'provider-load-failed', 'error', {
|
|
84
|
+
modulePath,
|
|
85
|
+
error: err instanceof Error ? err.message : String(err),
|
|
86
|
+
cwd,
|
|
87
|
+
configDir: dirname(modulePath),
|
|
88
|
+
});
|
|
89
|
+
return createNullProvider();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { featuresConfigLoader } from '../core/features-config.js';
|
|
2
|
+
import { NullMemoryRoutingProvider } from './null-provider.js';
|
|
3
|
+
import { loadMemoryRoutingProvider } from './provider-loader.js';
|
|
4
|
+
import { validateMemoryRoutingConfig } from './validate-config.js';
|
|
5
|
+
let cachedProvider = null;
|
|
6
|
+
let loadPromise = null;
|
|
7
|
+
const nullProviderSingleton = new NullMemoryRoutingProvider();
|
|
8
|
+
export function getMemoryRoutingConfig() {
|
|
9
|
+
const raw = featuresConfigLoader.loadConfig().memory_routing;
|
|
10
|
+
const validation = validateMemoryRoutingConfig(raw ?? { enabled: false, provider: 'null' });
|
|
11
|
+
return validation.normalized;
|
|
12
|
+
}
|
|
13
|
+
export async function getMemoryRoutingProvider(forceReload = false) {
|
|
14
|
+
if (!forceReload && cachedProvider)
|
|
15
|
+
return cachedProvider;
|
|
16
|
+
if (!forceReload && loadPromise)
|
|
17
|
+
return loadPromise;
|
|
18
|
+
const config = getMemoryRoutingConfig();
|
|
19
|
+
loadPromise = loadMemoryRoutingProvider(config).then((provider) => {
|
|
20
|
+
cachedProvider = provider;
|
|
21
|
+
loadPromise = null;
|
|
22
|
+
return provider;
|
|
23
|
+
});
|
|
24
|
+
return loadPromise;
|
|
25
|
+
}
|
|
26
|
+
/** Synchronous accessor — returns shared null singleton until async load completes. */
|
|
27
|
+
export function getMemoryRoutingProviderSync() {
|
|
28
|
+
return cachedProvider ?? nullProviderSingleton;
|
|
29
|
+
}
|
|
30
|
+
export function resetMemoryRoutingProvider() {
|
|
31
|
+
cachedProvider = null;
|
|
32
|
+
loadPromise = null;
|
|
33
|
+
}
|
|
34
|
+
/** Kick off background load at module init. */
|
|
35
|
+
export function initializeMemoryRouting() {
|
|
36
|
+
const config = getMemoryRoutingConfig();
|
|
37
|
+
if (config.enabled) {
|
|
38
|
+
void getMemoryRoutingProvider();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const VALID_PROVIDERS = new Set(['null', 'repertoire', 'custom']);
|
|
2
|
+
export function validateMemoryRoutingConfig(raw) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
const input = (raw ?? {});
|
|
5
|
+
const enabled = input.enabled === true;
|
|
6
|
+
const provider = typeof input.provider === 'string' ? input.provider : 'null';
|
|
7
|
+
if (!VALID_PROVIDERS.has(provider)) {
|
|
8
|
+
errors.push(`Invalid memory_routing.provider "${provider}". Must be one of: null, repertoire, custom.`);
|
|
9
|
+
}
|
|
10
|
+
if (enabled && provider !== 'null' && !input.module_path) {
|
|
11
|
+
errors.push(`memory_routing.provider "${provider}" requires module_path when enabled.`);
|
|
12
|
+
}
|
|
13
|
+
if (input.module_path !== undefined && typeof input.module_path !== 'string') {
|
|
14
|
+
errors.push('memory_routing.module_path must be a string.');
|
|
15
|
+
}
|
|
16
|
+
if (input.config !== undefined && (typeof input.config !== 'object' || input.config === null)) {
|
|
17
|
+
errors.push('memory_routing.config must be an object.');
|
|
18
|
+
}
|
|
19
|
+
const normalized = {
|
|
20
|
+
enabled,
|
|
21
|
+
provider: VALID_PROVIDERS.has(provider)
|
|
22
|
+
? provider
|
|
23
|
+
: 'null',
|
|
24
|
+
};
|
|
25
|
+
if (typeof input.module_path === 'string') {
|
|
26
|
+
normalized.module_path = input.module_path;
|
|
27
|
+
}
|
|
28
|
+
if (input.config && typeof input.config === 'object') {
|
|
29
|
+
normalized.config = input.config;
|
|
30
|
+
}
|
|
31
|
+
return { valid: errors.length === 0, errors, normalized };
|
|
32
|
+
}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
10
10
|
import { getLevelFromScore, getAgentForTier, levelToTier, } from "../delegation/complexity-core.js";
|
|
11
11
|
import { ComplexityAnalyzer } from "../delegation/complexity-analyzer.js";
|
|
12
|
+
import { getMemoryRoutingProviderSync, initializeMemoryRouting, } from "../memory-routing/index.js";
|
|
13
|
+
initializeMemoryRouting();
|
|
12
14
|
export const NUCLEUS_THIN_DISPATCH_VERSION = "0.1.0";
|
|
13
15
|
/**
|
|
14
16
|
* Score the complexity of an operation given its description and context.
|
|
@@ -52,6 +54,32 @@ export function routeToAgent(scoreOrLevel, level) {
|
|
|
52
54
|
*/
|
|
53
55
|
export function scoreAndRoute(operation, context, thresholds) {
|
|
54
56
|
const score = scoreComplexity(operation, context, thresholds);
|
|
55
|
-
|
|
57
|
+
let agent = routeToAgent(score);
|
|
58
|
+
const provider = getMemoryRoutingProviderSync();
|
|
59
|
+
let memoryRouting;
|
|
60
|
+
if (provider.id !== "null") {
|
|
61
|
+
const resolved = provider.resolveThinDispatch(agent, operation, score.score);
|
|
62
|
+
agent = resolved.agent;
|
|
63
|
+
memoryRouting = {
|
|
64
|
+
providerId: resolved.context.providerId,
|
|
65
|
+
adjustedScore: resolved.adjustedScore,
|
|
66
|
+
signals: resolved.context.matchedSignals,
|
|
67
|
+
};
|
|
68
|
+
const adjustedLevel = getLevelFromScore(resolved.adjustedScore, thresholds);
|
|
69
|
+
frameworkLogger.log("nucleus-thin-dispatch", "memory-routing", "info", {
|
|
70
|
+
providerId: resolved.context.providerId,
|
|
71
|
+
baseAgent: routeToAgent(score),
|
|
72
|
+
resolvedAgent: agent,
|
|
73
|
+
baseScore: score.score,
|
|
74
|
+
adjustedScore: resolved.adjustedScore,
|
|
75
|
+
level: adjustedLevel,
|
|
76
|
+
signals: resolved.context.matchedSignals,
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
score: { ...score, score: resolved.adjustedScore, level: adjustedLevel },
|
|
80
|
+
agent,
|
|
81
|
+
memoryRouting,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
56
84
|
return { score, agent };
|
|
57
85
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Capabilities Configuration
|
|
3
|
-
*
|
|
4
|
-
* Centralized agent capability definitions for orchestration decisions
|
|
3
|
+
*
|
|
4
|
+
* Centralized agent capability definitions for orchestration decisions.
|
|
5
|
+
* Enriched at runtime by the active MemoryRoutingProvider (Repertoire, etc.).
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import type { AgentCapability } from '../types.js';
|
|
8
9
|
import { AGENT_REGISTRY } from "../../../agents/registry.js";
|
|
10
|
+
import { initializeMemoryRouting } from '../../../memory-routing/index.js';
|
|
11
|
+
import {
|
|
12
|
+
getProvider,
|
|
13
|
+
toMemoryCapabilityMap,
|
|
14
|
+
fromMemoryCapabilityMap,
|
|
15
|
+
} from './memory-routing-bridge.js';
|
|
9
16
|
|
|
10
17
|
const DEFAULT_AGENT_CAPABILITIES: Record<string, AgentCapability> = Object.fromEntries(
|
|
11
18
|
Object.entries(AGENT_REGISTRY).map(([name, entry]) => [
|
|
@@ -18,18 +25,31 @@ const DEFAULT_AGENT_CAPABILITIES: Record<string, AgentCapability> = Object.fromE
|
|
|
18
25
|
]),
|
|
19
26
|
);
|
|
20
27
|
|
|
28
|
+
initializeMemoryRouting();
|
|
29
|
+
|
|
21
30
|
/**
|
|
22
31
|
* Agent Capabilities Manager
|
|
23
32
|
* Manages agent capabilities for orchestration decisions
|
|
24
33
|
*/
|
|
25
34
|
export class AgentCapabilitiesManager {
|
|
26
35
|
private capabilities: Map<string, AgentCapability> = new Map();
|
|
36
|
+
private enriched = false;
|
|
27
37
|
|
|
28
38
|
constructor() {
|
|
29
|
-
// Initialize with default capabilities
|
|
30
39
|
Object.entries(DEFAULT_AGENT_CAPABILITIES).forEach(([agent, config]) => {
|
|
31
40
|
this.capabilities.set(agent, config);
|
|
32
41
|
});
|
|
42
|
+
this.applyMemoryRoutingEnrichment();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private applyMemoryRoutingEnrichment(): void {
|
|
46
|
+
const provider = getProvider();
|
|
47
|
+
if (provider.id === 'null') return;
|
|
48
|
+
|
|
49
|
+
const memoryCaps = toMemoryCapabilityMap(this.capabilities);
|
|
50
|
+
const enriched = provider.enhanceAgentCapabilities(memoryCaps);
|
|
51
|
+
this.capabilities = fromMemoryCapabilityMap(enriched);
|
|
52
|
+
this.enriched = true;
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
/**
|
|
@@ -43,6 +63,7 @@ export class AgentCapabilitiesManager {
|
|
|
43
63
|
* Get all registered agent capabilities
|
|
44
64
|
*/
|
|
45
65
|
getAllCapabilities(): Map<string, AgentCapability> {
|
|
66
|
+
if (!this.enriched) this.applyMemoryRoutingEnrichment();
|
|
46
67
|
return new Map(this.capabilities);
|
|
47
68
|
}
|
|
48
69
|
|
|
@@ -54,24 +75,35 @@ export class AgentCapabilitiesManager {
|
|
|
54
75
|
}
|
|
55
76
|
|
|
56
77
|
/**
|
|
57
|
-
* Select the best agent for a task based on complexity and
|
|
78
|
+
* Select the best agent for a task based on complexity, capabilities, and memory routing.
|
|
58
79
|
*/
|
|
59
|
-
selectAgentForTask(
|
|
80
|
+
selectAgentForTask(
|
|
81
|
+
requiredCapabilities: string[],
|
|
82
|
+
complexity: number,
|
|
83
|
+
operationDescription = '',
|
|
84
|
+
): string | null {
|
|
85
|
+
const provider = getProvider();
|
|
86
|
+
|
|
87
|
+
if (provider.id !== 'null' && operationDescription) {
|
|
88
|
+
const memoryCaps = toMemoryCapabilityMap(this.getAllCapabilities());
|
|
89
|
+
return provider.selectAgent(
|
|
90
|
+
memoryCaps,
|
|
91
|
+
requiredCapabilities,
|
|
92
|
+
complexity,
|
|
93
|
+
operationDescription,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
60
97
|
let bestAgent: string | null = null;
|
|
61
98
|
let bestScore = -1;
|
|
62
99
|
|
|
63
100
|
for (const [agent, caps] of this.capabilities) {
|
|
64
|
-
|
|
65
|
-
if (complexity > caps.complexityThreshold) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
101
|
+
if (complexity > caps.complexityThreshold) continue;
|
|
68
102
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
caps.capabilities.includes(cap)
|
|
103
|
+
const matchCount = requiredCapabilities.filter((cap) =>
|
|
104
|
+
caps.capabilities.includes(cap),
|
|
72
105
|
).length;
|
|
73
106
|
|
|
74
|
-
// Prefer agents with more concurrent tasks available
|
|
75
107
|
const score = matchCount * 10 + caps.concurrentTasks;
|
|
76
108
|
|
|
77
109
|
if (score > bestScore) {
|
|
@@ -97,7 +129,6 @@ export class AgentCapabilitiesManager {
|
|
|
97
129
|
}
|
|
98
130
|
}
|
|
99
131
|
|
|
100
|
-
// Singleton instance
|
|
101
132
|
let capabilitiesManagerInstance: AgentCapabilitiesManager | null = null;
|
|
102
133
|
|
|
103
134
|
export function getAgentCapabilitiesManager(): AgentCapabilitiesManager {
|
|
@@ -106,3 +137,7 @@ export function getAgentCapabilitiesManager(): AgentCapabilitiesManager {
|
|
|
106
137
|
}
|
|
107
138
|
return capabilitiesManagerInstance;
|
|
108
139
|
}
|
|
140
|
+
|
|
141
|
+
export function resetAgentCapabilitiesManager(): void {
|
|
142
|
+
capabilitiesManagerInstance = null;
|
|
143
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge between orchestrator types and the pluggable MemoryRoutingProvider.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getMemoryRoutingProviderSync,
|
|
7
|
+
type MemoryAgentCapability,
|
|
8
|
+
type MemoryOrchestrationTask,
|
|
9
|
+
type MemoryRoutingProvider,
|
|
10
|
+
} from '../../../memory-routing/index.js';
|
|
11
|
+
import type { AgentCapability, OrchestrationTask } from '../types.js';
|
|
12
|
+
|
|
13
|
+
export function toMemoryCapability(caps: AgentCapability): MemoryAgentCapability {
|
|
14
|
+
const result: MemoryAgentCapability = {
|
|
15
|
+
capabilities: caps.capabilities,
|
|
16
|
+
complexityThreshold: caps.complexityThreshold,
|
|
17
|
+
concurrentTasks: caps.concurrentTasks,
|
|
18
|
+
};
|
|
19
|
+
if (caps.memorySignals) result.memorySignals = caps.memorySignals;
|
|
20
|
+
if (caps.memoryTags) result.memoryTags = caps.memoryTags;
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function fromMemoryCapability(caps: MemoryAgentCapability): AgentCapability {
|
|
25
|
+
const result: AgentCapability = {
|
|
26
|
+
capabilities: caps.capabilities,
|
|
27
|
+
complexityThreshold: caps.complexityThreshold,
|
|
28
|
+
concurrentTasks: caps.concurrentTasks,
|
|
29
|
+
};
|
|
30
|
+
if (caps.memorySignals) result.memorySignals = caps.memorySignals;
|
|
31
|
+
if (caps.memoryTags) result.memoryTags = caps.memoryTags;
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function toMemoryTask(task: OrchestrationTask): MemoryOrchestrationTask {
|
|
36
|
+
const result: MemoryOrchestrationTask = {
|
|
37
|
+
id: task.id,
|
|
38
|
+
description: task.description,
|
|
39
|
+
type: task.type,
|
|
40
|
+
};
|
|
41
|
+
if (task.priority) result.priority = task.priority;
|
|
42
|
+
if (task.dependencies) result.dependencies = task.dependencies;
|
|
43
|
+
if (task.estimatedComplexity !== undefined) result.estimatedComplexity = task.estimatedComplexity;
|
|
44
|
+
if (task.metadata) result.metadata = task.metadata as Record<string, unknown>;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function fromMemoryTask(task: MemoryOrchestrationTask): OrchestrationTask {
|
|
49
|
+
const result: OrchestrationTask = {
|
|
50
|
+
id: task.id,
|
|
51
|
+
description: task.description,
|
|
52
|
+
type: task.type,
|
|
53
|
+
};
|
|
54
|
+
if (task.priority) result.priority = task.priority;
|
|
55
|
+
if (task.dependencies) result.dependencies = task.dependencies;
|
|
56
|
+
if (task.estimatedComplexity !== undefined) result.estimatedComplexity = task.estimatedComplexity;
|
|
57
|
+
const metadata = task.metadata as OrchestrationTask['metadata'];
|
|
58
|
+
if (metadata) result.metadata = metadata;
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function toMemoryCapabilityMap(
|
|
63
|
+
caps: Map<string, AgentCapability>,
|
|
64
|
+
): Map<string, MemoryAgentCapability> {
|
|
65
|
+
return new Map(
|
|
66
|
+
Array.from(caps.entries()).map(([k, v]) => [k, toMemoryCapability(v)]),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function fromMemoryCapabilityMap(
|
|
71
|
+
caps: Map<string, MemoryAgentCapability>,
|
|
72
|
+
): Map<string, AgentCapability> {
|
|
73
|
+
return new Map(
|
|
74
|
+
Array.from(caps.entries()).map(([k, v]) => [k, fromMemoryCapability(v)]),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getProvider(): MemoryRoutingProvider {
|
|
79
|
+
return getMemoryRoutingProviderSync();
|
|
80
|
+
}
|
|
@@ -11,6 +11,11 @@ import type {
|
|
|
11
11
|
TaskValidation
|
|
12
12
|
} from '../types.js';
|
|
13
13
|
import { getAgentCapabilitiesManager } from '../config/agent-capabilities.js';
|
|
14
|
+
import {
|
|
15
|
+
getProvider,
|
|
16
|
+
toMemoryTask,
|
|
17
|
+
fromMemoryTask,
|
|
18
|
+
} from '../config/memory-routing-bridge.js';
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
21
|
* Execution Planner
|
|
@@ -67,29 +72,41 @@ export class ExecutionPlanner {
|
|
|
67
72
|
tasks: OrchestrationTask[],
|
|
68
73
|
executionMode: string
|
|
69
74
|
): Promise<ExecutionPlan> {
|
|
75
|
+
const provider = getProvider();
|
|
76
|
+
const memoryTasks = provider.enrichTasks(tasks.map(toMemoryTask));
|
|
77
|
+
const enrichedTasks = memoryTasks.map(fromMemoryTask);
|
|
78
|
+
|
|
70
79
|
const agentAssignments = new Map<string, OrchestrationTask[]>();
|
|
71
80
|
|
|
72
81
|
switch (executionMode) {
|
|
73
82
|
case 'parallel':
|
|
74
|
-
this.createParallelPlan(
|
|
83
|
+
this.createParallelPlan(enrichedTasks, agentAssignments);
|
|
75
84
|
break;
|
|
76
85
|
case 'sequential':
|
|
77
|
-
this.createSequentialPlan(
|
|
86
|
+
this.createSequentialPlan(enrichedTasks, agentAssignments);
|
|
78
87
|
break;
|
|
79
88
|
case 'optimized':
|
|
80
89
|
default:
|
|
81
|
-
await this.createOptimizedPlan(
|
|
90
|
+
await this.createOptimizedPlan(enrichedTasks, agentAssignments);
|
|
82
91
|
break;
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
const estimatedDuration = this.estimateExecutionDuration(agentAssignments);
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
tasks,
|
|
96
|
+
const plan: ExecutionPlan = {
|
|
97
|
+
tasks: enrichedTasks,
|
|
89
98
|
strategy: executionMode as 'parallel' | 'sequential' | 'optimized',
|
|
90
99
|
agentAssignments,
|
|
91
100
|
estimatedDuration,
|
|
92
101
|
};
|
|
102
|
+
|
|
103
|
+
if (provider.id !== 'null') {
|
|
104
|
+
plan.memoryContext = {
|
|
105
|
+
...provider.buildInheritedContext(memoryTasks),
|
|
106
|
+
} as Record<string, unknown>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return plan;
|
|
93
110
|
}
|
|
94
111
|
|
|
95
112
|
/**
|
|
@@ -197,9 +214,14 @@ export class ExecutionPlanner {
|
|
|
197
214
|
): void {
|
|
198
215
|
for (const task of tasks) {
|
|
199
216
|
const complexity = task.estimatedComplexity || 30;
|
|
217
|
+
const requiredCaps = [
|
|
218
|
+
task.type,
|
|
219
|
+
...(task.metadata?.memorySignals ?? []),
|
|
220
|
+
];
|
|
200
221
|
const agent = this.capabilitiesManager.selectAgentForTask(
|
|
201
|
-
|
|
202
|
-
complexity
|
|
222
|
+
requiredCaps,
|
|
223
|
+
complexity,
|
|
224
|
+
task.description,
|
|
203
225
|
) || 'orchestrator';
|
|
204
226
|
|
|
205
227
|
if (!agentAssignments.has(agent)) {
|
|
@@ -247,9 +269,14 @@ export class ExecutionPlanner {
|
|
|
247
269
|
});
|
|
248
270
|
|
|
249
271
|
// Assign to agent
|
|
272
|
+
const requiredCaps = [
|
|
273
|
+
task.type,
|
|
274
|
+
...(task.metadata?.memorySignals ?? []),
|
|
275
|
+
];
|
|
250
276
|
const agent = this.capabilitiesManager.selectAgentForTask(
|
|
251
|
-
|
|
252
|
-
complexity
|
|
277
|
+
requiredCaps,
|
|
278
|
+
complexity,
|
|
279
|
+
task.description,
|
|
253
280
|
) || 'orchestrator';
|
|
254
281
|
|
|
255
282
|
if (!assignments.has(agent)) {
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
import { frameworkLogger } from '../../../core/framework-logger.js';
|
|
8
8
|
import { getExecutionPlanner } from '../execution/execution-planner.js';
|
|
9
9
|
import { mcpClientManager } from '../../../mcps/mcp-client.js';
|
|
10
|
-
import { addObservations, extractOrchestrationObservations } from '../aside-context.js';
|
|
10
|
+
import { addObservations, extractOrchestrationObservations, spawnAside } from '../aside-context.js';
|
|
11
|
+
import { getProvider } from '../config/memory-routing-bridge.js';
|
|
11
12
|
import type { OrchestrationResult, OrchestrationTask } from '../types.js';
|
|
12
13
|
|
|
13
14
|
export interface TaskHandlerDeps {
|
|
@@ -76,9 +77,43 @@ export class TaskHandler {
|
|
|
76
77
|
// Analyze complexity and create execution plan
|
|
77
78
|
const executionPlan = await this.planner.createExecutionPlan(tasks, executionMode);
|
|
78
79
|
|
|
80
|
+
const memoryProvider = getProvider();
|
|
81
|
+
if (memoryProvider.id !== 'null' && executionPlan.memoryContext) {
|
|
82
|
+
await spawnAside({
|
|
83
|
+
description: `Orchestrate: ${description}`,
|
|
84
|
+
sessionId,
|
|
85
|
+
inheritedContext: {
|
|
86
|
+
memoryRouting: executionPlan.memoryContext,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
79
91
|
// Execute orchestration (simulated for MCP server)
|
|
80
92
|
const results = await this.executePlan(executionPlan, sessionId, timeout);
|
|
81
93
|
|
|
94
|
+
// Feed per-task outcomes back to memory provider (not aggregate session stats)
|
|
95
|
+
if (memoryProvider.ingestFeedback) {
|
|
96
|
+
const outcomeByKey = new Map(
|
|
97
|
+
results.taskOutcomes.map((o) => [`${o.agent}:${o.taskId}`, o]),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
for (const [agent, assignedTasks] of executionPlan.agentAssignments) {
|
|
101
|
+
for (const task of assignedTasks) {
|
|
102
|
+
const outcome = outcomeByKey.get(`${agent}:${task.id}`);
|
|
103
|
+
memoryProvider.ingestFeedback({
|
|
104
|
+
timestamp: new Date().toISOString(),
|
|
105
|
+
sessionId,
|
|
106
|
+
taskId: task.id,
|
|
107
|
+
assignedAgent: agent,
|
|
108
|
+
memorySignals: task.metadata?.memorySignals ?? [],
|
|
109
|
+
complexity: task.estimatedComplexity ?? 30,
|
|
110
|
+
success: outcome?.success ?? false,
|
|
111
|
+
durationMs: outcome?.durationMs ?? 0,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
// Update result
|
|
83
118
|
orchestrationResult.success = results.success;
|
|
84
119
|
orchestrationResult.completedTasks = results.completedTasks;
|
|
@@ -136,11 +171,23 @@ export class TaskHandler {
|
|
|
136
171
|
bottlenecks: string[];
|
|
137
172
|
recommendations: string[];
|
|
138
173
|
agentOutputs?: Record<string, string>;
|
|
174
|
+
taskOutcomes: Array<{
|
|
175
|
+
taskId: string;
|
|
176
|
+
agent: string;
|
|
177
|
+
success: boolean;
|
|
178
|
+
durationMs: number;
|
|
179
|
+
}>;
|
|
139
180
|
}> {
|
|
140
181
|
const agentUtilization: Record<string, number> = {};
|
|
141
182
|
let completedTasks = 0;
|
|
142
183
|
let failedTasks = 0;
|
|
143
184
|
const agentOutputs: Record<string, string> = {};
|
|
185
|
+
const taskOutcomes: Array<{
|
|
186
|
+
taskId: string;
|
|
187
|
+
agent: string;
|
|
188
|
+
success: boolean;
|
|
189
|
+
durationMs: number;
|
|
190
|
+
}> = [];
|
|
144
191
|
|
|
145
192
|
const skillServers = new Set([
|
|
146
193
|
'code-review', 'code-reviewer',
|
|
@@ -158,6 +205,7 @@ export class TaskHandler {
|
|
|
158
205
|
|
|
159
206
|
if (skillServers.has(agent)) {
|
|
160
207
|
for (const task of tasks) {
|
|
208
|
+
const taskStart = Date.now();
|
|
161
209
|
try {
|
|
162
210
|
const toolName = this.mapAgentToTool(agent);
|
|
163
211
|
const result = await mcpClientManager.callServerTool(agent, toolName, {
|
|
@@ -169,9 +217,21 @@ export class TaskHandler {
|
|
|
169
217
|
const outputText = this.extractTextFromMcpResult(result);
|
|
170
218
|
agentOutputs[`${agent}:${task.id}`] = outputText;
|
|
171
219
|
completedTasks++;
|
|
220
|
+
taskOutcomes.push({
|
|
221
|
+
taskId: task.id,
|
|
222
|
+
agent,
|
|
223
|
+
success: true,
|
|
224
|
+
durationMs: Date.now() - taskStart,
|
|
225
|
+
});
|
|
172
226
|
} catch (err) {
|
|
173
227
|
failedTasks++;
|
|
174
228
|
agentOutputs[`${agent}:${task.id}`] = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
229
|
+
taskOutcomes.push({
|
|
230
|
+
taskId: task.id,
|
|
231
|
+
agent,
|
|
232
|
+
success: false,
|
|
233
|
+
durationMs: Date.now() - taskStart,
|
|
234
|
+
});
|
|
175
235
|
frameworkLogger.log('orchestrator.task-handler', 'task-execution-failed', 'warning', {
|
|
176
236
|
agent,
|
|
177
237
|
taskId: task.id,
|
|
@@ -181,7 +241,15 @@ export class TaskHandler {
|
|
|
181
241
|
}
|
|
182
242
|
} else {
|
|
183
243
|
// For non-MCP agents we still count them as "assigned" (they will be handled by the caller or legacy path)
|
|
184
|
-
|
|
244
|
+
for (const task of tasks) {
|
|
245
|
+
completedTasks++;
|
|
246
|
+
taskOutcomes.push({
|
|
247
|
+
taskId: task.id,
|
|
248
|
+
agent,
|
|
249
|
+
success: true,
|
|
250
|
+
durationMs: 0,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
185
253
|
}
|
|
186
254
|
}
|
|
187
255
|
|
|
@@ -205,6 +273,7 @@ export class TaskHandler {
|
|
|
205
273
|
bottlenecks,
|
|
206
274
|
recommendations,
|
|
207
275
|
agentOutputs,
|
|
276
|
+
taskOutcomes,
|
|
208
277
|
};
|
|
209
278
|
}
|
|
210
279
|
|
|
@@ -8,6 +8,17 @@ export interface AgentCapability {
|
|
|
8
8
|
capabilities: string[];
|
|
9
9
|
complexityThreshold: number;
|
|
10
10
|
concurrentTasks: number;
|
|
11
|
+
/** Populated by active MemoryRoutingProvider (e.g. Repertoire) */
|
|
12
|
+
memorySignals?: string[];
|
|
13
|
+
memoryTags?: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface OrchestrationTaskMetadata {
|
|
17
|
+
memorySignals?: string[];
|
|
18
|
+
matchedPrimitives?: string[];
|
|
19
|
+
synthesisContext?: string;
|
|
20
|
+
ontologicalTrapDetected?: boolean;
|
|
21
|
+
memoryProviderId?: string;
|
|
11
22
|
}
|
|
12
23
|
|
|
13
24
|
export interface OrchestrationTask {
|
|
@@ -17,6 +28,7 @@ export interface OrchestrationTask {
|
|
|
17
28
|
priority?: 'critical' | 'high' | 'medium' | 'low';
|
|
18
29
|
dependencies?: string[];
|
|
19
30
|
estimatedComplexity?: number;
|
|
31
|
+
metadata?: OrchestrationTaskMetadata;
|
|
20
32
|
}
|
|
21
33
|
|
|
22
34
|
export interface ExecutionPlan {
|
|
@@ -24,6 +36,7 @@ export interface ExecutionPlan {
|
|
|
24
36
|
strategy: 'parallel' | 'sequential' | 'optimized';
|
|
25
37
|
agentAssignments: Map<string, OrchestrationTask[]>;
|
|
26
38
|
estimatedDuration: number;
|
|
39
|
+
memoryContext?: Record<string, unknown>;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
export interface OrchestrationResult {
|
package/src/opencode/codex.codex
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.
|
|
2
|
+
"version": "3.3.0",
|
|
3
3
|
"terms": [
|
|
4
4
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
|
5
5
|
],
|