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.
Files changed (36) hide show
  1. package/.opencode/codex.codex +1 -1
  2. package/dist/CHANGELOG.md +36 -0
  3. package/dist/core/features-config.js +3 -0
  4. package/dist/integrations/grok/grok-cli.js +6 -0
  5. package/dist/mcps/index.js +1 -0
  6. package/dist/mcps/knowledge-skills/skill-invocation.server.js +3 -2
  7. package/dist/mcps/knowledge-skills/storyteller.server.js +167 -0
  8. package/dist/mcps/orchestrator/config/agent-capabilities.js +30 -11
  9. package/dist/mcps/orchestrator/config/memory-routing-bridge.js +70 -0
  10. package/dist/mcps/orchestrator/execution/execution-planner.js +38 -8
  11. package/dist/mcps/orchestrator/handlers/task-handler.js +55 -2
  12. package/dist/mcps/researcher-confidence.js +92 -0
  13. package/dist/mcps/researcher.server.js +19 -3
  14. package/dist/memory-routing/index.js +4 -0
  15. package/dist/memory-routing/null-provider.js +63 -0
  16. package/dist/memory-routing/provider-loader.js +91 -0
  17. package/dist/memory-routing/provider-registry.js +40 -0
  18. package/dist/memory-routing/types.js +7 -0
  19. package/dist/memory-routing/validate-config.js +32 -0
  20. package/dist/nucleus/thin-dispatch.js +29 -1
  21. package/package.json +1 -1
  22. package/scripts/node/install-bridges.cjs +364 -0
  23. package/scripts/node/postinstall.cjs +24 -83
  24. package/scripts/node/setup.cjs +2 -6
  25. package/scripts/node/universal-version-manager.js +2 -2
  26. package/src/mcps/index.ts +1 -0
  27. package/src/mcps/knowledge-skills/skill-invocation.server.ts +3 -2
  28. package/src/mcps/knowledge-skills/storyteller.server.ts +206 -0
  29. package/src/mcps/orchestrator/config/agent-capabilities.ts +49 -14
  30. package/src/mcps/orchestrator/config/memory-routing-bridge.ts +80 -0
  31. package/src/mcps/orchestrator/execution/execution-planner.ts +49 -10
  32. package/src/mcps/orchestrator/handlers/task-handler.ts +71 -2
  33. package/src/mcps/orchestrator/types.ts +17 -0
  34. package/src/mcps/researcher-confidence.ts +144 -0
  35. package/src/mcps/researcher.server.ts +28 -3
  36. 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 capabilities
78
+ * Select the best agent for a task based on complexity, capabilities, and memory routing.
58
79
  */
59
- selectAgentForTask(requiredCapabilities: string[], complexity: number): string | null {
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
- // Check if agent can handle the complexity
65
- if (complexity > caps.complexityThreshold) {
66
- continue;
67
- }
101
+ if (complexity > caps.complexityThreshold) continue;
68
102
 
69
- // Calculate capability match score
70
- const matchCount = requiredCapabilities.filter(cap =>
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(tasks, agentAssignments);
83
+ this.createParallelPlan(enrichedTasks, agentAssignments);
75
84
  break;
76
85
  case 'sequential':
77
- this.createSequentialPlan(tasks, agentAssignments);
86
+ this.createSequentialPlan(enrichedTasks, agentAssignments);
78
87
  break;
79
88
  case 'optimized':
80
89
  default:
81
- await this.createOptimizedPlan(tasks, agentAssignments);
90
+ await this.createOptimizedPlan(enrichedTasks, agentAssignments);
82
91
  break;
83
92
  }
84
93
 
85
94
  const estimatedDuration = this.estimateExecutionDuration(agentAssignments);
86
95
 
87
- return {
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.estimatedComplexity || 30;
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
- [task.type],
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
- [task.type],
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
- completedTasks += tasks.length;
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 {