strray-ai 1.13.3 ā 1.13.5
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/plugins/strray-codex-injection.js +119 -80
- package/.opencode/strray/routing-mappings.json +426 -211
- package/dist/delegation/analytics/outcome-tracker.d.ts.map +1 -1
- package/dist/delegation/analytics/outcome-tracker.js +1 -2
- package/dist/delegation/analytics/outcome-tracker.js.map +1 -1
- package/dist/plugin/strray-codex-injection.d.ts +23 -8
- package/dist/plugin/strray-codex-injection.d.ts.map +1 -1
- package/dist/plugin/strray-codex-injection.js +119 -80
- package/dist/plugin/strray-codex-injection.js.map +1 -1
- package/package.json +1 -1
|
@@ -146,6 +146,44 @@ function extractTaskDescription(input) {
|
|
|
146
146
|
}
|
|
147
147
|
return null;
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Estimate complexity score based on message content
|
|
151
|
+
* Higher complexity = orchestrator routing
|
|
152
|
+
* Lower complexity = code-reviewer routing
|
|
153
|
+
*/
|
|
154
|
+
function estimateComplexity(message) {
|
|
155
|
+
const text = message.toLowerCase();
|
|
156
|
+
// High complexity indicators
|
|
157
|
+
const highComplexityKeywords = [
|
|
158
|
+
"architecture", "system", "design", "complex", "multiple",
|
|
159
|
+
"integrate", "database", "migration", "refactor",
|
|
160
|
+
"performance", "optimize", "security", "audit",
|
|
161
|
+
"orchestrate", "coordinate", "workflow"
|
|
162
|
+
];
|
|
163
|
+
// Low complexity indicators
|
|
164
|
+
const lowComplexityKeywords = [
|
|
165
|
+
"review", "check", "simple", "quick", "fix",
|
|
166
|
+
"small", "typo", "format", "lint", "test"
|
|
167
|
+
];
|
|
168
|
+
let score = 50; // default medium
|
|
169
|
+
// Check message length
|
|
170
|
+
if (message.length > 200)
|
|
171
|
+
score += 10;
|
|
172
|
+
if (message.length > 500)
|
|
173
|
+
score += 15;
|
|
174
|
+
// Check for high complexity keywords
|
|
175
|
+
for (const keyword of highComplexityKeywords) {
|
|
176
|
+
if (text.includes(keyword))
|
|
177
|
+
score += 8;
|
|
178
|
+
}
|
|
179
|
+
// Check for low complexity keywords
|
|
180
|
+
for (const keyword of lowComplexityKeywords) {
|
|
181
|
+
if (text.includes(keyword))
|
|
182
|
+
score -= 5;
|
|
183
|
+
}
|
|
184
|
+
// Clamp to 0-100
|
|
185
|
+
return Math.max(0, Math.min(100, score));
|
|
186
|
+
}
|
|
149
187
|
async function loadTaskSkillRouter() {
|
|
150
188
|
if (taskSkillRouterInstance) {
|
|
151
189
|
return; // Already loaded
|
|
@@ -402,34 +440,7 @@ export default async function strrayCodexPlugin(input) {
|
|
|
402
440
|
showEssentialLinks: true
|
|
403
441
|
});
|
|
404
442
|
}
|
|
405
|
-
//
|
|
406
|
-
// PROMPT-LEVEL ROUTING: Route user prompts to best agent
|
|
407
|
-
// ============================================================
|
|
408
|
-
const userPrompt = String(_input.prompt || _input.message || _input.content || "");
|
|
409
|
-
if (userPrompt && userPrompt.length > 0) {
|
|
410
|
-
try {
|
|
411
|
-
await loadTaskSkillRouter();
|
|
412
|
-
if (taskSkillRouterInstance) {
|
|
413
|
-
const routingResult = taskSkillRouterInstance.routeTask(userPrompt, {
|
|
414
|
-
source: "prompt",
|
|
415
|
-
});
|
|
416
|
-
if (routingResult && routingResult.agent) {
|
|
417
|
-
const logger = await getOrCreateLogger(directory);
|
|
418
|
-
logger.log(`šÆ Prompt routed: "${userPrompt.slice(0, 50)}${userPrompt.length > 50 ? "..." : ""}" ā ${routingResult.agent} (confidence: ${routingResult.confidence})`);
|
|
419
|
-
// Add routing context to system prompt
|
|
420
|
-
leanPrompt += `\n\nšÆ Recommended Agent: @${routingResult.agent}\n`;
|
|
421
|
-
leanPrompt += `š Confidence: ${Math.round(routingResult.confidence * 100)}%\n`;
|
|
422
|
-
if (routingResult.context?.complexity > 50) {
|
|
423
|
-
leanPrompt += `ā ļø High complexity detected - consider using @orchestrator\n`;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
catch (e) {
|
|
429
|
-
const logger = await getOrCreateLogger(directory);
|
|
430
|
-
logger.error("Prompt routing error:", e);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
443
|
+
// Routing is handled in chat.message hook - this hook only does system prompt injection
|
|
433
444
|
if (output.system && Array.isArray(output.system)) {
|
|
434
445
|
output.system = [leanPrompt];
|
|
435
446
|
}
|
|
@@ -465,33 +476,7 @@ export default async function strrayCodexPlugin(input) {
|
|
|
465
476
|
}
|
|
466
477
|
}
|
|
467
478
|
const { tool, args } = input;
|
|
468
|
-
//
|
|
469
|
-
// TASK ROUTING: Analyze task and route to best agent
|
|
470
|
-
// Enabled in v1.10.5 - provides analytics data
|
|
471
|
-
// ============================================================
|
|
472
|
-
const taskDescription = extractTaskDescription(input);
|
|
473
|
-
if (taskDescription && featuresConfigLoader) {
|
|
474
|
-
try {
|
|
475
|
-
await loadTaskSkillRouter();
|
|
476
|
-
if (taskSkillRouterInstance) {
|
|
477
|
-
const routingResult = taskSkillRouterInstance.routeTask(taskDescription, {
|
|
478
|
-
toolName: tool,
|
|
479
|
-
});
|
|
480
|
-
if (routingResult && routingResult.agent) {
|
|
481
|
-
logger.log(`šÆ Task routed: "${taskDescription.slice(0, 50)}..." ā ${routingResult.agent} (confidence: ${routingResult.confidence})`);
|
|
482
|
-
// Store routing result for downstream processing
|
|
483
|
-
output._strrayRouting = routingResult;
|
|
484
|
-
// If complexity is high, log a warning
|
|
485
|
-
if (routingResult.context?.complexity > 50) {
|
|
486
|
-
logger.log(`ā ļø High complexity task detected (${routingResult.context.complexity}) - consider multi-agent orchestration`);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
catch (e) {
|
|
492
|
-
logger.error("Task routing error:", e);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
479
|
+
// Routing is handled in chat.message hook - this hook only does tool execution logging
|
|
495
480
|
// ENFORCER QUALITY GATE CHECK - Block on violations
|
|
496
481
|
await importQualityGate(directory);
|
|
497
482
|
if (!runQualityGateWithLogging) {
|
|
@@ -714,47 +699,101 @@ export default async function strrayCodexPlugin(input) {
|
|
|
714
699
|
}
|
|
715
700
|
},
|
|
716
701
|
/**
|
|
717
|
-
*
|
|
718
|
-
*
|
|
702
|
+
* chat.message - Intercept user messages for routing
|
|
703
|
+
* Output contains message and parts with user content
|
|
719
704
|
*/
|
|
720
|
-
"
|
|
705
|
+
"chat.message": async (input, output) => {
|
|
721
706
|
const logger = await getOrCreateLogger(directory);
|
|
722
|
-
//
|
|
723
|
-
const
|
|
724
|
-
|
|
707
|
+
// DEBUG: Log ALL output
|
|
708
|
+
const debugLogPath = path.join(process.cwd(), "logs", "framework", "routing-debug.log");
|
|
709
|
+
fs.appendFileSync(debugLogPath, `\n[${new Date().toISOString()}] === chat.message HOOK FIRED ===\n`);
|
|
710
|
+
fs.appendFileSync(debugLogPath, `OUTPUT KEYS: ${JSON.stringify(Object.keys(output || {}))}\n`);
|
|
711
|
+
fs.appendFileSync(debugLogPath, `MESSAGE: ${JSON.stringify(output?.message)}\n`);
|
|
712
|
+
fs.appendFileSync(debugLogPath, `PARTS: ${JSON.stringify(output?.parts)}\n`);
|
|
713
|
+
// Extract user message from parts (TextPart has type="text" and text field)
|
|
714
|
+
let userMessage = "";
|
|
715
|
+
if (output?.parts && Array.isArray(output.parts)) {
|
|
716
|
+
for (const part of output.parts) {
|
|
717
|
+
if (part?.type === "text" && part?.text) {
|
|
718
|
+
userMessage = part.text;
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
fs.appendFileSync(debugLogPath, `userMessage: "${userMessage.slice(0, 100)}"\n`);
|
|
724
|
+
if (!userMessage || userMessage.length === 0) {
|
|
725
|
+
fs.appendFileSync(debugLogPath, `SKIP: No user text found\n`);
|
|
725
726
|
return;
|
|
726
727
|
}
|
|
727
|
-
logger.log(`š¤ User message
|
|
728
|
+
logger.log(`š¤ User message: "${userMessage.slice(0, 50)}..."`);
|
|
728
729
|
try {
|
|
729
730
|
await loadTaskSkillRouter();
|
|
730
731
|
if (taskSkillRouterInstance) {
|
|
731
|
-
//
|
|
732
|
-
|
|
733
|
-
|
|
732
|
+
// Get complexity score for tiebreaking
|
|
733
|
+
let complexityScore = 50; // default medium
|
|
734
|
+
try {
|
|
735
|
+
if (featuresConfigLoader) {
|
|
736
|
+
const config = featuresConfigLoader.loadConfig();
|
|
737
|
+
if (config.model_routing?.complexity?.enabled) {
|
|
738
|
+
// Estimate complexity based on message length and keywords
|
|
739
|
+
complexityScore = estimateComplexity(userMessage);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch (e) {
|
|
744
|
+
// Silent fail for complexity estimation
|
|
745
|
+
}
|
|
746
|
+
fs.appendFileSync(debugLogPath, `Complexity estimated: ${complexityScore}\n`);
|
|
747
|
+
// Route with complexity context
|
|
748
|
+
const routingResult = taskSkillRouterInstance.routeTask(userMessage, {
|
|
749
|
+
source: "chat_message",
|
|
750
|
+
complexity: complexityScore,
|
|
734
751
|
});
|
|
752
|
+
fs.appendFileSync(debugLogPath, `Routing result: ${JSON.stringify(routingResult)}\n`);
|
|
735
753
|
if (routingResult && routingResult.agent) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
//
|
|
740
|
-
if (
|
|
741
|
-
|
|
754
|
+
// Apply weighted confidence scoring
|
|
755
|
+
let finalConfidence = routingResult.confidence;
|
|
756
|
+
let routingMethod = "keyword";
|
|
757
|
+
// If keyword confidence is low, use complexity-based routing
|
|
758
|
+
if (routingResult.confidence < 0.7 && complexityScore > 50) {
|
|
759
|
+
// High complexity tasks get orchestrator boost
|
|
760
|
+
if (complexityScore > 70) {
|
|
761
|
+
routingResult.agent = "orchestrator";
|
|
762
|
+
finalConfidence = Math.min(0.85, routingResult.confidence + 0.15);
|
|
763
|
+
routingMethod = "complexity";
|
|
764
|
+
}
|
|
742
765
|
}
|
|
743
|
-
|
|
744
|
-
|
|
766
|
+
// If low complexity and low confidence, boost code-reviewer
|
|
767
|
+
if (routingResult.confidence < 0.6 && complexityScore < 30) {
|
|
768
|
+
routingResult.agent = "code-reviewer";
|
|
769
|
+
finalConfidence = Math.min(0.75, routingResult.confidence + 0.15);
|
|
770
|
+
routingMethod = "complexity";
|
|
771
|
+
}
|
|
772
|
+
logger.log(`šÆ Routed to: @${routingResult.agent} (${Math.round(finalConfidence * 100)}%) via ${routingMethod}`);
|
|
773
|
+
fs.appendFileSync(debugLogPath, `Final agent: ${routingResult.agent}, confidence: ${finalConfidence}, method: ${routingMethod}\n`);
|
|
774
|
+
// Store routing in session for later use
|
|
775
|
+
const sessionRoutingPath = path.join(process.cwd(), "logs", "framework", "session-routing.json");
|
|
776
|
+
try {
|
|
777
|
+
fs.appendFileSync(sessionRoutingPath, JSON.stringify({
|
|
778
|
+
timestamp: new Date().toISOString(),
|
|
779
|
+
message: userMessage.slice(0, 100),
|
|
780
|
+
agent: routingResult.agent,
|
|
781
|
+
confidence: finalConfidence,
|
|
782
|
+
method: routingMethod,
|
|
783
|
+
complexity: complexityScore,
|
|
784
|
+
}) + "\n");
|
|
785
|
+
}
|
|
786
|
+
catch (e) {
|
|
787
|
+
// Silent fail for session routing logging
|
|
745
788
|
}
|
|
746
|
-
// Log routing outcome
|
|
747
|
-
logToolActivity(directory, "routing", "user_message", {
|
|
748
|
-
agent: routingResult.agent,
|
|
749
|
-
confidence: routingResult.confidence,
|
|
750
|
-
skill: routingResult.skill,
|
|
751
|
-
});
|
|
752
789
|
}
|
|
753
790
|
}
|
|
754
791
|
}
|
|
755
792
|
catch (e) {
|
|
756
|
-
logger.error("
|
|
793
|
+
logger.error("Chat message routing error:", e);
|
|
794
|
+
fs.appendFileSync(debugLogPath, `ERROR: ${e}\n`);
|
|
757
795
|
}
|
|
796
|
+
fs.appendFileSync(debugLogPath, `=== END chat.message ===\n`);
|
|
758
797
|
},
|
|
759
798
|
config: async (_config) => {
|
|
760
799
|
const logger = await getOrCreateLogger(directory);
|