0xray 4.0.36 → 4.0.38
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/.grok-plugin/plugin.json +1 -1
- package/.xray/features.json +1 -1
- package/dist/CHANGELOG.md +28 -0
- package/dist/analytics/emerging-pattern-detector.d.ts +12 -1
- package/dist/analytics/emerging-pattern-detector.js +37 -12
- package/dist/analytics/pattern-learning-engine.js +1 -9
- package/dist/analytics/predictive-analytics.js +51 -33
- package/dist/analytics/prompt-pattern-analyzer.d.ts +1 -0
- package/dist/analytics/prompt-pattern-analyzer.js +39 -20
- package/dist/analytics/routing-performance-analyzer.d.ts +2 -0
- package/dist/analytics/routing-performance-analyzer.js +89 -37
- package/dist/analytics/simple-pattern-analyzer.d.ts +4 -0
- package/dist/analytics/simple-pattern-analyzer.js +66 -28
- package/dist/architect/architect-tools.d.ts +3 -0
- package/dist/architect/architect-tools.js +22 -0
- package/dist/cli/commands/grok-install.js +1 -1
- package/dist/cli/commands/hermes-install.js +11 -5
- package/dist/cli/commands/openclaw-install.js +13 -7
- package/dist/cli/commands/opencode-install.js +7 -4
- package/dist/cli/commands/suit-monitor.d.ts +30 -0
- package/dist/cli/commands/suit-monitor.js +128 -0
- package/dist/cli/index.js +52 -15
- package/dist/cli/suit-pulse.d.ts +29 -0
- package/dist/cli/suit-pulse.js +270 -0
- package/dist/delegation/agent-delegator.d.ts +4 -2
- package/dist/delegation/agent-delegator.js +46 -27
- package/dist/delegation/agent-expertise.js +2 -1
- package/dist/delegation/analytics/outcome-tracker.d.ts +6 -0
- package/dist/delegation/analytics/outcome-tracker.js +17 -3
- package/dist/delegation/ast-code-parser.d.ts +5 -0
- package/dist/delegation/ast-code-parser.js +53 -48
- package/dist/delegation/codebase-context-analyzer.d.ts +3 -7
- package/dist/delegation/codebase-context-analyzer.js +188 -159
- package/dist/delegation/session-coordinator.js +3 -2
- package/dist/enforcement/loaders/codex-loader.js +18 -14
- package/dist/enforcement/validators/architecture-validators.js +115 -9
- package/dist/governance/codex-policy.service.d.ts +12 -2
- package/dist/governance/codex-policy.service.js +91 -16
- package/dist/integrations/grok/hooks/session-start.js +6 -6
- package/dist/integrations/hooks/goggles-pipeline.mjs +169 -28
- package/dist/integrations/hooks/goggles-planes.json +172 -8
- package/dist/integrations/hooks/plates.cjs +138 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
- package/dist/integrations/hooks/write-home.cjs +59 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/architect-tools.server.d.ts +6 -0
- package/dist/mcps/architect-tools.server.js +19 -18
- package/dist/mcps/orchestrator/server.d.ts +4 -3
- package/dist/mcps/orchestrator/server.js +29 -17
- package/dist/memory-routing/lookup-cache.d.ts +8 -0
- package/dist/memory-routing/lookup-cache.js +72 -0
- package/dist/memory-routing/plates.d.ts +1 -1
- package/dist/memory-routing/plates.js +26 -0
- package/dist/memory-routing/provider-loader.js +3 -0
- package/dist/metrics/agent-metrics.d.ts +14 -5
- package/dist/metrics/agent-metrics.js +362 -224
- package/dist/nucleus/synthesis-consult-receipt.js +38 -16
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
- package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
- package/dist/orchestrator/orchestrator.js +1 -2
- package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
- package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
- package/dist/processors/implementations/console-log-guard-processor.js +39 -50
- package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
- package/dist/processors/implementations/performance-budget-processor.js +29 -38
- package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
- package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
- package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
- package/dist/reporting/framework-reporting-system.d.ts +3 -0
- package/dist/reporting/framework-reporting-system.js +61 -8
- package/dist/reporting/log-parser.js +188 -105
- package/dist/reporting/metrics.js +42 -18
- package/dist/security/prompt-security-validator.js +8 -7
- package/dist/security/security-auditor.d.ts +4 -0
- package/dist/security/security-auditor.js +132 -57
- package/dist/security/security-hardener.d.ts +2 -0
- package/dist/security/security-hardener.js +31 -6
- package/dist/security/security-headers.d.ts +2 -0
- package/dist/security/security-headers.js +23 -21
- package/dist/security/security-scanner.js +18 -4
- package/dist/session/session-cleanup-manager.d.ts +2 -0
- package/dist/session/session-cleanup-manager.js +29 -8
- package/dist/session/session-monitor.d.ts +14 -4
- package/dist/session/session-monitor.js +208 -106
- package/dist/session/session-state-manager.d.ts +11 -0
- package/dist/session/session-state-manager.js +90 -8
- package/docs-site/docs/plates/activity-log.md +38 -0
- package/docs-site/docs/plates/cursor-compact.md +35 -0
- package/docs-site/docs/plates/grok-compact.md +35 -0
- package/docs-site/docs/plates/index.md +25 -3
- package/docs-site/docs/plates/inference-files.md +35 -0
- package/docs-site/docs/plates/kept-line.md +35 -0
- package/docs-site/docs/plates/lens-gate.md +35 -0
- package/docs-site/docs/plates/lens-page.md +35 -0
- package/docs-site/docs/plates/notes-page.md +35 -0
- package/docs-site/docs/plates/package-face.md +35 -0
- package/docs-site/docs/plates/payload-heat.md +35 -0
- package/docs-site/docs/plates/pickup-stamp.md +35 -0
- package/docs-site/docs/plates/pre-tool.md +35 -0
- package/docs-site/docs/plates/record-map.md +41 -0
- package/docs-site/docs/plates/reflection-page.md +35 -0
- package/docs-site/docs/plates/review.md +57 -0
- package/docs-site/docs/plates/session-capture.md +38 -0
- package/docs-site/docs/plates/site-manual.md +35 -0
- package/docs-site/docs/plates/stamp-plate.md +35 -0
- package/docs-site/docs/plates/station-card.md +35 -0
- package/docs-site/docs/plates/station-heat.md +35 -0
- package/docs-site/docs/plates/suit-organs.md +38 -0
- package/docs-site/docs/plates/suit-settings.md +35 -0
- package/docs-site/docs/plates/suit-wear.md +37 -0
- package/docs-site/docs/plates/trail-state.md +35 -0
- package/docs-site/docs/plates/work-fresh.md +35 -0
- package/docs-site/docs/plates/write-home.md +38 -0
- package/package.json +3 -2
- package/scripts/node/install-bridges.cjs +302 -78
- package/scripts/node/postinstall.cjs +14 -3
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/architect-tools.server.ts +25 -20
- package/src/mcps/orchestrator/server.ts +33 -19
- package/xray/features.json +1 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.js.map +0 -1
package/.grok-plugin/plugin.json
CHANGED
package/.xray/features.json
CHANGED
package/dist/CHANGELOG.md
CHANGED
|
@@ -42,6 +42,34 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
|
|
|
42
42
|
- Archive root `integrations/` costume SKILL dump → `docs/archive/integrations-costume/`. Live wear is `src/integrations`.
|
|
43
43
|
- Archive root `commands/` stale copy → `docs/archive/commands-copy/`. Live wear is `src/opencode/commands`.
|
|
44
44
|
|
|
45
|
+
## [4.0.38] - 2026-10-03
|
|
46
|
+
|
|
47
|
+
### 🔄 Changes
|
|
48
|
+
|
|
49
|
+
### 🔎 Other Changes
|
|
50
|
+
- Merge pull request #171 from 0xRayAI/fix/solar-verdict-decides (7b6ac081a)
|
|
51
|
+
- Merge remote-tracking branch 'origin/main' into fix/solar-verdict-decides (a342e0b0d)
|
|
52
|
+
- fix(types): declare work-freshness so the Codex 70 import typechecks (16b2db194)
|
|
53
|
+
- feat(codex): term 70 requires current source and worn npm (dacecc916)
|
|
54
|
+
- fix(governance): honor Dynamo's recommendation (968cd6a14)
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## [4.0.37] - 2026-10-02
|
|
59
|
+
|
|
60
|
+
### 🔄 Changes
|
|
61
|
+
|
|
62
|
+
### 🐛 Bug Fixes
|
|
63
|
+
- fix: a naked checkout wakes with repertoire on (7b75bedbc)
|
|
64
|
+
|
|
65
|
+
### ⚡ Performance
|
|
66
|
+
- perf: cut repeated work on the processor data path (baadb4d35)
|
|
67
|
+
|
|
68
|
+
### 📚 Documentation
|
|
69
|
+
- docs: move fleet house SSOT out of the kit (7dcf08e94)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
45
73
|
## [4.0.36] - 2026-10-02
|
|
46
74
|
|
|
47
75
|
### 🔄 Changes
|
|
@@ -32,6 +32,10 @@ export declare class EmergingPatternDetector {
|
|
|
32
32
|
* Extract meaningful keywords from task description
|
|
33
33
|
*/
|
|
34
34
|
private extractKeywords;
|
|
35
|
+
/**
|
|
36
|
+
* Jaccard |A ∩ B| / |A ∪ B| without copying either set.
|
|
37
|
+
*/
|
|
38
|
+
private jaccard;
|
|
35
39
|
/**
|
|
36
40
|
* Calculate Jaccard similarity between two keyword sets
|
|
37
41
|
*/
|
|
@@ -43,7 +47,14 @@ export declare class EmergingPatternDetector {
|
|
|
43
47
|
/**
|
|
44
48
|
* Detect emergent patterns from recent routing outcomes
|
|
45
49
|
*/
|
|
46
|
-
detectEmergingPatterns(outcomes:
|
|
50
|
+
detectEmergingPatterns(outcomes: ReadonlyArray<{
|
|
51
|
+
taskId: string;
|
|
52
|
+
taskDescription?: string;
|
|
53
|
+
routedAgent: string;
|
|
54
|
+
routedSkill: string;
|
|
55
|
+
success?: boolean;
|
|
56
|
+
confidence: number;
|
|
57
|
+
}>): PatternDiscoveryResult;
|
|
47
58
|
/**
|
|
48
59
|
* Check if a specific pattern is emerging
|
|
49
60
|
*/
|
|
@@ -26,21 +26,32 @@ export class EmergingPatternDetector {
|
|
|
26
26
|
const words = text.toLowerCase()
|
|
27
27
|
.replace(/[^\w\s]/g, ' ')
|
|
28
28
|
.split(/\s+/)
|
|
29
|
-
.filter(word => word.length > 2)
|
|
30
|
-
.filter(word => !this.stopWords.has(word));
|
|
29
|
+
.filter(word => word.length > 2 && !this.stopWords.has(word));
|
|
31
30
|
return [...new Set(words)];
|
|
32
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Jaccard |A ∩ B| / |A ∪ B| without copying either set.
|
|
34
|
+
*/
|
|
35
|
+
jaccard(left, right) {
|
|
36
|
+
if (left.size === 0 || right.size === 0)
|
|
37
|
+
return 0;
|
|
38
|
+
let intersection = 0;
|
|
39
|
+
const small = left.size <= right.size ? left : right;
|
|
40
|
+
const large = small === left ? right : left;
|
|
41
|
+
for (const word of small) {
|
|
42
|
+
if (large.has(word))
|
|
43
|
+
intersection++;
|
|
44
|
+
}
|
|
45
|
+
const union = left.size + right.size - intersection;
|
|
46
|
+
return union === 0 ? 0 : intersection / union;
|
|
47
|
+
}
|
|
33
48
|
/**
|
|
34
49
|
* Calculate Jaccard similarity between two keyword sets
|
|
35
50
|
*/
|
|
36
51
|
calculateSimilarity(keywords1, keywords2) {
|
|
37
52
|
if (keywords1.length === 0 || keywords2.length === 0)
|
|
38
53
|
return 0;
|
|
39
|
-
|
|
40
|
-
const set2 = new Set(keywords2);
|
|
41
|
-
const intersection = new Set([...set1].filter(x => set2.has(x)));
|
|
42
|
-
const union = new Set([...set1, ...set2]);
|
|
43
|
-
return intersection.size / union.size;
|
|
54
|
+
return this.jaccard(new Set(keywords1), new Set(keywords2));
|
|
44
55
|
}
|
|
45
56
|
/**
|
|
46
57
|
* Cluster similar task descriptions
|
|
@@ -48,6 +59,10 @@ export class EmergingPatternDetector {
|
|
|
48
59
|
clusterTasks(tasks) {
|
|
49
60
|
const clusters = [];
|
|
50
61
|
const assigned = new Set();
|
|
62
|
+
const keywordSets = new Map();
|
|
63
|
+
for (const task of tasks) {
|
|
64
|
+
keywordSets.set(task, new Set(task.keywords));
|
|
65
|
+
}
|
|
51
66
|
// Sort by frequency (most frequent first)
|
|
52
67
|
const sortedTasks = [...tasks].sort((a, b) => b.keywords.length - a.keywords.length);
|
|
53
68
|
for (const task of sortedTasks) {
|
|
@@ -62,7 +77,7 @@ export class EmergingPatternDetector {
|
|
|
62
77
|
for (const otherTask of sortedTasks) {
|
|
63
78
|
if (assigned.has(otherTask.id) || otherTask.id === task.id)
|
|
64
79
|
continue;
|
|
65
|
-
const similarity = this.
|
|
80
|
+
const similarity = this.jaccard(clusterKeywords, keywordSets.get(otherTask) ?? new Set(otherTask.keywords));
|
|
66
81
|
if (similarity >= this.clusterSimilarityThreshold) {
|
|
67
82
|
otherTask.keywords.forEach(kw => clusterKeywords.add(kw));
|
|
68
83
|
clusterDescriptions.push(otherTask.description);
|
|
@@ -124,12 +139,22 @@ export class EmergingPatternDetector {
|
|
|
124
139
|
continue;
|
|
125
140
|
if (cluster.confidence < this.minConfidenceThreshold)
|
|
126
141
|
continue;
|
|
127
|
-
//
|
|
128
|
-
const
|
|
129
|
-
const
|
|
142
|
+
// Same membership as description includes, in taskData order.
|
|
143
|
+
const patternDescriptions = new Set(cluster.patterns);
|
|
144
|
+
const clusterOutcomes = [];
|
|
145
|
+
let successCount = 0;
|
|
146
|
+
let confidenceSum = 0;
|
|
147
|
+
for (const task of taskData) {
|
|
148
|
+
if (!patternDescriptions.has(task.description))
|
|
149
|
+
continue;
|
|
150
|
+
clusterOutcomes.push(task);
|
|
151
|
+
if (task.success)
|
|
152
|
+
successCount++;
|
|
153
|
+
confidenceSum += task.confidence;
|
|
154
|
+
}
|
|
130
155
|
const successRate = clusterOutcomes.length > 0 ? successCount / clusterOutcomes.length : 0;
|
|
131
156
|
const avgConfidence = clusterOutcomes.length > 0
|
|
132
|
-
?
|
|
157
|
+
? confidenceSum / clusterOutcomes.length
|
|
133
158
|
: 0;
|
|
134
159
|
// Determine suggested agent/skill based on most common
|
|
135
160
|
const agentCounts = new Map();
|
|
@@ -78,15 +78,7 @@ export class PatternLearningEngine {
|
|
|
78
78
|
}
|
|
79
79
|
// Detect emerging patterns for new additions
|
|
80
80
|
if (this.config.enableAutoAddition) {
|
|
81
|
-
const emergentResult = emergingPatternDetector.detectEmergingPatterns(outcomes
|
|
82
|
-
taskId: o.taskId,
|
|
83
|
-
taskDescription: o.taskDescription,
|
|
84
|
-
routedAgent: o.routedAgent,
|
|
85
|
-
routedSkill: o.routedSkill,
|
|
86
|
-
confidence: o.confidence,
|
|
87
|
-
timestamp: new Date(),
|
|
88
|
-
success: o.success
|
|
89
|
-
})));
|
|
81
|
+
const emergentResult = emergingPatternDetector.detectEmergingPatterns(outcomes);
|
|
90
82
|
result.newPatterns = this.generateNewPatterns(emergentResult.emergentPatterns, existingMappings);
|
|
91
83
|
}
|
|
92
84
|
// Generate recommendations
|
|
@@ -16,11 +16,16 @@ function keywordOverlapScore(description, historicalDescriptions) {
|
|
|
16
16
|
return 0;
|
|
17
17
|
let totalScore = 0;
|
|
18
18
|
let matchCount = 0;
|
|
19
|
+
const histWords = new Set();
|
|
19
20
|
for (const hist of historicalDescriptions) {
|
|
20
|
-
|
|
21
|
+
histWords.clear();
|
|
22
|
+
for (const word of hist.toLowerCase().split(/\W+/)) {
|
|
23
|
+
if (word.length > 2)
|
|
24
|
+
histWords.add(word);
|
|
25
|
+
}
|
|
21
26
|
let overlap = 0;
|
|
22
|
-
for (const
|
|
23
|
-
if (histWords.has(
|
|
27
|
+
for (const word of descWords) {
|
|
28
|
+
if (histWords.has(word))
|
|
24
29
|
overlap++;
|
|
25
30
|
}
|
|
26
31
|
if (overlap > 0) {
|
|
@@ -64,42 +69,50 @@ function estimateTaskDuration(agentOutcomes) {
|
|
|
64
69
|
*/
|
|
65
70
|
function buildAgentMetrics(agentOutcomes) {
|
|
66
71
|
const totalTasks = agentOutcomes.length;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
let successfulTasks = 0;
|
|
73
|
+
let durationSum = 0;
|
|
74
|
+
let durationCount = 0;
|
|
75
|
+
const recentStart = Math.max(0, totalTasks - 20);
|
|
76
|
+
const recentPerformance = [];
|
|
77
|
+
const taskCounts = new Map();
|
|
78
|
+
for (let i = 0; i < agentOutcomes.length; i++) {
|
|
79
|
+
const outcome = agentOutcomes[i];
|
|
80
|
+
if (!outcome)
|
|
81
|
+
continue;
|
|
82
|
+
if (outcome.success)
|
|
83
|
+
successfulTasks++;
|
|
84
|
+
if (outcome.executionTimeMs !== undefined) {
|
|
85
|
+
durationSum += outcome.executionTimeMs;
|
|
86
|
+
durationCount++;
|
|
87
|
+
}
|
|
83
88
|
const taskType = outcome.taskType || "unknown";
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
const bucket = taskCounts.get(taskType);
|
|
90
|
+
if (bucket) {
|
|
91
|
+
bucket.count++;
|
|
92
|
+
if (outcome.success)
|
|
93
|
+
bucket.successes++;
|
|
86
94
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const successes = taskOutcomes.filter(o => o.success).length;
|
|
93
|
-
const entry = taskTypeBreakdown[taskType];
|
|
94
|
-
if (entry) {
|
|
95
|
-
entry.successRate = successes / taskOutcomes.length;
|
|
95
|
+
else {
|
|
96
|
+
taskCounts.set(taskType, {
|
|
97
|
+
count: 1,
|
|
98
|
+
successes: outcome.success ? 1 : 0,
|
|
99
|
+
});
|
|
96
100
|
}
|
|
101
|
+
if (i >= recentStart)
|
|
102
|
+
recentPerformance.push(outcome.success ? 1 : 0);
|
|
103
|
+
}
|
|
104
|
+
const taskTypeBreakdown = {};
|
|
105
|
+
for (const [taskType, bucket] of taskCounts) {
|
|
106
|
+
taskTypeBreakdown[taskType] = {
|
|
107
|
+
count: bucket.count,
|
|
108
|
+
successRate: bucket.count > 0 ? bucket.successes / bucket.count : 0,
|
|
109
|
+
};
|
|
97
110
|
}
|
|
98
111
|
return {
|
|
99
112
|
totalTasks,
|
|
100
113
|
successfulTasks,
|
|
101
|
-
failedTasks,
|
|
102
|
-
averageExecutionTime,
|
|
114
|
+
failedTasks: totalTasks - successfulTasks,
|
|
115
|
+
averageExecutionTime: durationCount > 0 ? durationSum / durationCount : 0,
|
|
103
116
|
successRate: totalTasks > 0 ? successfulTasks / totalTasks : 0,
|
|
104
117
|
recentPerformance,
|
|
105
118
|
taskTypeBreakdown,
|
|
@@ -200,6 +213,11 @@ export const predictiveAnalytics = {
|
|
|
200
213
|
const outcomes = routingOutcomeTracker.getOutcomes();
|
|
201
214
|
if (stats.length === 0 || outcomes.length === 0)
|
|
202
215
|
return null;
|
|
216
|
+
const statByAgent = new Map();
|
|
217
|
+
for (const stat of stats) {
|
|
218
|
+
if (!statByAgent.has(stat.agent))
|
|
219
|
+
statByAgent.set(stat.agent, stat);
|
|
220
|
+
}
|
|
203
221
|
// Group outcomes by agent
|
|
204
222
|
const byAgent = new Map();
|
|
205
223
|
for (const o of outcomes) {
|
|
@@ -215,7 +233,7 @@ export const predictiveAnalytics = {
|
|
|
215
233
|
for (const [agent, agentOutcomes] of byAgent) {
|
|
216
234
|
const descriptions = agentOutcomes.map(o => o.taskDescription);
|
|
217
235
|
const overlap = keywordOverlapScore(taskDescription, descriptions);
|
|
218
|
-
const agentStat =
|
|
236
|
+
const agentStat = statByAgent.get(agent);
|
|
219
237
|
const successRate = agentStat?.successRate ?? 0;
|
|
220
238
|
const total = agentStat?.total ?? 0;
|
|
221
239
|
// Weighted score: 70% keyword overlap + 30% historical success rate
|
|
@@ -20,17 +20,22 @@ class PromptPatternAnalyzer {
|
|
|
20
20
|
if (promptData.length === 0) {
|
|
21
21
|
return this.emptyComparisonResult();
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
let templateMatchCount = 0;
|
|
24
|
+
for (const prompt of promptData) {
|
|
25
|
+
if (prompt.templatePrompt && prompt.templatePrompt.length > 0)
|
|
26
|
+
templateMatchCount++;
|
|
27
|
+
}
|
|
28
|
+
const keywordsByPrompt = promptData.map((prompt) => this.extractKeywords(prompt.userRequest || ""));
|
|
29
|
+
const outcomeByTask = this.indexOutcomes(outcomes);
|
|
30
|
+
const gaps = this.detectTemplateGaps(promptData, outcomeByTask);
|
|
31
|
+
const emergingPatterns = this.identifyEmergingPatterns(promptData, keywordsByPrompt, outcomeByTask);
|
|
32
|
+
const topMissedKeywords = this.analyzeMissedKeywords(promptData, keywordsByPrompt);
|
|
28
33
|
const agentCoverage = this.calculateAgentCoverage(promptData);
|
|
29
34
|
return {
|
|
30
35
|
totalPrompts: promptData.length,
|
|
31
|
-
templateMatches:
|
|
32
|
-
nonTemplatePrompts:
|
|
33
|
-
templateMatchRate: promptData.length > 0 ?
|
|
36
|
+
templateMatches: templateMatchCount,
|
|
37
|
+
nonTemplatePrompts: promptData.length - templateMatchCount,
|
|
38
|
+
templateMatchRate: promptData.length > 0 ? templateMatchCount / promptData.length : 0,
|
|
34
39
|
gaps,
|
|
35
40
|
emergingPatterns,
|
|
36
41
|
topMissedKeywords,
|
|
@@ -40,7 +45,15 @@ class PromptPatternAnalyzer {
|
|
|
40
45
|
/**
|
|
41
46
|
* Detect gaps where templates don't match user requests
|
|
42
47
|
*/
|
|
43
|
-
|
|
48
|
+
indexOutcomes(outcomes) {
|
|
49
|
+
const indexed = new Map();
|
|
50
|
+
for (const outcome of outcomes) {
|
|
51
|
+
if (!indexed.has(outcome.taskId))
|
|
52
|
+
indexed.set(outcome.taskId, outcome);
|
|
53
|
+
}
|
|
54
|
+
return indexed;
|
|
55
|
+
}
|
|
56
|
+
detectTemplateGaps(promptData, outcomeByTask) {
|
|
44
57
|
const gaps = [];
|
|
45
58
|
const gapMap = new Map();
|
|
46
59
|
for (const prompt of promptData) {
|
|
@@ -54,7 +67,7 @@ class PromptPatternAnalyzer {
|
|
|
54
67
|
}
|
|
55
68
|
}
|
|
56
69
|
else {
|
|
57
|
-
const outcome =
|
|
70
|
+
const outcome = outcomeByTask.get(prompt.taskId);
|
|
58
71
|
gaps.push({
|
|
59
72
|
gapType: this.classifyGapType(prompt, outcome),
|
|
60
73
|
userRequest: prompt.userRequest || '',
|
|
@@ -79,11 +92,10 @@ class PromptPatternAnalyzer {
|
|
|
79
92
|
/**
|
|
80
93
|
* Identify emerging patterns from real usage
|
|
81
94
|
*/
|
|
82
|
-
identifyEmergingPatterns(promptData,
|
|
95
|
+
identifyEmergingPatterns(promptData, keywordsByPrompt, outcomeByTask) {
|
|
83
96
|
const patternMap = new Map();
|
|
84
97
|
const keywordFrequency = new Map();
|
|
85
|
-
for (const
|
|
86
|
-
const keywords = this.extractKeywords(prompt.userRequest || '');
|
|
98
|
+
for (const keywords of keywordsByPrompt) {
|
|
87
99
|
for (const keyword of keywords) {
|
|
88
100
|
keywordFrequency.set(keyword, (keywordFrequency.get(keyword) || 0) + 1);
|
|
89
101
|
}
|
|
@@ -92,13 +104,17 @@ class PromptPatternAnalyzer {
|
|
|
92
104
|
.filter(([, count]) => count >= this.minFrequencyThreshold)
|
|
93
105
|
.sort((a, b) => b[1] - a[1])
|
|
94
106
|
.slice(0, 50);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
107
|
+
const significantSet = new Set(significantKeywords.map(([keyword]) => keyword));
|
|
108
|
+
for (let i = 0; i < promptData.length; i++) {
|
|
109
|
+
const prompt = promptData[i];
|
|
110
|
+
const keywords = keywordsByPrompt[i];
|
|
111
|
+
if (!prompt || !keywords)
|
|
112
|
+
continue;
|
|
113
|
+
const significantKeywordsInPrompt = keywords.filter((kw) => significantSet.has(kw));
|
|
98
114
|
if (significantKeywordsInPrompt.length === 0)
|
|
99
115
|
continue;
|
|
100
116
|
const patternKey = significantKeywordsInPrompt.sort().join("|");
|
|
101
|
-
const outcome =
|
|
117
|
+
const outcome = outcomeByTask.get(prompt.taskId);
|
|
102
118
|
if (!outcome)
|
|
103
119
|
continue;
|
|
104
120
|
if (patternMap.has(patternKey)) {
|
|
@@ -139,12 +155,15 @@ class PromptPatternAnalyzer {
|
|
|
139
155
|
/**
|
|
140
156
|
* Analyze frequently missed keywords
|
|
141
157
|
*/
|
|
142
|
-
analyzeMissedKeywords(promptData) {
|
|
158
|
+
analyzeMissedKeywords(promptData, keywordsByPrompt) {
|
|
143
159
|
const missedKeywords = new Map();
|
|
144
160
|
const keywordToAgent = new Map();
|
|
145
|
-
for (
|
|
161
|
+
for (let i = 0; i < promptData.length; i++) {
|
|
162
|
+
const prompt = promptData[i];
|
|
163
|
+
const keywords = keywordsByPrompt[i];
|
|
164
|
+
if (!prompt || !keywords)
|
|
165
|
+
continue;
|
|
146
166
|
if (!prompt.templatePrompt || prompt.templatePrompt.length === 0) {
|
|
147
|
-
const keywords = this.extractKeywords(prompt.userRequest || '');
|
|
148
167
|
for (const keyword of keywords) {
|
|
149
168
|
if (keyword.length > 3) {
|
|
150
169
|
missedKeywords.set(keyword, (missedKeywords.get(keyword) || 0) + 1);
|
|
@@ -62,6 +62,8 @@ declare class RoutingPerformanceAnalyzer {
|
|
|
62
62
|
private readonly lowConfidenceThreshold;
|
|
63
63
|
private readonly mediumConfidenceThreshold;
|
|
64
64
|
private readonly minSamplesForMetrics;
|
|
65
|
+
/** First row for each taskId, matching Array.find. */
|
|
66
|
+
private firstByTaskId;
|
|
65
67
|
/**
|
|
66
68
|
* Generate comprehensive routing performance report
|
|
67
69
|
*/
|
|
@@ -11,6 +11,15 @@ class RoutingPerformanceAnalyzer {
|
|
|
11
11
|
lowConfidenceThreshold = 0.6;
|
|
12
12
|
mediumConfidenceThreshold = 0.8;
|
|
13
13
|
minSamplesForMetrics = 5;
|
|
14
|
+
/** First row for each taskId, matching Array.find. */
|
|
15
|
+
firstByTaskId(rows) {
|
|
16
|
+
const indexed = new Map();
|
|
17
|
+
for (const row of rows) {
|
|
18
|
+
if (!indexed.has(row.taskId))
|
|
19
|
+
indexed.set(row.taskId, row);
|
|
20
|
+
}
|
|
21
|
+
return indexed;
|
|
22
|
+
}
|
|
14
23
|
/**
|
|
15
24
|
* Generate comprehensive routing performance report
|
|
16
25
|
*/
|
|
@@ -25,7 +34,7 @@ class RoutingPerformanceAnalyzer {
|
|
|
25
34
|
}
|
|
26
35
|
const agentMetrics = this.calculateAgentMetrics(outcomes, promptData);
|
|
27
36
|
const keywordEffectiveness = this.analyzeKeywordEffectiveness(decisions, outcomes);
|
|
28
|
-
const confidenceMetrics = this.analyzeConfidenceThresholds(outcomes);
|
|
37
|
+
const confidenceMetrics = this.analyzeConfidenceThresholds(outcomes, promptData);
|
|
29
38
|
const timeRange = this.calculateTimeRange(outcomes);
|
|
30
39
|
const overallStats = this.calculateOverallStats(outcomes);
|
|
31
40
|
const recommendations = this.generateRecommendations(agentMetrics, keywordEffectiveness, confidenceMetrics);
|
|
@@ -45,9 +54,10 @@ class RoutingPerformanceAnalyzer {
|
|
|
45
54
|
*/
|
|
46
55
|
calculateAgentMetrics(outcomes, promptData) {
|
|
47
56
|
const agentMap = new Map();
|
|
57
|
+
const promptByTask = this.firstByTaskId(promptData);
|
|
48
58
|
for (const outcome of outcomes) {
|
|
49
59
|
const agent = outcome.routedAgent;
|
|
50
|
-
const prompt =
|
|
60
|
+
const prompt = promptByTask.get(outcome.taskId);
|
|
51
61
|
const confidence = prompt?.confidence ?? 0;
|
|
52
62
|
const executionTime = prompt?.usageMetadata?.retryCount ?? 0;
|
|
53
63
|
if (!agentMap.has(agent)) {
|
|
@@ -58,7 +68,7 @@ class RoutingPerformanceAnalyzer {
|
|
|
58
68
|
failures: 0,
|
|
59
69
|
escalated: 0,
|
|
60
70
|
confidenceSum: 0,
|
|
61
|
-
|
|
71
|
+
executionSum: 0,
|
|
62
72
|
highConfidence: 0,
|
|
63
73
|
mediumConfidence: 0,
|
|
64
74
|
lowConfidence: 0,
|
|
@@ -91,15 +101,12 @@ class RoutingPerformanceAnalyzer {
|
|
|
91
101
|
if (confidence < 0.75) {
|
|
92
102
|
metrics.escalated++;
|
|
93
103
|
}
|
|
94
|
-
metrics.
|
|
104
|
+
metrics.executionSum += executionTime;
|
|
95
105
|
}
|
|
96
106
|
return Array.from(agentMap.entries())
|
|
97
107
|
.filter(([, metrics]) => metrics.total >= this.minSamplesForMetrics)
|
|
98
108
|
.map(([agent, metrics]) => {
|
|
99
|
-
const avgExecutionTime = metrics.
|
|
100
|
-
? metrics.executionTimes.reduce((sum, time) => sum + time, 0) /
|
|
101
|
-
metrics.executionTimes.length
|
|
102
|
-
: 0;
|
|
109
|
+
const avgExecutionTime = metrics.total > 0 ? metrics.executionSum / metrics.total : 0;
|
|
103
110
|
const timestamps = metrics.timestamps.sort((a, b) => a.getTime() - b.getTime());
|
|
104
111
|
return {
|
|
105
112
|
agent,
|
|
@@ -129,11 +136,12 @@ class RoutingPerformanceAnalyzer {
|
|
|
129
136
|
*/
|
|
130
137
|
analyzeKeywordEffectiveness(decisions, outcomes) {
|
|
131
138
|
const keywordMap = new Map();
|
|
139
|
+
const outcomeByTask = this.firstByTaskId(outcomes);
|
|
132
140
|
for (const decision of decisions) {
|
|
133
141
|
const keyword = decision.keywordMatched;
|
|
134
142
|
if (!keyword)
|
|
135
143
|
continue;
|
|
136
|
-
const outcome =
|
|
144
|
+
const outcome = outcomeByTask.get(decision.taskId);
|
|
137
145
|
if (!outcome)
|
|
138
146
|
continue;
|
|
139
147
|
if (!keywordMap.has(keyword)) {
|
|
@@ -175,30 +183,43 @@ class RoutingPerformanceAnalyzer {
|
|
|
175
183
|
/**
|
|
176
184
|
* Analyze confidence threshold performance
|
|
177
185
|
*/
|
|
178
|
-
analyzeConfidenceThresholds(outcomes) {
|
|
179
|
-
const promptData = routingOutcomeTracker.getPromptData();
|
|
186
|
+
analyzeConfidenceThresholds(outcomes, promptData) {
|
|
180
187
|
const thresholds = [0.5, 0.6, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
const promptByTask = this.firstByTaskId(promptData);
|
|
189
|
+
const totalRoutings = thresholds.map(() => 0);
|
|
190
|
+
const successfulRoutings = thresholds.map(() => 0);
|
|
191
|
+
for (const outcome of outcomes) {
|
|
192
|
+
const prompt = promptByTask.get(outcome.taskId);
|
|
193
|
+
const confidence = prompt?.confidence ?? 1;
|
|
194
|
+
for (let i = 0; i < thresholds.length; i++) {
|
|
195
|
+
const threshold = thresholds[i];
|
|
196
|
+
if (threshold === undefined || !(confidence < threshold))
|
|
197
|
+
continue;
|
|
198
|
+
const total = totalRoutings[i];
|
|
199
|
+
const successful = successfulRoutings[i];
|
|
200
|
+
if (total === undefined || successful === undefined)
|
|
201
|
+
continue;
|
|
202
|
+
totalRoutings[i] = total + 1;
|
|
203
|
+
if (outcome.success === true)
|
|
204
|
+
successfulRoutings[i] = successful + 1;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return thresholds.map((threshold, i) => {
|
|
208
|
+
const total = totalRoutings[i] ?? 0;
|
|
209
|
+
const successful = successfulRoutings[i] ?? 0;
|
|
210
|
+
const successRate = total > 0 ? successful / total : 0;
|
|
211
|
+
const escalatedCount = total;
|
|
191
212
|
let recommendation = "Threshold is optimal";
|
|
192
|
-
if (successRate < 0.6 && escalatedCount >
|
|
213
|
+
if (successRate < 0.6 && escalatedCount > total * 0.5) {
|
|
193
214
|
recommendation = "Consider lowering threshold to reduce escalations";
|
|
194
215
|
}
|
|
195
|
-
else if (successRate > 0.9 && escalatedCount <
|
|
216
|
+
else if (successRate > 0.9 && escalatedCount < total * 0.2) {
|
|
196
217
|
recommendation = "Consider raising threshold to improve routing precision";
|
|
197
218
|
}
|
|
198
219
|
return {
|
|
199
220
|
threshold,
|
|
200
|
-
totalRoutings,
|
|
201
|
-
successfulRoutings,
|
|
221
|
+
totalRoutings: total,
|
|
222
|
+
successfulRoutings: successful,
|
|
202
223
|
successRate,
|
|
203
224
|
escalatedCount,
|
|
204
225
|
recommendation,
|
|
@@ -209,10 +230,14 @@ class RoutingPerformanceAnalyzer {
|
|
|
209
230
|
* Calculate overall routing statistics
|
|
210
231
|
*/
|
|
211
232
|
calculateOverallStats(outcomes) {
|
|
212
|
-
|
|
233
|
+
let successful = 0;
|
|
234
|
+
let totalConfidence = 0;
|
|
235
|
+
for (const outcome of outcomes) {
|
|
236
|
+
if (outcome.success === true)
|
|
237
|
+
successful++;
|
|
238
|
+
totalConfidence += outcome.confidence || 0;
|
|
239
|
+
}
|
|
213
240
|
const successRate = outcomes.length > 0 ? successful / outcomes.length : 0;
|
|
214
|
-
// Confidence is in outcomes, not promptData
|
|
215
|
-
const totalConfidence = outcomes.reduce((sum, o) => sum + (o.confidence || 0), 0);
|
|
216
241
|
const avgConfidence = outcomes.length > 0 ? totalConfidence / outcomes.length : 0;
|
|
217
242
|
return { successRate, avgConfidence };
|
|
218
243
|
}
|
|
@@ -228,16 +253,43 @@ class RoutingPerformanceAnalyzer {
|
|
|
228
253
|
const d = ts instanceof Date ? ts : new Date(ts);
|
|
229
254
|
return d.getTime();
|
|
230
255
|
};
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
start: firstTs instanceof Date ? firstTs : (firstTs ? new Date(firstTs) : now),
|
|
239
|
-
end: lastTs instanceof Date ? lastTs : (lastTs ? new Date(lastTs) : now),
|
|
256
|
+
const asRange = (firstTs, lastTs) => {
|
|
257
|
+
const now = new Date();
|
|
258
|
+
return {
|
|
259
|
+
start: firstTs instanceof Date ? firstTs : (firstTs ? new Date(firstTs) : now),
|
|
260
|
+
end: lastTs instanceof Date ? lastTs : (lastTs ? new Date(lastTs) : now),
|
|
261
|
+
};
|
|
240
262
|
};
|
|
263
|
+
const firstOutcome = outcomes[0];
|
|
264
|
+
if (firstOutcome) {
|
|
265
|
+
let startTs = firstOutcome.timestamp;
|
|
266
|
+
let endTs = startTs;
|
|
267
|
+
let startTime = getTime(startTs);
|
|
268
|
+
let endTime = startTime;
|
|
269
|
+
let finite = !Number.isNaN(startTime);
|
|
270
|
+
for (let i = 1; finite && i < outcomes.length; i++) {
|
|
271
|
+
const outcome = outcomes[i];
|
|
272
|
+
if (!outcome)
|
|
273
|
+
continue;
|
|
274
|
+
const time = getTime(outcome.timestamp);
|
|
275
|
+
if (Number.isNaN(time)) {
|
|
276
|
+
finite = false;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
if (time < startTime) {
|
|
280
|
+
startTime = time;
|
|
281
|
+
startTs = outcome.timestamp;
|
|
282
|
+
}
|
|
283
|
+
if (time >= endTime) {
|
|
284
|
+
endTime = time;
|
|
285
|
+
endTs = outcome.timestamp;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (finite)
|
|
289
|
+
return asRange(startTs, endTs);
|
|
290
|
+
}
|
|
291
|
+
const sorted = [...outcomes].sort((a, b) => getTime(a.timestamp) - getTime(b.timestamp));
|
|
292
|
+
return asRange(sorted[0]?.timestamp, sorted[sorted.length - 1]?.timestamp);
|
|
241
293
|
}
|
|
242
294
|
/**
|
|
243
295
|
* Generate recommendations based on analysis
|
|
@@ -66,6 +66,10 @@ export declare class SimplePatternAnalyzer {
|
|
|
66
66
|
* Calculate statistics from parsed entries
|
|
67
67
|
*/
|
|
68
68
|
private calculateStats;
|
|
69
|
+
/**
|
|
70
|
+
* Earliest and latest timestamp. Matches a stable sort on finite times.
|
|
71
|
+
*/
|
|
72
|
+
private entryDateRange;
|
|
69
73
|
/**
|
|
70
74
|
* Generate human-readable insights
|
|
71
75
|
*/
|