0xray 4.0.35 → 4.0.37
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 +25 -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/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
- package/dist/integrations/hooks/goggles-pipeline.mjs +221 -44
- 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 +4 -2
- package/scripts/mjs/run-grok-hook.mjs +50 -0
- package/scripts/node/install-bridges.cjs +305 -81
- package/scripts/node/postinstall.cjs +14 -3
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- 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
|
@@ -7,6 +7,7 @@ const DEFAULT_RETENTION_CONFIG = {
|
|
|
7
7
|
cleanupIntervalMs: 60 * 60 * 1000, // 1 hour
|
|
8
8
|
};
|
|
9
9
|
const INVOCATION_STORE_KEY = "agent_invocations";
|
|
10
|
+
const periodKeyCache = new WeakMap();
|
|
10
11
|
export class AgentMetricsSystem {
|
|
11
12
|
stateManager;
|
|
12
13
|
retentionConfig;
|
|
@@ -50,6 +51,61 @@ export class AgentMetricsSystem {
|
|
|
50
51
|
generateId() {
|
|
51
52
|
return `inv-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
52
53
|
}
|
|
54
|
+
newAgentAcc() {
|
|
55
|
+
return {
|
|
56
|
+
count: 0,
|
|
57
|
+
success: 0,
|
|
58
|
+
duration: 0,
|
|
59
|
+
complexity: 0,
|
|
60
|
+
minTs: Number.POSITIVE_INFINITY,
|
|
61
|
+
maxTs: Number.NEGATIVE_INFINITY,
|
|
62
|
+
operations: new Set(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
addAgent(acc, inv) {
|
|
66
|
+
acc.count += 1;
|
|
67
|
+
if (inv.success)
|
|
68
|
+
acc.success += 1;
|
|
69
|
+
acc.duration += inv.duration;
|
|
70
|
+
acc.complexity += inv.complexityScore;
|
|
71
|
+
acc.minTs = Math.min(acc.minTs, inv.timestamp);
|
|
72
|
+
acc.maxTs = Math.max(acc.maxTs, inv.timestamp);
|
|
73
|
+
acc.operations.add(inv.operation);
|
|
74
|
+
}
|
|
75
|
+
finishAgent(agentName, acc) {
|
|
76
|
+
return {
|
|
77
|
+
agentName,
|
|
78
|
+
totalInvocations: acc.count,
|
|
79
|
+
successfulInvocations: acc.success,
|
|
80
|
+
failedInvocations: acc.count - acc.success,
|
|
81
|
+
successRate: (acc.success / acc.count) * 100,
|
|
82
|
+
averageDuration: acc.duration / acc.count,
|
|
83
|
+
averageComplexity: acc.complexity / acc.count,
|
|
84
|
+
lastInvoked: acc.maxTs,
|
|
85
|
+
firstInvoked: acc.minTs,
|
|
86
|
+
operations: [...acc.operations],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
newCountAcc() {
|
|
90
|
+
return { count: 0, success: 0, duration: 0, agents: {} };
|
|
91
|
+
}
|
|
92
|
+
addCount(acc, inv) {
|
|
93
|
+
acc.count += 1;
|
|
94
|
+
if (inv.success)
|
|
95
|
+
acc.success += 1;
|
|
96
|
+
acc.duration += inv.duration;
|
|
97
|
+
acc.agents[inv.agentName] = (acc.agents[inv.agentName] || 0) + 1;
|
|
98
|
+
}
|
|
99
|
+
finishCount(acc) {
|
|
100
|
+
return {
|
|
101
|
+
totalInvocations: acc.count,
|
|
102
|
+
successfulInvocations: acc.success,
|
|
103
|
+
failedInvocations: acc.count - acc.success,
|
|
104
|
+
successRate: (acc.success / acc.count) * 100,
|
|
105
|
+
averageDuration: acc.duration / acc.count,
|
|
106
|
+
agents: acc.agents,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
53
109
|
trackInvocation(params) {
|
|
54
110
|
const invocation = {
|
|
55
111
|
id: this.generateId(),
|
|
@@ -106,33 +162,52 @@ export class AgentMetricsSystem {
|
|
|
106
162
|
return this.getInvocations().filter((inv) => inv.timestamp >= start && inv.timestamp <= end);
|
|
107
163
|
}
|
|
108
164
|
filterInvocations(filter) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
165
|
+
const invocations = this.getInvocations();
|
|
166
|
+
const names = filter.agentNames;
|
|
167
|
+
const types = filter.agentTypes;
|
|
168
|
+
const levels = filter.complexityLevels;
|
|
169
|
+
const range = filter.timeRange;
|
|
170
|
+
const sessionId = filter.sessionId;
|
|
171
|
+
const hasNames = !!names && names.length > 0;
|
|
172
|
+
const hasTypes = !!types && types.length > 0;
|
|
173
|
+
const hasLevels = !!levels && levels.length > 0;
|
|
174
|
+
const successOnly = !!filter.successOnly;
|
|
175
|
+
const failureOnly = !!filter.failureOnly;
|
|
176
|
+
const hasSession = !!sessionId;
|
|
177
|
+
if (!hasNames &&
|
|
178
|
+
!hasTypes &&
|
|
179
|
+
!hasLevels &&
|
|
180
|
+
!range &&
|
|
181
|
+
!successOnly &&
|
|
182
|
+
!failureOnly &&
|
|
183
|
+
!hasSession) {
|
|
184
|
+
return invocations;
|
|
128
185
|
}
|
|
129
|
-
|
|
130
|
-
|
|
186
|
+
const matched = [];
|
|
187
|
+
for (const inv of invocations) {
|
|
188
|
+
if (hasNames && names && !names.includes(inv.agentName))
|
|
189
|
+
continue;
|
|
190
|
+
if (hasTypes && types && !types.includes(inv.agentType))
|
|
191
|
+
continue;
|
|
192
|
+
if (range && (inv.timestamp < range.start || inv.timestamp > range.end))
|
|
193
|
+
continue;
|
|
194
|
+
if (hasLevels && levels && !levels.includes(inv.complexityLevel))
|
|
195
|
+
continue;
|
|
196
|
+
if (successOnly && !inv.success)
|
|
197
|
+
continue;
|
|
198
|
+
if (failureOnly && inv.success)
|
|
199
|
+
continue;
|
|
200
|
+
if (hasSession && inv.sessionId !== sessionId)
|
|
201
|
+
continue;
|
|
202
|
+
matched.push(inv);
|
|
131
203
|
}
|
|
132
|
-
return
|
|
204
|
+
return matched;
|
|
133
205
|
}
|
|
134
206
|
aggregateMetrics(filter) {
|
|
135
207
|
const invocations = filter ? this.filterInvocations(filter) : this.getInvocations();
|
|
208
|
+
return this.aggregateInvocations(invocations);
|
|
209
|
+
}
|
|
210
|
+
aggregateInvocations(invocations) {
|
|
136
211
|
if (invocations.length === 0) {
|
|
137
212
|
return {
|
|
138
213
|
summary: {
|
|
@@ -147,139 +222,138 @@ export class AgentMetricsSystem {
|
|
|
147
222
|
byComplexity: {},
|
|
148
223
|
};
|
|
149
224
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
summary: {
|
|
155
|
-
totalInvocations: invocations.length,
|
|
156
|
-
totalAgents: new Set(invocations.map((inv) => inv.agentName)).size,
|
|
157
|
-
timeRange: {
|
|
158
|
-
start: Math.min(...timestamps),
|
|
159
|
-
end: Math.max(...timestamps),
|
|
160
|
-
},
|
|
161
|
-
overallSuccessRate: (successfulCount / invocations.length) * 100,
|
|
162
|
-
averageDuration: totalDuration / invocations.length,
|
|
163
|
-
},
|
|
164
|
-
byAgent: {},
|
|
165
|
-
byTimePeriod: {},
|
|
166
|
-
byComplexity: {},
|
|
167
|
-
};
|
|
225
|
+
let totalDuration = 0;
|
|
226
|
+
let successfulCount = 0;
|
|
227
|
+
let minTs = Number.POSITIVE_INFINITY;
|
|
228
|
+
let maxTs = Number.NEGATIVE_INFINITY;
|
|
168
229
|
const byAgent = new Map();
|
|
169
230
|
const byTimePeriod = new Map();
|
|
170
231
|
const byComplexity = new Map();
|
|
232
|
+
const cursor = this.freshPeriodCursor();
|
|
171
233
|
for (const inv of invocations) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// Group by day
|
|
182
|
-
const dayKey = this.getDayKey(inv.timestamp);
|
|
183
|
-
const dayInvs = byTimePeriod.get(dayKey) || [];
|
|
184
|
-
dayInvs.push(inv);
|
|
185
|
-
byTimePeriod.set(dayKey, dayInvs);
|
|
186
|
-
// Group by week
|
|
187
|
-
const weekKey = this.getWeekKey(inv.timestamp);
|
|
188
|
-
const weekInvs = byTimePeriod.get(weekKey) || [];
|
|
189
|
-
weekInvs.push(inv);
|
|
190
|
-
byTimePeriod.set(weekKey, weekInvs);
|
|
191
|
-
// Group by month
|
|
192
|
-
const monthKey = this.getMonthKey(inv.timestamp);
|
|
193
|
-
const monthInvs = byTimePeriod.get(monthKey) || [];
|
|
194
|
-
monthInvs.push(inv);
|
|
195
|
-
byTimePeriod.set(monthKey, monthInvs);
|
|
196
|
-
// Group by complexity
|
|
197
|
-
const complexityKey = inv.complexityLevel;
|
|
198
|
-
const complexityInvs = byComplexity.get(complexityKey) || [];
|
|
199
|
-
complexityInvs.push(inv);
|
|
200
|
-
byComplexity.set(complexityKey, complexityInvs);
|
|
201
|
-
}
|
|
202
|
-
// Build agent summaries
|
|
203
|
-
for (const [agentName, agentInvs] of byAgent) {
|
|
204
|
-
const successful = agentInvs.filter((inv) => inv.success);
|
|
205
|
-
const timestamps = agentInvs.map((inv) => inv.timestamp);
|
|
206
|
-
const durations = agentInvs.map((inv) => inv.duration);
|
|
207
|
-
const complexityScores = agentInvs.map((inv) => inv.complexityScore);
|
|
208
|
-
aggregated.byAgent[agentName] = {
|
|
209
|
-
agentName,
|
|
210
|
-
totalInvocations: agentInvs.length,
|
|
211
|
-
successfulInvocations: successful.length,
|
|
212
|
-
failedInvocations: agentInvs.length - successful.length,
|
|
213
|
-
successRate: (successful.length / agentInvs.length) * 100,
|
|
214
|
-
averageDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
|
|
215
|
-
averageComplexity: complexityScores.reduce((a, b) => a + b, 0) / complexityScores.length,
|
|
216
|
-
lastInvoked: Math.max(...timestamps),
|
|
217
|
-
firstInvoked: Math.min(...timestamps),
|
|
218
|
-
operations: [...new Set(agentInvs.map((inv) => inv.operation))],
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
// Build time period summaries
|
|
222
|
-
for (const [period, periodInvs] of byTimePeriod) {
|
|
223
|
-
const successful = periodInvs.filter((inv) => inv.success);
|
|
224
|
-
const durations = periodInvs.map((inv) => inv.duration);
|
|
225
|
-
const agents = {};
|
|
226
|
-
const periodType = this.getPeriodType(period);
|
|
227
|
-
for (const inv of periodInvs) {
|
|
228
|
-
agents[inv.agentName] = (agents[inv.agentName] || 0) + 1;
|
|
234
|
+
totalDuration += inv.duration;
|
|
235
|
+
if (inv.success)
|
|
236
|
+
successfulCount += 1;
|
|
237
|
+
minTs = Math.min(minTs, inv.timestamp);
|
|
238
|
+
maxTs = Math.max(maxTs, inv.timestamp);
|
|
239
|
+
let agentAcc = byAgent.get(inv.agentName);
|
|
240
|
+
if (!agentAcc) {
|
|
241
|
+
agentAcc = this.newAgentAcc();
|
|
242
|
+
byAgent.set(inv.agentName, agentAcc);
|
|
229
243
|
}
|
|
230
|
-
|
|
244
|
+
this.addAgent(agentAcc, inv);
|
|
245
|
+
const keys = this.periodKeys(inv, cursor);
|
|
246
|
+
this.addPeriod(byTimePeriod, keys.hour, inv);
|
|
247
|
+
this.addPeriod(byTimePeriod, keys.day, inv);
|
|
248
|
+
this.addPeriod(byTimePeriod, keys.week, inv);
|
|
249
|
+
this.addPeriod(byTimePeriod, keys.month, inv);
|
|
250
|
+
this.addPeriod(byComplexity, inv.complexityLevel, inv);
|
|
251
|
+
}
|
|
252
|
+
const byAgentSummary = {};
|
|
253
|
+
for (const [agentName, acc] of byAgent) {
|
|
254
|
+
byAgentSummary[agentName] = this.finishAgent(agentName, acc);
|
|
255
|
+
}
|
|
256
|
+
const byTimeSummary = {};
|
|
257
|
+
for (const [period, acc] of byTimePeriod) {
|
|
258
|
+
byTimeSummary[period] = {
|
|
231
259
|
period,
|
|
232
|
-
periodType,
|
|
233
|
-
|
|
234
|
-
successfulInvocations: successful.length,
|
|
235
|
-
failedInvocations: periodInvs.length - successful.length,
|
|
236
|
-
successRate: (successful.length / periodInvs.length) * 100,
|
|
237
|
-
averageDuration: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
238
|
-
agents,
|
|
260
|
+
periodType: this.getPeriodType(period),
|
|
261
|
+
...this.finishCount(acc),
|
|
239
262
|
};
|
|
240
263
|
}
|
|
241
|
-
|
|
242
|
-
for (const [level,
|
|
243
|
-
|
|
244
|
-
const durations = levelInvs.map((inv) => inv.duration);
|
|
245
|
-
const agents = {};
|
|
246
|
-
for (const inv of levelInvs) {
|
|
247
|
-
agents[inv.agentName] = (agents[inv.agentName] || 0) + 1;
|
|
248
|
-
}
|
|
249
|
-
aggregated.byComplexity[level] = {
|
|
264
|
+
const byComplexitySummary = {};
|
|
265
|
+
for (const [level, acc] of byComplexity) {
|
|
266
|
+
byComplexitySummary[level] = {
|
|
250
267
|
level: level,
|
|
251
|
-
|
|
252
|
-
successfulInvocations: successful.length,
|
|
253
|
-
failedInvocations: levelInvs.length - successful.length,
|
|
254
|
-
successRate: (successful.length / levelInvs.length) * 100,
|
|
255
|
-
averageDuration: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
256
|
-
agents,
|
|
268
|
+
...this.finishCount(acc),
|
|
257
269
|
};
|
|
258
270
|
}
|
|
259
|
-
return
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
271
|
+
return {
|
|
272
|
+
summary: {
|
|
273
|
+
totalInvocations: invocations.length,
|
|
274
|
+
totalAgents: byAgent.size,
|
|
275
|
+
timeRange: { start: minTs, end: maxTs },
|
|
276
|
+
overallSuccessRate: (successfulCount / invocations.length) * 100,
|
|
277
|
+
averageDuration: totalDuration / invocations.length,
|
|
278
|
+
},
|
|
279
|
+
byAgent: byAgentSummary,
|
|
280
|
+
byTimePeriod: byTimeSummary,
|
|
281
|
+
byComplexity: byComplexitySummary,
|
|
282
|
+
};
|
|
267
283
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
284
|
+
addPeriod(map, key, inv) {
|
|
285
|
+
let acc = map.get(key);
|
|
286
|
+
if (!acc) {
|
|
287
|
+
acc = this.newCountAcc();
|
|
288
|
+
map.set(key, acc);
|
|
289
|
+
}
|
|
290
|
+
this.addCount(acc, inv);
|
|
273
291
|
}
|
|
274
|
-
|
|
275
|
-
return
|
|
292
|
+
freshPeriodCursor() {
|
|
293
|
+
return {
|
|
294
|
+
hourCache: new Map(),
|
|
295
|
+
weekCache: new Map(),
|
|
296
|
+
dayStart: Number.NaN,
|
|
297
|
+
dayEnd: Number.NaN,
|
|
298
|
+
week: "",
|
|
299
|
+
};
|
|
276
300
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
301
|
+
// Hour, day, and month stay on UTC ISO. Week keeps the old local Y-M-D label.
|
|
302
|
+
periodKeys(inv, cursor) {
|
|
303
|
+
const cached = periodKeyCache.get(inv);
|
|
304
|
+
if (cached && cached.timestamp === inv.timestamp)
|
|
305
|
+
return cached;
|
|
306
|
+
const hourId = Math.floor(inv.timestamp / 3600000);
|
|
307
|
+
let parts = cursor.hourCache.get(hourId);
|
|
308
|
+
if (!parts) {
|
|
309
|
+
const iso = new Date(inv.timestamp).toISOString();
|
|
310
|
+
parts = {
|
|
311
|
+
hour: `${iso.slice(0, 13)}:00`,
|
|
312
|
+
day: iso.slice(0, 10),
|
|
313
|
+
month: iso.slice(0, 7),
|
|
314
|
+
};
|
|
315
|
+
cursor.hourCache.set(hourId, parts);
|
|
316
|
+
}
|
|
317
|
+
let week = cursor.week;
|
|
318
|
+
if (!(inv.timestamp >= cursor.dayStart && inv.timestamp < cursor.dayEnd)) {
|
|
319
|
+
const date = new Date(inv.timestamp);
|
|
320
|
+
const year = date.getFullYear();
|
|
321
|
+
const monthIndex = date.getMonth();
|
|
322
|
+
const day = date.getDate();
|
|
323
|
+
const localId = year * 512 + monthIndex * 40 + day;
|
|
324
|
+
const cachedWeek = cursor.weekCache.get(localId);
|
|
325
|
+
if (cachedWeek !== undefined) {
|
|
326
|
+
week = cachedWeek;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
const weekNumber = this.weekNumber(year, monthIndex, day);
|
|
330
|
+
week = `${year}-W${weekNumber.toString().padStart(2, "0")}`;
|
|
331
|
+
cursor.weekCache.set(localId, week);
|
|
332
|
+
}
|
|
333
|
+
cursor.week = week;
|
|
334
|
+
cursor.dayStart = new Date(year, monthIndex, day).getTime();
|
|
335
|
+
cursor.dayEnd = new Date(year, monthIndex, day + 1).getTime();
|
|
336
|
+
}
|
|
337
|
+
const stamp = {
|
|
338
|
+
timestamp: inv.timestamp,
|
|
339
|
+
hour: parts.hour,
|
|
340
|
+
day: parts.day,
|
|
341
|
+
week,
|
|
342
|
+
month: parts.month,
|
|
343
|
+
};
|
|
344
|
+
periodKeyCache.set(inv, stamp);
|
|
345
|
+
return stamp;
|
|
346
|
+
}
|
|
347
|
+
// Same Thursday-week count as the previous Date.UTC(local Y-M-D) helper.
|
|
348
|
+
weekNumber(year, monthIndex, day) {
|
|
349
|
+
const utcMidnight = Date.UTC(year, monthIndex, day);
|
|
350
|
+
const days = Math.floor(utcMidnight / 86400000);
|
|
351
|
+
const dow = (((days + 4) % 7) + 7) % 7;
|
|
352
|
+
const dayNum = dow === 0 ? 7 : dow;
|
|
353
|
+
const shifted = utcMidnight + (4 - dayNum) * 86400000;
|
|
354
|
+
const shiftedYear = new Date(shifted).getUTCFullYear();
|
|
355
|
+
const yearStart = Date.UTC(shiftedYear, 0, 1);
|
|
356
|
+
return Math.ceil(((shifted - yearStart) / 86400000 + 1) / 7);
|
|
283
357
|
}
|
|
284
358
|
getPeriodType(period) {
|
|
285
359
|
if (period.includes("W"))
|
|
@@ -290,112 +364,168 @@ export class AgentMetricsSystem {
|
|
|
290
364
|
return "day";
|
|
291
365
|
return "hour";
|
|
292
366
|
}
|
|
367
|
+
utcStamp(year, monthIndex, day, hour) {
|
|
368
|
+
if (monthIndex < 0 || monthIndex > 11 || day < 1 || hour < 0 || hour > 23)
|
|
369
|
+
return null;
|
|
370
|
+
const stamp = Date.UTC(year, monthIndex, day, hour, 0, 0, 0);
|
|
371
|
+
const date = new Date(stamp);
|
|
372
|
+
if (date.getUTCFullYear() !== year ||
|
|
373
|
+
date.getUTCMonth() !== monthIndex ||
|
|
374
|
+
date.getUTCDate() !== day ||
|
|
375
|
+
date.getUTCHours() !== hour) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
return stamp;
|
|
379
|
+
}
|
|
380
|
+
utcPeriodRange(period, periodType) {
|
|
381
|
+
if (periodType === "hour") {
|
|
382
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):00$/.exec(period);
|
|
383
|
+
const year = this.groupNumber(match, 1);
|
|
384
|
+
const month = this.groupNumber(match, 2);
|
|
385
|
+
const day = this.groupNumber(match, 3);
|
|
386
|
+
const hour = this.groupNumber(match, 4);
|
|
387
|
+
if (year === null || month === null || day === null || hour === null)
|
|
388
|
+
return null;
|
|
389
|
+
const start = this.utcStamp(year, month - 1, day, hour);
|
|
390
|
+
if (start === null)
|
|
391
|
+
return null;
|
|
392
|
+
return { start, end: start + 3600000 };
|
|
393
|
+
}
|
|
394
|
+
if (periodType === "day") {
|
|
395
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(period);
|
|
396
|
+
const year = this.groupNumber(match, 1);
|
|
397
|
+
const month = this.groupNumber(match, 2);
|
|
398
|
+
const day = this.groupNumber(match, 3);
|
|
399
|
+
if (year === null || month === null || day === null)
|
|
400
|
+
return null;
|
|
401
|
+
const start = this.utcStamp(year, month - 1, day, 0);
|
|
402
|
+
if (start === null)
|
|
403
|
+
return null;
|
|
404
|
+
return { start, end: start + 86400000 };
|
|
405
|
+
}
|
|
406
|
+
const match = /^(\d{4})-(\d{2})$/.exec(period);
|
|
407
|
+
const year = this.groupNumber(match, 1);
|
|
408
|
+
const month = this.groupNumber(match, 2);
|
|
409
|
+
if (year === null || month === null)
|
|
410
|
+
return null;
|
|
411
|
+
const start = this.utcStamp(year, month - 1, 1, 0);
|
|
412
|
+
if (start === null)
|
|
413
|
+
return null;
|
|
414
|
+
return { start, end: Date.UTC(year, month, 1, 0, 0, 0, 0) };
|
|
415
|
+
}
|
|
416
|
+
groupNumber(match, index) {
|
|
417
|
+
if (!match)
|
|
418
|
+
return null;
|
|
419
|
+
const part = match[index];
|
|
420
|
+
if (part === undefined)
|
|
421
|
+
return null;
|
|
422
|
+
const value = Number(part);
|
|
423
|
+
if (Number.isNaN(value))
|
|
424
|
+
return null;
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
293
427
|
getAgentSummary(agentName) {
|
|
294
|
-
const
|
|
295
|
-
|
|
428
|
+
const acc = this.newAgentAcc();
|
|
429
|
+
for (const inv of this.getInvocations()) {
|
|
430
|
+
if (inv.agentName !== agentName)
|
|
431
|
+
continue;
|
|
432
|
+
this.addAgent(acc, inv);
|
|
433
|
+
}
|
|
434
|
+
if (acc.count === 0)
|
|
296
435
|
return null;
|
|
297
|
-
|
|
298
|
-
const timestamps = invocations.map((inv) => inv.timestamp);
|
|
299
|
-
const durations = invocations.map((inv) => inv.duration);
|
|
300
|
-
const complexityScores = invocations.map((inv) => inv.complexityScore);
|
|
301
|
-
return {
|
|
302
|
-
agentName,
|
|
303
|
-
totalInvocations: invocations.length,
|
|
304
|
-
successfulInvocations: successful.length,
|
|
305
|
-
failedInvocations: invocations.length - successful.length,
|
|
306
|
-
successRate: (successful.length / invocations.length) * 100,
|
|
307
|
-
averageDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
|
|
308
|
-
averageComplexity: complexityScores.reduce((a, b) => a + b, 0) / complexityScores.length,
|
|
309
|
-
lastInvoked: Math.max(...timestamps),
|
|
310
|
-
firstInvoked: Math.min(...timestamps),
|
|
311
|
-
operations: [...new Set(invocations.map((inv) => inv.operation))],
|
|
312
|
-
};
|
|
436
|
+
return this.finishAgent(agentName, acc);
|
|
313
437
|
}
|
|
314
438
|
getTimePeriodSummary(period, periodType) {
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
filtered = allInvocations.filter((inv) => this.getMonthKey(inv.timestamp) === period);
|
|
329
|
-
break;
|
|
330
|
-
default:
|
|
439
|
+
const invocations = this.getInvocations();
|
|
440
|
+
const acc = this.newCountAcc();
|
|
441
|
+
if (periodType === "week") {
|
|
442
|
+
const cursor = this.freshPeriodCursor();
|
|
443
|
+
for (const inv of invocations) {
|
|
444
|
+
if (this.periodKeys(inv, cursor).week !== period)
|
|
445
|
+
continue;
|
|
446
|
+
this.addCount(acc, inv);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else if (periodType === "hour" || periodType === "day" || periodType === "month") {
|
|
450
|
+
const range = this.utcPeriodRange(period, periodType);
|
|
451
|
+
if (!range)
|
|
331
452
|
return null;
|
|
453
|
+
for (const inv of invocations) {
|
|
454
|
+
if (inv.timestamp < range.start || inv.timestamp >= range.end)
|
|
455
|
+
continue;
|
|
456
|
+
this.addCount(acc, inv);
|
|
457
|
+
}
|
|
332
458
|
}
|
|
333
|
-
|
|
459
|
+
else {
|
|
334
460
|
return null;
|
|
335
|
-
const successful = filtered.filter((inv) => inv.success);
|
|
336
|
-
const durations = filtered.map((inv) => inv.duration);
|
|
337
|
-
const agents = {};
|
|
338
|
-
for (const inv of filtered) {
|
|
339
|
-
agents[inv.agentName] = (agents[inv.agentName] || 0) + 1;
|
|
340
461
|
}
|
|
462
|
+
if (acc.count === 0)
|
|
463
|
+
return null;
|
|
341
464
|
return {
|
|
342
465
|
period,
|
|
343
466
|
periodType,
|
|
344
|
-
|
|
345
|
-
successfulInvocations: successful.length,
|
|
346
|
-
failedInvocations: filtered.length - successful.length,
|
|
347
|
-
successRate: (successful.length / filtered.length) * 100,
|
|
348
|
-
averageDuration: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
349
|
-
agents,
|
|
467
|
+
...this.finishCount(acc),
|
|
350
468
|
};
|
|
351
469
|
}
|
|
352
470
|
getComplexitySummary(level) {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const agents = {};
|
|
359
|
-
for (const inv of invocations) {
|
|
360
|
-
agents[inv.agentName] = (agents[inv.agentName] || 0) + 1;
|
|
471
|
+
const acc = this.newCountAcc();
|
|
472
|
+
for (const inv of this.getInvocations()) {
|
|
473
|
+
if (inv.complexityLevel !== level)
|
|
474
|
+
continue;
|
|
475
|
+
this.addCount(acc, inv);
|
|
361
476
|
}
|
|
477
|
+
if (acc.count === 0)
|
|
478
|
+
return null;
|
|
362
479
|
return {
|
|
363
480
|
level,
|
|
364
|
-
|
|
365
|
-
successfulInvocations: successful.length,
|
|
366
|
-
failedInvocations: invocations.length - successful.length,
|
|
367
|
-
successRate: (successful.length / invocations.length) * 100,
|
|
368
|
-
averageDuration: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
369
|
-
agents,
|
|
481
|
+
...this.finishCount(acc),
|
|
370
482
|
};
|
|
371
483
|
}
|
|
372
484
|
cleanup(olderThanMs, maxEntries) {
|
|
373
485
|
const cutoff = olderThanMs ? Date.now() - olderThanMs : 0;
|
|
374
486
|
const limit = maxEntries || this.retentionConfig.maxEntries;
|
|
375
|
-
|
|
487
|
+
const invocations = this.getInvocations();
|
|
376
488
|
const byAgent = {};
|
|
377
489
|
const beforeCount = invocations.length;
|
|
490
|
+
let next = invocations;
|
|
378
491
|
// Filter by age
|
|
379
492
|
if (cutoff > 0) {
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
493
|
+
let removedAny = false;
|
|
494
|
+
for (const inv of invocations) {
|
|
495
|
+
if (inv.timestamp < cutoff) {
|
|
496
|
+
removedAny = true;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (removedAny) {
|
|
501
|
+
const kept = [];
|
|
502
|
+
for (const inv of invocations) {
|
|
503
|
+
if (inv.timestamp >= cutoff) {
|
|
504
|
+
kept.push(inv);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
byAgent[inv.agentName] = (byAgent[inv.agentName] || 0) + 1;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
next = kept;
|
|
384
511
|
}
|
|
385
|
-
invocations = filtered;
|
|
386
512
|
}
|
|
387
513
|
// Limit by count
|
|
388
|
-
if (
|
|
389
|
-
const
|
|
390
|
-
|
|
514
|
+
if (next.length > limit) {
|
|
515
|
+
const drop = next.length - limit;
|
|
516
|
+
let index = 0;
|
|
517
|
+
for (const inv of next) {
|
|
518
|
+
if (index >= drop)
|
|
519
|
+
break;
|
|
391
520
|
byAgent[inv.agentName] = (byAgent[inv.agentName] || 0) + 1;
|
|
521
|
+
index += 1;
|
|
392
522
|
}
|
|
393
|
-
|
|
523
|
+
next = next.slice(drop);
|
|
394
524
|
}
|
|
395
|
-
this.saveInvocations(
|
|
525
|
+
this.saveInvocations(next);
|
|
396
526
|
return {
|
|
397
|
-
removed: beforeCount -
|
|
398
|
-
total:
|
|
527
|
+
removed: beforeCount - next.length,
|
|
528
|
+
total: next.length,
|
|
399
529
|
byAgent,
|
|
400
530
|
};
|
|
401
531
|
}
|
|
@@ -431,7 +561,7 @@ export class AgentMetricsSystem {
|
|
|
431
561
|
case "summary":
|
|
432
562
|
return {
|
|
433
563
|
format: "summary",
|
|
434
|
-
data: this.
|
|
564
|
+
data: this.aggregateInvocations(invocations),
|
|
435
565
|
exportedAt,
|
|
436
566
|
entryCount: invocations.length,
|
|
437
567
|
metadata,
|
|
@@ -441,7 +571,7 @@ export class AgentMetricsSystem {
|
|
|
441
571
|
format: "detailed",
|
|
442
572
|
data: {
|
|
443
573
|
invocations,
|
|
444
|
-
aggregated: this.
|
|
574
|
+
aggregated: this.aggregateInvocations(invocations),
|
|
445
575
|
},
|
|
446
576
|
exportedAt,
|
|
447
577
|
entryCount: invocations.length,
|
|
@@ -511,11 +641,19 @@ export class AgentMetricsSystem {
|
|
|
511
641
|
topAgents: [],
|
|
512
642
|
};
|
|
513
643
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
644
|
+
let successful = 0;
|
|
645
|
+
let totalDuration = 0;
|
|
646
|
+
let oldest = Number.POSITIVE_INFINITY;
|
|
647
|
+
let newest = Number.NEGATIVE_INFINITY;
|
|
648
|
+
const unique = new Set();
|
|
517
649
|
const agentCounts = {};
|
|
518
650
|
for (const inv of invocations) {
|
|
651
|
+
totalDuration += inv.duration;
|
|
652
|
+
if (inv.success)
|
|
653
|
+
successful += 1;
|
|
654
|
+
oldest = Math.min(oldest, inv.timestamp);
|
|
655
|
+
newest = Math.max(newest, inv.timestamp);
|
|
656
|
+
unique.add(inv.agentName);
|
|
519
657
|
agentCounts[inv.agentName] = (agentCounts[inv.agentName] || 0) + 1;
|
|
520
658
|
}
|
|
521
659
|
const topAgents = Object.entries(agentCounts)
|
|
@@ -524,11 +662,11 @@ export class AgentMetricsSystem {
|
|
|
524
662
|
.map(([name, count]) => ({ name, count }));
|
|
525
663
|
return {
|
|
526
664
|
totalInvocations: invocations.length,
|
|
527
|
-
uniqueAgents:
|
|
528
|
-
oldestInvocation:
|
|
529
|
-
newestInvocation:
|
|
530
|
-
successRate: (successful
|
|
531
|
-
averageDuration:
|
|
665
|
+
uniqueAgents: unique.size,
|
|
666
|
+
oldestInvocation: oldest,
|
|
667
|
+
newestInvocation: newest,
|
|
668
|
+
successRate: (successful / invocations.length) * 100,
|
|
669
|
+
averageDuration: totalDuration / invocations.length,
|
|
532
670
|
topAgents,
|
|
533
671
|
};
|
|
534
672
|
}
|