0xray 3.2.0 → 3.3.1
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 +36 -0
- package/dist/core/features-config.js +3 -0
- package/dist/integrations/grok/grok-cli.js +6 -0
- package/dist/mcps/index.js +1 -0
- package/dist/mcps/knowledge-skills/skill-invocation.server.js +3 -2
- package/dist/mcps/knowledge-skills/storyteller.server.js +167 -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 +38 -8
- package/dist/mcps/orchestrator/handlers/task-handler.js +55 -2
- package/dist/mcps/researcher-confidence.js +92 -0
- package/dist/mcps/researcher.server.js +19 -3
- package/dist/memory-routing/index.js +4 -0
- package/dist/memory-routing/null-provider.js +63 -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/scripts/node/install-bridges.cjs +364 -0
- package/scripts/node/postinstall.cjs +24 -83
- package/scripts/node/setup.cjs +2 -6
- package/scripts/node/universal-version-manager.js +2 -2
- package/src/mcps/index.ts +1 -0
- package/src/mcps/knowledge-skills/skill-invocation.server.ts +3 -2
- package/src/mcps/knowledge-skills/storyteller.server.ts +206 -0
- 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 +49 -10
- package/src/mcps/orchestrator/handlers/task-handler.ts +71 -2
- package/src/mcps/orchestrator/types.ts +17 -0
- package/src/mcps/researcher-confidence.ts +144 -0
- package/src/mcps/researcher.server.ts +28 -3
- package/src/opencode/codex.codex +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storyteller MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Returns storyteller SKILL.md guidance plus a session-aware writing prompt.
|
|
5
|
+
* The host LLM writes the story; this server supplies the contract.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { XrayKnowledgeSkillBase } from '../shared/knowledge-skill-base.js';
|
|
13
|
+
|
|
14
|
+
const STORY_TYPES = ['reflection', 'saga', 'journey', 'narrative'] as const;
|
|
15
|
+
type StoryType = (typeof STORY_TYPES)[number];
|
|
16
|
+
|
|
17
|
+
interface WriteStoryArgs {
|
|
18
|
+
title?: string;
|
|
19
|
+
context?: Record<string, unknown>;
|
|
20
|
+
framework?: string;
|
|
21
|
+
output?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveSkillMd(): string {
|
|
25
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const candidates = [
|
|
27
|
+
join(here, '../../../skills/storyteller/SKILL.md'),
|
|
28
|
+
join(here, '../../skills/storyteller/SKILL.md'),
|
|
29
|
+
join(process.cwd(), 'node_modules/0xray/dist/skills/storyteller/SKILL.md'),
|
|
30
|
+
join(process.cwd(), 'node_modules/xray/dist/skills/storyteller/SKILL.md'),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
for (const path of candidates) {
|
|
34
|
+
if (existsSync(path)) {
|
|
35
|
+
return readFileSync(path, 'utf-8');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return '# Storyteller\n\nWrite warmly candid technical reflections with scene-first openings.';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getStoryMeta(storyType: StoryType) {
|
|
43
|
+
const defaults: Record<
|
|
44
|
+
StoryType,
|
|
45
|
+
{ location: string; min_words: number; ideal_words: number; framework: string }
|
|
46
|
+
> = {
|
|
47
|
+
reflection: {
|
|
48
|
+
location: 'docs/reflections/',
|
|
49
|
+
min_words: 2000,
|
|
50
|
+
ideal_words: 5000,
|
|
51
|
+
framework: 'three_act_structure',
|
|
52
|
+
},
|
|
53
|
+
saga: {
|
|
54
|
+
location: 'docs/reflections/deep/',
|
|
55
|
+
min_words: 5000,
|
|
56
|
+
ideal_words: 15000,
|
|
57
|
+
framework: 'hero_journey',
|
|
58
|
+
},
|
|
59
|
+
journey: {
|
|
60
|
+
location: 'docs/reflections/deep/',
|
|
61
|
+
min_words: 1500,
|
|
62
|
+
ideal_words: 4000,
|
|
63
|
+
framework: 'three_act_structure',
|
|
64
|
+
},
|
|
65
|
+
narrative: {
|
|
66
|
+
location: 'docs/reflections/',
|
|
67
|
+
min_words: 1000,
|
|
68
|
+
ideal_words: 3000,
|
|
69
|
+
framework: 'three_act_structure',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
return defaults[storyType];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function gitContext(): string {
|
|
76
|
+
try {
|
|
77
|
+
const commits = execSync('git log --oneline -20 2>/dev/null', { encoding: 'utf-8' });
|
|
78
|
+
const files = execSync(
|
|
79
|
+
'git diff --name-only HEAD~10 2>/dev/null || git ls-files 2>/dev/null | head -50',
|
|
80
|
+
{ encoding: 'utf-8' },
|
|
81
|
+
);
|
|
82
|
+
return `Recent commits:\n${commits}\n\nRecent files:\n${files}`;
|
|
83
|
+
} catch {
|
|
84
|
+
return 'No git context available in this workspace.';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildPrompt(
|
|
89
|
+
storyType: StoryType,
|
|
90
|
+
title: string,
|
|
91
|
+
meta: ReturnType<typeof getStoryMeta>,
|
|
92
|
+
extraContext?: Record<string, unknown>,
|
|
93
|
+
): string {
|
|
94
|
+
const contextBlock = extraContext
|
|
95
|
+
? `\n## Provided context\n${JSON.stringify(extraContext, null, 2)}\n`
|
|
96
|
+
: '';
|
|
97
|
+
|
|
98
|
+
return `Write a ${storyType} titled "${title}".
|
|
99
|
+
|
|
100
|
+
## Requirements
|
|
101
|
+
- Framework: ${meta.framework}
|
|
102
|
+
- Target length: ${meta.ideal_words} words (minimum: ${meta.min_words})
|
|
103
|
+
- Save to: ${meta.location}
|
|
104
|
+
- Follow storyteller SKILL.md voice (warmly candid, scene-first, no executive summary)
|
|
105
|
+
- Include Key Takeaways and What Next sections
|
|
106
|
+
|
|
107
|
+
## Session / repo context
|
|
108
|
+
${gitContext()}
|
|
109
|
+
${contextBlock}
|
|
110
|
+
|
|
111
|
+
Write the complete ${storyType} now.`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function writeStoryTool(storyType: StoryType, args: WriteStoryArgs) {
|
|
115
|
+
const meta = getStoryMeta(storyType);
|
|
116
|
+
if (args.framework) meta.framework = args.framework;
|
|
117
|
+
|
|
118
|
+
const title =
|
|
119
|
+
args.title ??
|
|
120
|
+
`${storyType.charAt(0).toUpperCase() + storyType.slice(1)} - ${new Date().toISOString().split('T')[0]}`;
|
|
121
|
+
const skillMd = resolveSkillMd();
|
|
122
|
+
const prompt = buildPrompt(storyType, title, meta, args.context);
|
|
123
|
+
const slug = title
|
|
124
|
+
.toLowerCase()
|
|
125
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
126
|
+
.replace(/^-|-$/g, '');
|
|
127
|
+
const filename = `${storyType}-${slug}-${new Date().toISOString().split('T')[0]}.md`;
|
|
128
|
+
const outputPath = args.output ?? join(meta.location, filename);
|
|
129
|
+
|
|
130
|
+
const outputDir = join(process.cwd(), dirname(outputPath));
|
|
131
|
+
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
|
|
132
|
+
|
|
133
|
+
const fullPath = join(process.cwd(), outputPath);
|
|
134
|
+
const frontmatter = `---
|
|
135
|
+
story_type: ${storyType}
|
|
136
|
+
title: "${title}"
|
|
137
|
+
framework: ${meta.framework}
|
|
138
|
+
target_words: ${meta.ideal_words}
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
writeFileSync(fullPath, `${frontmatter}\n${prompt}\n`, 'utf-8');
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: 'text' as const,
|
|
149
|
+
text: [
|
|
150
|
+
`Storyteller ${storyType} brief prepared.`,
|
|
151
|
+
`Output template: ${outputPath}`,
|
|
152
|
+
'',
|
|
153
|
+
'--- SKILL (excerpt) ---',
|
|
154
|
+
skillMd.slice(0, 4000),
|
|
155
|
+
skillMd.length > 4000 ? '\n...(truncated; full SKILL.md on disk)' : '',
|
|
156
|
+
'',
|
|
157
|
+
'--- WRITING PROMPT ---',
|
|
158
|
+
prompt,
|
|
159
|
+
'',
|
|
160
|
+
'Host LLM: write the full story into the output path using the prompt above.',
|
|
161
|
+
].join('\n'),
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
class StorytellerServer extends XrayKnowledgeSkillBase {
|
|
168
|
+
constructor() {
|
|
169
|
+
super('storyteller', '1.0.0');
|
|
170
|
+
|
|
171
|
+
const writeSchema = {
|
|
172
|
+
type: 'object' as const,
|
|
173
|
+
properties: {
|
|
174
|
+
title: { type: 'string', description: 'Story title' },
|
|
175
|
+
context: { type: 'object', description: 'Session context (commits, changes, notes)' },
|
|
176
|
+
framework: { type: 'string', description: 'Storytelling framework override' },
|
|
177
|
+
output: { type: 'string', description: 'Relative output path' },
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
this.tools = STORY_TYPES.map((storyType) => ({
|
|
182
|
+
name: `write_${storyType}`,
|
|
183
|
+
description: `Prepare ${storyType} story brief using storyteller SKILL.md`,
|
|
184
|
+
inputSchema: writeSchema,
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
this.handlers = {
|
|
188
|
+
write_reflection: async (args) => writeStoryTool('reflection', args as WriteStoryArgs),
|
|
189
|
+
write_saga: async (args) => writeStoryTool('saga', args as WriteStoryArgs),
|
|
190
|
+
write_journey: async (args) => writeStoryTool('journey', args as WriteStoryArgs),
|
|
191
|
+
write_narrative: async (args) => writeStoryTool('narrative', args as WriteStoryArgs),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
this.setupToolHandlers();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (import.meta.url === `file://${fileURLToPath(import.meta.url)}`) {
|
|
199
|
+
const server = new StorytellerServer();
|
|
200
|
+
server.run().catch((error) => {
|
|
201
|
+
process.stderr.write(`FATAL: ${String(error)}\n`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export { StorytellerServer };
|
|
@@ -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
|
/**
|
|
@@ -196,10 +213,18 @@ export class ExecutionPlanner {
|
|
|
196
213
|
agentAssignments: Map<string, OrchestrationTask[]>
|
|
197
214
|
): void {
|
|
198
215
|
for (const task of tasks) {
|
|
199
|
-
const complexity = task
|
|
216
|
+
const complexity = this.calculateTaskComplexity(task);
|
|
217
|
+
const requiredCaps = [
|
|
218
|
+
task.type,
|
|
219
|
+
...(task.metadata?.memorySignals ?? []),
|
|
220
|
+
];
|
|
221
|
+
const operationDescription = task.metadata?.memoryHighConfidenceTrap
|
|
222
|
+
? `${task.description} TYPE: ontological-trap`
|
|
223
|
+
: task.description;
|
|
200
224
|
const agent = this.capabilitiesManager.selectAgentForTask(
|
|
201
|
-
|
|
202
|
-
complexity
|
|
225
|
+
requiredCaps,
|
|
226
|
+
complexity,
|
|
227
|
+
operationDescription,
|
|
203
228
|
) || 'orchestrator';
|
|
204
229
|
|
|
205
230
|
if (!agentAssignments.has(agent)) {
|
|
@@ -247,9 +272,14 @@ export class ExecutionPlanner {
|
|
|
247
272
|
});
|
|
248
273
|
|
|
249
274
|
// Assign to agent
|
|
275
|
+
const requiredCaps = [
|
|
276
|
+
task.type,
|
|
277
|
+
...(task.metadata?.memorySignals ?? []),
|
|
278
|
+
];
|
|
250
279
|
const agent = this.capabilitiesManager.selectAgentForTask(
|
|
251
|
-
|
|
252
|
-
complexity
|
|
280
|
+
requiredCaps,
|
|
281
|
+
complexity,
|
|
282
|
+
task.description,
|
|
253
283
|
) || 'orchestrator';
|
|
254
284
|
|
|
255
285
|
if (!assignments.has(agent)) {
|
|
@@ -319,6 +349,15 @@ export class ExecutionPlanner {
|
|
|
319
349
|
complexity += task.dependencies.length * 5;
|
|
320
350
|
}
|
|
321
351
|
|
|
352
|
+
if (task.metadata?.memoryComplexityBoost !== undefined) {
|
|
353
|
+
complexity += task.metadata.memoryComplexityBoost;
|
|
354
|
+
} else {
|
|
355
|
+
const provider = getProvider();
|
|
356
|
+
if (provider.id !== 'null' && provider.getTaskConfidence) {
|
|
357
|
+
complexity += provider.getTaskConfidence(toMemoryTask(task)).complexityBoost;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
322
361
|
return Math.min(Math.max(Math.round(complexity), 1), 100);
|
|
323
362
|
}
|
|
324
363
|
|
|
@@ -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,21 @@ 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;
|
|
22
|
+
memorySignalConfidences?: Record<string, number>;
|
|
23
|
+
memoryAvgConfidence?: number;
|
|
24
|
+
memoryHighConfidenceTrap?: boolean;
|
|
25
|
+
memoryComplexityBoost?: number;
|
|
11
26
|
}
|
|
12
27
|
|
|
13
28
|
export interface OrchestrationTask {
|
|
@@ -17,6 +32,7 @@ export interface OrchestrationTask {
|
|
|
17
32
|
priority?: 'critical' | 'high' | 'medium' | 'low';
|
|
18
33
|
dependencies?: string[];
|
|
19
34
|
estimatedComplexity?: number;
|
|
35
|
+
metadata?: OrchestrationTaskMetadata;
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
export interface ExecutionPlan {
|
|
@@ -24,6 +40,7 @@ export interface ExecutionPlan {
|
|
|
24
40
|
strategy: 'parallel' | 'sequential' | 'optimized';
|
|
25
41
|
agentAssignments: Map<string, OrchestrationTask[]>;
|
|
26
42
|
estimatedDuration: number;
|
|
43
|
+
memoryContext?: Record<string, unknown>;
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
export interface OrchestrationResult {
|