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.
Files changed (169) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.xray/features.json +1 -1
  3. package/dist/CHANGELOG.md +28 -0
  4. package/dist/analytics/emerging-pattern-detector.d.ts +12 -1
  5. package/dist/analytics/emerging-pattern-detector.js +37 -12
  6. package/dist/analytics/pattern-learning-engine.js +1 -9
  7. package/dist/analytics/predictive-analytics.js +51 -33
  8. package/dist/analytics/prompt-pattern-analyzer.d.ts +1 -0
  9. package/dist/analytics/prompt-pattern-analyzer.js +39 -20
  10. package/dist/analytics/routing-performance-analyzer.d.ts +2 -0
  11. package/dist/analytics/routing-performance-analyzer.js +89 -37
  12. package/dist/analytics/simple-pattern-analyzer.d.ts +4 -0
  13. package/dist/analytics/simple-pattern-analyzer.js +66 -28
  14. package/dist/architect/architect-tools.d.ts +3 -0
  15. package/dist/architect/architect-tools.js +22 -0
  16. package/dist/cli/commands/grok-install.js +1 -1
  17. package/dist/cli/commands/hermes-install.js +11 -5
  18. package/dist/cli/commands/openclaw-install.js +13 -7
  19. package/dist/cli/commands/opencode-install.js +7 -4
  20. package/dist/cli/commands/suit-monitor.d.ts +30 -0
  21. package/dist/cli/commands/suit-monitor.js +128 -0
  22. package/dist/cli/index.js +52 -15
  23. package/dist/cli/suit-pulse.d.ts +29 -0
  24. package/dist/cli/suit-pulse.js +270 -0
  25. package/dist/delegation/agent-delegator.d.ts +4 -2
  26. package/dist/delegation/agent-delegator.js +46 -27
  27. package/dist/delegation/agent-expertise.js +2 -1
  28. package/dist/delegation/analytics/outcome-tracker.d.ts +6 -0
  29. package/dist/delegation/analytics/outcome-tracker.js +17 -3
  30. package/dist/delegation/ast-code-parser.d.ts +5 -0
  31. package/dist/delegation/ast-code-parser.js +53 -48
  32. package/dist/delegation/codebase-context-analyzer.d.ts +3 -7
  33. package/dist/delegation/codebase-context-analyzer.js +188 -159
  34. package/dist/delegation/session-coordinator.js +3 -2
  35. package/dist/enforcement/loaders/codex-loader.js +18 -14
  36. package/dist/enforcement/validators/architecture-validators.js +115 -9
  37. package/dist/governance/codex-policy.service.d.ts +12 -2
  38. package/dist/governance/codex-policy.service.js +91 -16
  39. package/dist/integrations/grok/hooks/session-start.js +6 -6
  40. package/dist/integrations/hooks/goggles-pipeline.mjs +169 -28
  41. package/dist/integrations/hooks/goggles-planes.json +172 -8
  42. package/dist/integrations/hooks/plates.cjs +138 -0
  43. package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
  44. package/dist/integrations/hooks/write-home.cjs +59 -0
  45. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  46. package/dist/mcps/architect-tools.server.d.ts +6 -0
  47. package/dist/mcps/architect-tools.server.js +19 -18
  48. package/dist/mcps/orchestrator/server.d.ts +4 -3
  49. package/dist/mcps/orchestrator/server.js +29 -17
  50. package/dist/memory-routing/lookup-cache.d.ts +8 -0
  51. package/dist/memory-routing/lookup-cache.js +72 -0
  52. package/dist/memory-routing/plates.d.ts +1 -1
  53. package/dist/memory-routing/plates.js +26 -0
  54. package/dist/memory-routing/provider-loader.js +3 -0
  55. package/dist/metrics/agent-metrics.d.ts +14 -5
  56. package/dist/metrics/agent-metrics.js +362 -224
  57. package/dist/nucleus/synthesis-consult-receipt.js +38 -16
  58. package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
  59. package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
  60. package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
  61. package/dist/orchestrator/orchestrator.js +1 -2
  62. package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
  63. package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
  64. package/dist/processors/implementations/console-log-guard-processor.js +39 -50
  65. package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
  66. package/dist/processors/implementations/performance-budget-processor.js +29 -38
  67. package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
  68. package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
  69. package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
  70. package/dist/reporting/framework-reporting-system.d.ts +3 -0
  71. package/dist/reporting/framework-reporting-system.js +61 -8
  72. package/dist/reporting/log-parser.js +188 -105
  73. package/dist/reporting/metrics.js +42 -18
  74. package/dist/security/prompt-security-validator.js +8 -7
  75. package/dist/security/security-auditor.d.ts +4 -0
  76. package/dist/security/security-auditor.js +132 -57
  77. package/dist/security/security-hardener.d.ts +2 -0
  78. package/dist/security/security-hardener.js +31 -6
  79. package/dist/security/security-headers.d.ts +2 -0
  80. package/dist/security/security-headers.js +23 -21
  81. package/dist/security/security-scanner.js +18 -4
  82. package/dist/session/session-cleanup-manager.d.ts +2 -0
  83. package/dist/session/session-cleanup-manager.js +29 -8
  84. package/dist/session/session-monitor.d.ts +14 -4
  85. package/dist/session/session-monitor.js +208 -106
  86. package/dist/session/session-state-manager.d.ts +11 -0
  87. package/dist/session/session-state-manager.js +90 -8
  88. package/docs-site/docs/plates/activity-log.md +38 -0
  89. package/docs-site/docs/plates/cursor-compact.md +35 -0
  90. package/docs-site/docs/plates/grok-compact.md +35 -0
  91. package/docs-site/docs/plates/index.md +25 -3
  92. package/docs-site/docs/plates/inference-files.md +35 -0
  93. package/docs-site/docs/plates/kept-line.md +35 -0
  94. package/docs-site/docs/plates/lens-gate.md +35 -0
  95. package/docs-site/docs/plates/lens-page.md +35 -0
  96. package/docs-site/docs/plates/notes-page.md +35 -0
  97. package/docs-site/docs/plates/package-face.md +35 -0
  98. package/docs-site/docs/plates/payload-heat.md +35 -0
  99. package/docs-site/docs/plates/pickup-stamp.md +35 -0
  100. package/docs-site/docs/plates/pre-tool.md +35 -0
  101. package/docs-site/docs/plates/record-map.md +41 -0
  102. package/docs-site/docs/plates/reflection-page.md +35 -0
  103. package/docs-site/docs/plates/review.md +57 -0
  104. package/docs-site/docs/plates/session-capture.md +38 -0
  105. package/docs-site/docs/plates/site-manual.md +35 -0
  106. package/docs-site/docs/plates/stamp-plate.md +35 -0
  107. package/docs-site/docs/plates/station-card.md +35 -0
  108. package/docs-site/docs/plates/station-heat.md +35 -0
  109. package/docs-site/docs/plates/suit-organs.md +38 -0
  110. package/docs-site/docs/plates/suit-settings.md +35 -0
  111. package/docs-site/docs/plates/suit-wear.md +37 -0
  112. package/docs-site/docs/plates/trail-state.md +35 -0
  113. package/docs-site/docs/plates/work-fresh.md +35 -0
  114. package/docs-site/docs/plates/write-home.md +38 -0
  115. package/package.json +3 -2
  116. package/scripts/node/install-bridges.cjs +302 -78
  117. package/scripts/node/postinstall.cjs +14 -3
  118. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  119. package/src/mcps/architect-tools.server.ts +25 -20
  120. package/src/mcps/orchestrator/server.ts +33 -19
  121. package/xray/features.json +1 -1
  122. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
  123. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
  124. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
  125. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
  126. package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
  127. package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
  128. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
  129. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
  130. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
  131. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
  132. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
  133. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
  134. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
  135. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
  136. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
  137. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
  138. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
  139. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
  140. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
  141. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
  142. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
  143. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
  144. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
  145. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
  146. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
  147. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
  148. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
  149. package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
  150. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
  151. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
  152. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
  153. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
  154. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
  155. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
  156. package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
  157. package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
  158. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
  159. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
  160. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
  161. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
  162. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
  163. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
  164. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
  165. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
  166. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
  167. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
  168. package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
  169. 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
- let invocations = this.getInvocations();
110
- if (filter.agentNames?.length) {
111
- invocations = invocations.filter((inv) => filter.agentNames.includes(inv.agentName));
112
- }
113
- if (filter.agentTypes?.length) {
114
- invocations = invocations.filter((inv) => filter.agentTypes.includes(inv.agentType));
115
- }
116
- if (filter.timeRange) {
117
- invocations = invocations.filter((inv) => inv.timestamp >= filter.timeRange.start &&
118
- inv.timestamp <= filter.timeRange.end);
119
- }
120
- if (filter.complexityLevels?.length) {
121
- invocations = invocations.filter((inv) => filter.complexityLevels.includes(inv.complexityLevel));
122
- }
123
- if (filter.successOnly) {
124
- invocations = invocations.filter((inv) => inv.success);
125
- }
126
- if (filter.failureOnly) {
127
- invocations = invocations.filter((inv) => !inv.success);
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
- if (filter.sessionId) {
130
- invocations = invocations.filter((inv) => inv.sessionId === filter.sessionId);
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 invocations;
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
- const timestamps = invocations.map((inv) => inv.timestamp);
151
- const totalDuration = invocations.reduce((sum, inv) => sum + inv.duration, 0);
152
- const successfulCount = invocations.filter((inv) => inv.success).length;
153
- const aggregated = {
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
- // Group by agent
173
- const agentInvs = byAgent.get(inv.agentName) || [];
174
- agentInvs.push(inv);
175
- byAgent.set(inv.agentName, agentInvs);
176
- // Group by time period (hour)
177
- const hourKey = this.getHourKey(inv.timestamp);
178
- const hourInvs = byTimePeriod.get(hourKey) || [];
179
- hourInvs.push(inv);
180
- byTimePeriod.set(hourKey, hourInvs);
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
- aggregated.byTimePeriod[period] = {
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
- totalInvocations: periodInvs.length,
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
- // Build complexity summaries
242
- for (const [level, levelInvs] of byComplexity) {
243
- const successful = levelInvs.filter((inv) => inv.success);
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
- totalInvocations: levelInvs.length,
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 aggregated;
260
- }
261
- getHourKey(timestamp) {
262
- const date = new Date(timestamp);
263
- return `${date.toISOString().slice(0, 13)}:00`;
264
- }
265
- getDayKey(timestamp) {
266
- return new Date(timestamp).toISOString().slice(0, 10);
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
- getWeekKey(timestamp) {
269
- const date = new Date(timestamp);
270
- const year = date.getFullYear();
271
- const week = this.getWeekNumber(date);
272
- return `${year}-W${week.toString().padStart(2, "0")}`;
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
- getMonthKey(timestamp) {
275
- return new Date(timestamp).toISOString().slice(0, 7);
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
- getWeekNumber(date) {
278
- const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
279
- const dayNum = d.getUTCDay() || 7;
280
- d.setUTCDate(d.getUTCDate() + 4 - dayNum);
281
- const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
282
- return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
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 invocations = this.getInvocationsByAgent(agentName);
295
- if (invocations.length === 0)
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
- const successful = invocations.filter((inv) => inv.success);
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 allInvocations = this.getInvocations();
316
- let filtered;
317
- switch (periodType) {
318
- case "hour":
319
- filtered = allInvocations.filter((inv) => this.getHourKey(inv.timestamp) === period);
320
- break;
321
- case "day":
322
- filtered = allInvocations.filter((inv) => this.getDayKey(inv.timestamp) === period);
323
- break;
324
- case "week":
325
- filtered = allInvocations.filter((inv) => this.getWeekKey(inv.timestamp) === period);
326
- break;
327
- case "month":
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
- if (filtered.length === 0)
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
- totalInvocations: filtered.length,
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 invocations = this.getInvocations().filter((inv) => inv.complexityLevel === level);
354
- if (invocations.length === 0)
355
- return null;
356
- const successful = invocations.filter((inv) => inv.success);
357
- const durations = invocations.map((inv) => inv.duration);
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
- totalInvocations: invocations.length,
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
- let invocations = this.getInvocations();
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
- const filtered = invocations.filter((inv) => inv.timestamp >= cutoff);
381
- const removed = invocations.filter((inv) => inv.timestamp < cutoff);
382
- for (const inv of removed) {
383
- byAgent[inv.agentName] = (byAgent[inv.agentName] || 0) + 1;
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 (invocations.length > limit) {
389
- const removed = invocations.slice(0, invocations.length - limit);
390
- for (const inv of removed) {
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
- invocations = invocations.slice(invocations.length - limit);
523
+ next = next.slice(drop);
394
524
  }
395
- this.saveInvocations(invocations);
525
+ this.saveInvocations(next);
396
526
  return {
397
- removed: beforeCount - invocations.length,
398
- total: invocations.length,
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.aggregateMetrics(filter),
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.aggregateMetrics(filter),
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
- const timestamps = invocations.map((inv) => inv.timestamp);
515
- const durations = invocations.map((inv) => inv.duration);
516
- const successful = invocations.filter((inv) => inv.success);
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: new Set(invocations.map((inv) => inv.agentName)).size,
528
- oldestInvocation: Math.min(...timestamps),
529
- newestInvocation: Math.max(...timestamps),
530
- successRate: (successful.length / invocations.length) * 100,
531
- averageDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
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
  }