Skip to content

Commit be39379

Browse files
committed
feat: enable task routing and add comprehensive analytics logging
- Enable task routing in plugin (was commented out) - Add TaskSkillRouter variable declarations - Implement loadTaskSkillRouter() to dynamically load router - Add RouterCore logging for all routing decisions (keyword, history, complexity, fallback) - Add AgentDelegator logging for agent selection with complexity details - Outcome tracking persists to disk (logs/framework/routing-outcomes.json) Now analytics will capture: - Task routing decisions with agent/skill/confidence - Complexity scores for each task - Agent selection rationale - Historical outcomes for pattern analysis
1 parent b63f35f commit be39379

File tree

3 files changed

+94
-30
lines changed

3 files changed

+94
-30
lines changed

src/delegation/agent-delegator.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,26 @@ export class AgentDelegator {
636636
});
637637
}
638638

639-
return agents.length > 0
639+
const finalAgents = agents.length > 0
640640
? agents
641641
: [{ name: "enforcer", confidence: 0.75, role: "validation" }];
642+
643+
// Log complete agent selection for analytics
644+
frameworkLogger.log(
645+
"agent-delegator",
646+
"agents-selected",
647+
"info",
648+
{
649+
operation,
650+
strategy: complexityScore.recommendedStrategy,
651+
complexityLevel: complexityScore.level,
652+
complexityScore: complexityScore.score,
653+
agents: finalAgents.map(a => ({ name: a.name, role: a.role, confidence: a.confidence })),
654+
agentCount: finalAgents.length,
655+
}
656+
);
657+
658+
return finalAgents;
642659
}
643660

644661
private mapOperationToType(

src/delegation/routing/router-core.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ export class RouterCore {
8686

8787
// Validate input
8888
if (!taskDescription || typeof taskDescription !== 'string') {
89+
frameworkLogger.log('router-core', 'routing-fallback', 'info', {
90+
reason: 'Invalid task description',
91+
agent: DEFAULT_ROUTING.agent,
92+
skill: DEFAULT_ROUTING.skill,
93+
}, sessionId);
8994
return {
9095
...DEFAULT_ROUTING,
9196
reason: 'Invalid task description',
@@ -95,6 +100,12 @@ export class RouterCore {
95100
// 0. SPECIAL CASE: Release/Publish detection
96101
const releaseDetection = this.keywordMatcher.detectReleaseWorkflow(taskDescription);
97102
if (releaseDetection.isRelease) {
103+
frameworkLogger.log('router-core', 'release-workflow-detected', 'info', {
104+
taskDescription: taskDescription.slice(0, 100),
105+
agent: RELEASE_WORKFLOW_ROUTING.agent,
106+
skill: RELEASE_WORKFLOW_ROUTING.skill,
107+
confidence: RELEASE_WORKFLOW_ROUTING.confidence,
108+
}, sessionId);
98109
return this.createReleaseRouting(taskDescription, releaseDetection, sessionId);
99110
}
100111

@@ -103,6 +114,13 @@ export class RouterCore {
103114
// 1. Try keyword matching first (highest priority)
104115
const keywordResult = this.performKeywordMatching(descLower);
105116
if (keywordResult) {
117+
frameworkLogger.log('router-core', 'keyword-match', 'info', {
118+
taskDescription: taskDescription.slice(0, 100),
119+
agent: keywordResult.agent,
120+
skill: keywordResult.skill,
121+
confidence: keywordResult.confidence,
122+
matchedKeyword: keywordResult.matchedKeyword,
123+
}, sessionId);
106124
return this.applyKernelInsights(keywordResult, taskDescription);
107125
}
108126

@@ -111,6 +129,12 @@ export class RouterCore {
111129
const historyResult = this.historyMatcher.match(taskId);
112130
if (historyResult) {
113131
this.logHistoryMatch(taskId, historyResult, sessionId);
132+
frameworkLogger.log('router-core', 'history-match', 'info', {
133+
taskId,
134+
agent: historyResult.agent,
135+
skill: historyResult.skill,
136+
confidence: historyResult.confidence,
137+
}, sessionId);
114138
return historyResult;
115139
}
116140
}
@@ -119,11 +143,24 @@ export class RouterCore {
119143
if (complexity !== undefined) {
120144
const complexityResult = this.complexityRouter.route(complexity, options);
121145
if (complexityResult) {
146+
frameworkLogger.log('router-core', 'complexity-match', 'info', {
147+
taskDescription: taskDescription.slice(0, 100),
148+
complexity,
149+
agent: complexityResult.agent,
150+
skill: complexityResult.skill,
151+
confidence: complexityResult.confidence,
152+
}, sessionId);
122153
return complexityResult;
123154
}
124155
}
125156

126157
// 4. Default fallback
158+
frameworkLogger.log('router-core', 'routing-fallback', 'info', {
159+
taskDescription: taskDescription.slice(0, 100),
160+
reason: 'No keyword match, no history, no complexity provided',
161+
agent: DEFAULT_ROUTING.agent,
162+
skill: DEFAULT_ROUTING.skill,
163+
}, sessionId);
127164
return {
128165
...DEFAULT_ROUTING,
129166
reason: 'No keyword match, no history, no complexity provided',

src/plugin/strray-codex-injection.ts

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ let ProcessorManager: any;
3131
let StrRayStateManager: any;
3232
let featuresConfigLoader: any;
3333
let detectTaskType: any;
34-
// TODO: Enable TaskSkillRouter after v1.11.0
35-
// let TaskSkillRouter: any;
36-
// let taskSkillSkillRouterInstance: any;
34+
let TaskSkillRouter: any;
35+
let taskSkillRouterInstance: any;
3736

3837
async function loadStrRayComponents() {
3938
if (ProcessorManager && StrRayStateManager && featuresConfigLoader) {
@@ -102,8 +101,6 @@ async function loadStrRayComponents() {
102101
/**
103102
* Extract task description from tool input
104103
*/
105-
// TODO: Enable after v1.11.0
106-
/*
107104
function extractTaskDescription(input: { tool: string; args?: Record<string, unknown> }): string | null {
108105
const { tool, args } = input;
109106

@@ -124,11 +121,31 @@ function extractTaskDescription(input: { tool: string; args?: Record<string, unk
124121

125122
return null;
126123
}
127-
*/
128124

129125
async function loadTaskSkillRouter(): Promise<void> {
130-
// Task routing will be available after framework is built and installed
131-
// For now, tasks are routed based on explicit @agent syntax
126+
if (taskSkillRouterInstance) {
127+
return; // Already loaded
128+
}
129+
130+
// Try local dist first (for development)
131+
try {
132+
const module = await import(
133+
"../../dist/delegation/task-skill-router.js" as any
134+
);
135+
TaskSkillRouter = module.TaskSkillRouter;
136+
taskSkillRouterInstance = new TaskSkillRouter();
137+
} catch (distError) {
138+
// Try node_modules (for consumer installs)
139+
try {
140+
const module = await import(
141+
"strray-ai/dist/delegation/task-skill-router.js" as any
142+
);
143+
TaskSkillRouter = module.TaskSkillRouter;
144+
taskSkillRouterInstance = new TaskSkillRouter();
145+
} catch (nmError) {
146+
// Task routing not available - continue without it
147+
}
148+
}
132149
}
133150

134151
function spawnPromise(
@@ -567,46 +584,39 @@ export default async function strrayCodexPlugin(input: {
567584

568585
// ============================================================
569586
// TASK ROUTING: Analyze task and route to best agent
570-
// TODO: Enable after v1.11.0 - requires built framework
587+
// Enabled in v1.10.5 - provides analytics data
571588
// ============================================================
572-
/*
573589
const taskDescription = extractTaskDescription(input);
574590

575591
if (taskDescription && featuresConfigLoader) {
576592
try {
577593
await loadTaskSkillRouter();
578594

579595
if (taskSkillRouterInstance) {
580-
const config = featuresConfigLoader.loadConfig();
596+
const routingResult = taskSkillRouterInstance.routeTask(taskDescription, {
597+
toolName: tool,
598+
});
581599

582-
// Check if task routing is enabled (model_routing.enabled flag)
583-
if (config.model_routing?.enabled) {
584-
const routingResult = taskSkillRouterInstance.routeTask(taskDescription, {
585-
toolName: tool,
586-
});
600+
if (routingResult && routingResult.agent) {
601+
logger.log(
602+
`🎯 Task routed: "${taskDescription.slice(0, 50)}..." → ${routingResult.agent} (confidence: ${routingResult.confidence})`,
603+
);
604+
605+
// Store routing result for downstream processing
606+
output._strrayRouting = routingResult;
587607

588-
if (routingResult && routingResult.agent) {
608+
// If complexity is high, log a warning
609+
if (routingResult.context?.complexity > 50) {
589610
logger.log(
590-
`🎯 Task routed: "${taskDescription.slice(0, 50)}..." → ${routingResult.agent} (confidence: ${routingResult.confidence})`,
611+
`⚠️ High complexity task detected (${routingResult.context.complexity}) - consider multi-agent orchestration`,
591612
);
592-
593-
// Store routing result for downstream processing
594-
output._strrayRouting = routingResult;
595-
596-
// If complexity is high, log a warning
597-
if (routingResult.context?.complexity > 50) {
598-
logger.log(
599-
`⚠️ High complexity task detected (${routingResult.context.complexity}) - consider multi-agent orchestration`,
600-
);
601-
}
602613
}
603614
}
604615
}
605616
} catch (e) {
606617
logger.error("Task routing error:", e);
607618
}
608619
}
609-
*/
610620

611621
// ENFORCER QUALITY GATE CHECK - Block on violations
612622
const qualityGateResult = await runEnforcerQualityGate(input, logger);

0 commit comments

Comments
 (0)