0xray 4.0.36 → 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.
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 +15 -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
@@ -1,6 +1,7 @@
1
1
  import { frameworkLogger } from "../core/framework-logger.js";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
+ import { fileURLToPath } from "node:url";
4
5
  import { getComprehensiveLogs, filterLogsByConfig, frameworkLogToParsedEntry, } from "./log-parser.js";
5
6
  import { calculateTimeRange, calculateMetrics, calculatePeakActivity, calculateHealthScore, generateInsights, generateRecommendations, generateAlerts, } from "./metrics.js";
6
7
  import { formatReport } from "./report-formatter.js";
@@ -11,11 +12,11 @@ export class FrameworkReportingSystem {
11
12
  const reportId = this.generateReportId(config);
12
13
  const cached = this.getCachedReport(reportId);
13
14
  if (cached && this.isCacheValid(cached.timestamp)) {
14
- return formatReport(cached.data, config.outputFormat);
15
+ return cached.formatted;
15
16
  }
16
17
  const reportData = await this.collectReportData(config);
17
- this.cacheReport(reportId, reportData);
18
18
  const formattedReport = formatReport(reportData, config.outputFormat);
19
+ this.cacheReport(reportId, reportData, formattedReport);
19
20
  if (config.outputPath) {
20
21
  await this.saveReportToFile(formattedReport, config.outputPath);
21
22
  }
@@ -49,8 +50,14 @@ export class FrameworkReportingSystem {
49
50
  ...new Set(recentLogs.map((log) => log.component)),
50
51
  ];
51
52
  const recentActivity = parsedLogs.slice(0, 10);
52
- const errorCount = recentLogs.filter((log) => log.status === "error").length;
53
- const successCount = recentLogs.filter((log) => log.status === "success").length;
53
+ let errorCount = 0;
54
+ let successCount = 0;
55
+ for (const log of recentLogs) {
56
+ if (log.status === "error")
57
+ errorCount++;
58
+ else if (log.status === "success")
59
+ successCount++;
60
+ }
54
61
  const healthScore = recentLogs.length > 0
55
62
  ? (successCount / (successCount + errorCount)) * 100
56
63
  : 100;
@@ -82,8 +89,50 @@ const report = await reportingSystem.generateCustomReport('${template.name}');
82
89
  `;
83
90
  }
84
91
  generateReportId(config) {
85
- const timeKey = config.timeRange?.lastHours || "all";
86
- return `${config.type}-${config.outputFormat}-${timeKey}`;
92
+ const range = config.timeRange;
93
+ const file = this.currentLogStamp();
94
+ return JSON.stringify([
95
+ config.type,
96
+ config.outputFormat,
97
+ range?.lastHours ?? null,
98
+ range?.start?.toISOString() ?? null,
99
+ range?.end?.toISOString() ?? null,
100
+ config.sessionId ?? null,
101
+ config.jobId ?? null,
102
+ file.size,
103
+ file.mtimeMs,
104
+ this.memoryLogStamp(),
105
+ ]);
106
+ }
107
+ // Same activity.log path readCurrentLogFile rebuilds from size and mtime.
108
+ activityLogFile() {
109
+ const currentFilePath = fileURLToPath(import.meta.url);
110
+ const projectRoot = path.resolve(path.dirname(currentFilePath), "../../");
111
+ return path.join(projectRoot, "logs", "framework", "activity.log");
112
+ }
113
+ currentLogStamp() {
114
+ try {
115
+ const stat = fs.statSync(this.activityLogFile());
116
+ return { size: stat.size, mtimeMs: stat.mtimeMs };
117
+ }
118
+ catch {
119
+ return { size: -1, mtimeMs: -1 };
120
+ }
121
+ }
122
+ memoryLogStamp() {
123
+ const recent = frameworkLogger.getRecentLogs(1000);
124
+ const last = recent[recent.length - 1];
125
+ if (!last)
126
+ return "0";
127
+ return [
128
+ recent.length,
129
+ last.timestamp,
130
+ last.component,
131
+ last.action,
132
+ last.status,
133
+ last.sessionId ?? "",
134
+ last.jobId ?? "",
135
+ ].join("\0");
87
136
  }
88
137
  getCachedReport(reportId) {
89
138
  return this.reportCache.get(reportId) || null;
@@ -93,8 +142,12 @@ const report = await reportingSystem.generateCustomReport('${template.name}');
93
142
  const maxCacheAgeMs = 5 * 60 * 1000;
94
143
  return cacheAgeMs < maxCacheAgeMs;
95
144
  }
96
- cacheReport(reportId, data) {
97
- this.reportCache.set(reportId, { data, timestamp: new Date() });
145
+ cacheReport(reportId, data, formatted) {
146
+ this.reportCache.set(reportId, {
147
+ data,
148
+ formatted,
149
+ timestamp: new Date(),
150
+ });
98
151
  if (this.reportCache.size > 10) {
99
152
  const keys = Array.from(this.reportCache.keys());
100
153
  if (keys.length > 0) {
@@ -1,7 +1,13 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
+ import { createGunzip } from "zlib";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { frameworkLogger } from "../core/framework-logger.js";
6
+ const LOG_LINE_RE = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+(.+?)\s+-\s+(\w+)$/;
7
+ const LOG_LINE_FALLBACK_RE = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\s+\[([^\]]+)\]\s+(.+?)\s+-\s+(\w+)$/;
8
+ // Parsed rows stay cached while size and mtime stay the same.
9
+ let currentLogCache = null;
10
+ const rotatedLogCache = new Map();
5
11
  export function levelToStatus(level) {
6
12
  switch (level.toUpperCase()) {
7
13
  case "ERROR":
@@ -37,8 +43,7 @@ export function inferAgent(component) {
37
43
  return "system";
38
44
  }
39
45
  export function parseLogLine(line) {
40
- const logRegex = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+(.+?)\s+-\s+(\w+)$/;
41
- const match = line.match(logRegex);
46
+ const match = line.match(LOG_LINE_RE);
42
47
  if (match && match[1] && match[2] && match[3] && match[4] && match[5]) {
43
48
  const timestamp = match[1];
44
49
  const jobId = match[2];
@@ -60,8 +65,7 @@ export function parseLogLine(line) {
60
65
  agent: inferAgent(component),
61
66
  };
62
67
  }
63
- const fallbackRegex = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\s+\[([^\]]+)\]\s+(.+?)\s+-\s+(\w+)$/;
64
- const fallbackMatch = line.match(fallbackRegex);
68
+ const fallbackMatch = line.match(LOG_LINE_FALLBACK_RE);
65
69
  if (fallbackMatch &&
66
70
  fallbackMatch[1] &&
67
71
  fallbackMatch[2] &&
@@ -102,86 +106,154 @@ export function frameworkLogToParsedEntry(entry) {
102
106
  details: entry.details,
103
107
  };
104
108
  }
105
- export async function readCurrentLogFile(timeRange) {
109
+ function parseLogContent(content) {
106
110
  const logs = [];
111
+ const lines = content.split("\n");
112
+ for (const line of lines) {
113
+ if (!line.trim())
114
+ continue;
115
+ try {
116
+ const logEntry = parseLogLine(line);
117
+ if (logEntry)
118
+ logs.push(logEntry);
119
+ }
120
+ catch {
121
+ // Continue processing other lines
122
+ }
123
+ }
124
+ return logs;
125
+ }
126
+ function windowOf(timeRange) {
127
+ const startTime = timeRange?.start?.getTime() ??
128
+ (timeRange?.lastHours
129
+ ? Date.now() - timeRange.lastHours * 60 * 60 * 1000
130
+ : 0);
131
+ const endTime = timeRange?.end?.getTime() ?? Date.now();
132
+ return { startTime, endTime };
133
+ }
134
+ function withinWindow(logs, startTime, endTime) {
135
+ const filtered = [];
136
+ for (const log of logs) {
137
+ if (log.timestamp >= startTime && log.timestamp <= endTime) {
138
+ filtered.push(log);
139
+ }
140
+ }
141
+ return filtered;
142
+ }
143
+ // Length prefixes keep the first timestamp + component + action row.
144
+ function eventKey(log) {
145
+ const component = log.component;
146
+ const action = log.action;
147
+ return `${log.timestamp}\0${component.length}\0${component}\0${action.length}\0${action}`;
148
+ }
149
+ function uniqueSorted(logs) {
150
+ const seen = new Set();
151
+ const unique = [];
152
+ for (const log of logs) {
153
+ const key = eventKey(log);
154
+ if (seen.has(key))
155
+ continue;
156
+ seen.add(key);
157
+ unique.push(log);
158
+ }
159
+ unique.sort((a, b) => a.timestamp - b.timestamp);
160
+ return unique;
161
+ }
162
+ export async function readCurrentLogFile(timeRange) {
107
163
  const currentFilePath = fileURLToPath(import.meta.url);
108
164
  const projectRoot = path.resolve(path.dirname(currentFilePath), "../../");
109
- const logDir = path.join(projectRoot, "logs", "framework");
110
- const logFile = path.join(logDir, "activity.log");
165
+ const logFile = path.join(projectRoot, "logs", "framework", "activity.log");
111
166
  try {
112
- const fsModule = await import("fs");
113
- if (!fsModule.existsSync(logFile)) {
114
- return logs;
167
+ if (!fs.existsSync(logFile)) {
168
+ currentLogCache = null;
169
+ return [];
115
170
  }
116
- const content = fsModule.readFileSync(logFile, "utf8");
117
- const lines = content.split("\n");
118
- const startTime = timeRange?.start?.getTime() ??
119
- (timeRange?.lastHours
120
- ? Date.now() - timeRange.lastHours * 60 * 60 * 1000
121
- : 0);
122
- const endTime = timeRange?.end?.getTime() ?? Date.now();
123
- for (const line of lines) {
124
- if (line.trim()) {
125
- try {
126
- const logEntry = parseLogLine(line);
127
- if (logEntry &&
128
- logEntry.timestamp >= startTime &&
129
- logEntry.timestamp <= endTime) {
130
- logs.push(logEntry);
131
- }
132
- }
133
- catch {
134
- // Continue processing other lines
135
- }
171
+ const before = fs.statSync(logFile);
172
+ let entries;
173
+ const hit = currentLogCache;
174
+ if (hit && hit.mtimeMs === before.mtimeMs && hit.size === before.size) {
175
+ entries = hit.entries;
176
+ }
177
+ else {
178
+ const content = fs.readFileSync(logFile, "utf8");
179
+ const after = fs.statSync(logFile);
180
+ const parsed = parseLogContent(content);
181
+ if (before.mtimeMs === after.mtimeMs && before.size === after.size) {
182
+ currentLogCache = {
183
+ mtimeMs: after.mtimeMs,
184
+ size: after.size,
185
+ entries: parsed,
186
+ };
136
187
  }
188
+ else {
189
+ currentLogCache = null;
190
+ }
191
+ entries = parsed;
137
192
  }
193
+ const { startTime, endTime } = windowOf(timeRange);
194
+ return withinWindow(entries, startTime, endTime);
138
195
  }
139
196
  catch (error) {
140
197
  await frameworkLogger.log("framework-reporting-system", "current-log-read-failed", "warning", { error: String(error) });
141
198
  }
142
- return logs;
199
+ return [];
143
200
  }
144
201
  export async function parseCompressedLogFile(filePath, startTime, endTime) {
145
202
  return new Promise((resolve, reject) => {
146
203
  const logs = [];
147
- (async () => {
148
- try {
149
- const zlib = await import("zlib");
150
- const fsModule = await import("fs");
151
- const readStream = fsModule.createReadStream(filePath);
152
- const gunzip = zlib.createGunzip();
153
- let buffer = "";
154
- readStream
155
- .pipe(gunzip)
156
- .on("data", (chunk) => {
157
- buffer += chunk.toString();
158
- const lines = buffer.split("\n");
159
- buffer = lines.pop() || "";
160
- for (const line of lines) {
161
- if (line.trim()) {
162
- try {
163
- const logEntry = parseLogLine(line);
164
- if (logEntry &&
165
- logEntry.timestamp >= startTime &&
166
- logEntry.timestamp <= endTime) {
167
- logs.push(logEntry);
168
- }
169
- }
170
- catch {
171
- // Skip malformed lines
204
+ let buffer = "";
205
+ try {
206
+ const readStream = fs.createReadStream(filePath);
207
+ readStream
208
+ .pipe(createGunzip())
209
+ .on("data", (chunk) => {
210
+ buffer += chunk.toString();
211
+ const lines = buffer.split("\n");
212
+ buffer = lines.pop() || "";
213
+ for (const line of lines) {
214
+ if (line.trim()) {
215
+ try {
216
+ const logEntry = parseLogLine(line);
217
+ if (logEntry &&
218
+ logEntry.timestamp >= startTime &&
219
+ logEntry.timestamp <= endTime) {
220
+ logs.push(logEntry);
172
221
  }
173
222
  }
223
+ catch {
224
+ // Skip malformed lines
225
+ }
174
226
  }
175
- })
176
- .on("end", () => resolve(logs))
177
- .on("error", reject);
178
- }
179
- catch (error) {
180
- reject(error);
181
- }
182
- })();
227
+ }
228
+ })
229
+ .on("end", () => resolve(logs))
230
+ .on("error", reject);
231
+ }
232
+ catch (error) {
233
+ reject(error);
234
+ }
183
235
  });
184
236
  }
237
+ async function cachedRotatedEntries(filePath) {
238
+ const before = fs.statSync(filePath);
239
+ const hit = rotatedLogCache.get(filePath);
240
+ if (hit && hit.mtimeMs === before.mtimeMs && hit.size === before.size) {
241
+ return hit.entries;
242
+ }
243
+ const entries = await parseCompressedLogFile(filePath, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY);
244
+ const after = fs.statSync(filePath);
245
+ if (before.mtimeMs === after.mtimeMs && before.size === after.size) {
246
+ rotatedLogCache.set(filePath, {
247
+ mtimeMs: after.mtimeMs,
248
+ size: after.size,
249
+ entries,
250
+ });
251
+ }
252
+ else {
253
+ rotatedLogCache.delete(filePath);
254
+ }
255
+ return entries;
256
+ }
185
257
  export async function readRotatedLogFiles(timeRange) {
186
258
  const logs = [];
187
259
  const logDir = path.join(process.cwd(), "logs", "framework");
@@ -193,15 +265,18 @@ export async function readRotatedLogFiles(timeRange) {
193
265
  .filter((file) => file.startsWith("framework-activity-") && file.endsWith(".log.gz"))
194
266
  .sort()
195
267
  .reverse();
196
- const startTime = timeRange?.start?.getTime() ??
197
- (timeRange?.lastHours
198
- ? Date.now() - timeRange.lastHours * 60 * 60 * 1000
199
- : 0);
200
- const endTime = timeRange?.end?.getTime() ?? Date.now();
268
+ const keep = new Set(files.slice(0, 3).map((file) => path.join(logDir, file)));
269
+ for (const key of rotatedLogCache.keys()) {
270
+ if (!keep.has(key))
271
+ rotatedLogCache.delete(key);
272
+ }
273
+ const { startTime, endTime } = windowOf(timeRange);
201
274
  for (const file of files.slice(0, 3)) {
275
+ const filePath = path.join(logDir, file);
202
276
  try {
203
- const fileLogs = await parseCompressedLogFile(path.join(logDir, file), startTime, endTime);
204
- logs.push(...fileLogs);
277
+ const fileLogs = withinWindow(await cachedRotatedEntries(filePath), startTime, endTime);
278
+ for (const entry of fileLogs)
279
+ logs.push(entry);
205
280
  if (logs.length > 5000)
206
281
  break;
207
282
  }
@@ -216,12 +291,15 @@ export async function readRotatedLogFiles(timeRange) {
216
291
  return logs;
217
292
  }
218
293
  export async function getComprehensiveLogs(config) {
294
+ const allLogs = [];
219
295
  const recentLogs = frameworkLogger.getRecentLogs(1000);
220
- const convertedRecent = recentLogs.map(frameworkLogToParsedEntry);
221
- let allLogs = [...convertedRecent];
296
+ for (const entry of recentLogs) {
297
+ allLogs.push(frameworkLogToParsedEntry(entry));
298
+ }
222
299
  try {
223
300
  const currentLogs = await readCurrentLogFile(config.timeRange);
224
- allLogs = [...allLogs, ...currentLogs];
301
+ for (const entry of currentLogs)
302
+ allLogs.push(entry);
225
303
  }
226
304
  catch (error) {
227
305
  await frameworkLogger.log("framework-reporting-system", "current-log-read-failed", "warning", { error: String(error) });
@@ -234,48 +312,53 @@ export async function getComprehensiveLogs(config) {
234
312
  24 * 60 * 60 * 1000))) {
235
313
  try {
236
314
  const rotatedLogs = await readRotatedLogFiles(config.timeRange);
237
- allLogs = [...allLogs, ...rotatedLogs];
315
+ for (const entry of rotatedLogs)
316
+ allLogs.push(entry);
238
317
  }
239
318
  catch (error) {
240
319
  await frameworkLogger.log("framework-reporting-system", "rotated-logs-read-failed", "warning", { error: String(error) });
241
320
  }
242
321
  }
243
- const uniqueLogs = allLogs.filter((log, index, self) => index ===
244
- self.findIndex((l) => l.timestamp === log.timestamp &&
245
- l.component === log.component &&
246
- l.action === log.action));
247
- return uniqueLogs.sort((a, b) => a.timestamp - b.timestamp);
322
+ return uniqueSorted(allLogs);
248
323
  }
249
- export function filterLogsByConfig(logs, config) {
250
- let filtered = logs;
251
- if (config.sessionId) {
252
- filtered = filtered.filter((log) => log.sessionId === config.sessionId);
253
- }
254
- if (config.jobId) {
255
- filtered = filtered.filter((log) => log.jobId === config.jobId);
256
- }
257
- if (config.timeRange) {
258
- const startTime = config.timeRange.start?.getTime() ??
259
- (config.timeRange.lastHours
260
- ? Date.now() - config.timeRange.lastHours * 60 * 60 * 1000
261
- : 0);
262
- const endTime = config.timeRange.end?.getTime() ?? Date.now();
263
- filtered = filtered.filter((log) => log.timestamp >= startTime && log.timestamp <= endTime);
264
- }
265
- switch (config.type) {
324
+ function matchesReportType(log, type) {
325
+ switch (type) {
266
326
  case "orchestration":
267
- filtered = filtered.filter((log) => log.component === "agent-delegator" ||
327
+ return (log.component === "agent-delegator" ||
268
328
  log.action.includes("delegation"));
269
- break;
270
329
  case "agent-usage":
271
- filtered = filtered.filter((log) => log.agent || log.component.includes("agent"));
272
- break;
330
+ return Boolean(log.agent) || log.component.includes("agent");
273
331
  case "context-awareness":
274
- filtered = filtered.filter((log) => log.component.includes("context") || log.component.includes("ast"));
275
- break;
332
+ return (log.component.includes("context") || log.component.includes("ast"));
276
333
  case "performance":
277
- filtered = filtered.filter((log) => log.action.includes("complete") || log.action.includes("failed"));
278
- break;
334
+ return (log.action.includes("complete") || log.action.includes("failed"));
335
+ default:
336
+ return true;
337
+ }
338
+ }
339
+ export function filterLogsByConfig(logs, config) {
340
+ const sessionId = config.sessionId;
341
+ const jobId = config.jobId;
342
+ let startTime = 0;
343
+ let endTime = 0;
344
+ const hasTime = Boolean(config.timeRange);
345
+ if (config.timeRange) {
346
+ const window = windowOf(config.timeRange);
347
+ startTime = window.startTime;
348
+ endTime = window.endTime;
349
+ }
350
+ const filtered = [];
351
+ for (const log of logs) {
352
+ if (sessionId && log.sessionId !== sessionId)
353
+ continue;
354
+ if (jobId && log.jobId !== jobId)
355
+ continue;
356
+ if (hasTime && (log.timestamp < startTime || log.timestamp > endTime)) {
357
+ continue;
358
+ }
359
+ if (!matchesReportType(log, config.type))
360
+ continue;
361
+ filtered.push(log);
279
362
  }
280
363
  return filtered;
281
364
  }
@@ -2,10 +2,21 @@ export function calculateTimeRange(logs, timeRange) {
2
2
  if (logs.length === 0) {
3
3
  return { start: new Date(), end: new Date() };
4
4
  }
5
- const timestamps = logs.map((log) => log.timestamp);
5
+ let min = Infinity;
6
+ let max = -Infinity;
7
+ for (const log of logs) {
8
+ const ts = log.timestamp;
9
+ if (Number.isNaN(ts)) {
10
+ return { start: new Date(NaN), end: new Date(NaN) };
11
+ }
12
+ if (ts < min)
13
+ min = ts;
14
+ if (ts > max)
15
+ max = ts;
16
+ }
6
17
  return {
7
- start: new Date(Math.min(...timestamps)),
8
- end: new Date(Math.max(...timestamps)),
18
+ start: new Date(min),
19
+ end: new Date(max),
9
20
  };
10
21
  }
11
22
  export function calculateMetrics(logs) {
@@ -22,8 +33,11 @@ export function calculateMetrics(logs) {
22
33
  let terminalOperations = 0;
23
34
  let analysisOperations = 0;
24
35
  let orchestrationOperations = 0;
36
+ let successCount = 0;
25
37
  const responseTimes = [];
26
38
  for (const log of logs) {
39
+ if (log.status === "success")
40
+ successCount++;
27
41
  if (log.agent) {
28
42
  agentUsage.set(log.agent, (agentUsage.get(log.agent) || 0) + 1);
29
43
  }
@@ -52,8 +66,6 @@ export function calculateMetrics(logs) {
52
66
  if (log.status === "success") {
53
67
  toolStats.success++;
54
68
  }
55
- toolStats.rate =
56
- toolStats.total > 0 ? (toolStats.success / toolStats.total) * 100 : 0;
57
69
  if (["write", "edit", "read"].includes(toolName)) {
58
70
  fileOperations++;
59
71
  }
@@ -72,9 +84,7 @@ export function calculateMetrics(logs) {
72
84
  toolSuccessRate.forEach((stats) => {
73
85
  stats.rate = stats.total > 0 ? (stats.success / stats.total) * 100 : 0;
74
86
  });
75
- const successLogs = logs.filter((log) => log.status === "success");
76
- const errorLogs = logs.filter((log) => log.status === "error");
77
- const successRate = logs.length > 0 ? (successLogs.length / logs.length) * 100 : 100;
87
+ const successRate = logs.length > 0 ? (successCount / logs.length) * 100 : 100;
78
88
  const enhancementSuccessRate = enhancementSuccesses + enhancementFailures > 0
79
89
  ? (enhancementSuccesses /
80
90
  (enhancementSuccesses + enhancementFailures)) *
@@ -117,25 +127,36 @@ export function calculateMetrics(logs) {
117
127
  export function calculatePeakActivity(logs) {
118
128
  const minuteGroups = new Map();
119
129
  for (const log of logs) {
120
- const minute = new Date(log.timestamp).toISOString().slice(0, 16);
130
+ const ts = log.timestamp;
131
+ const minute = Number.isFinite(ts)
132
+ ? Math.floor(ts / 60000) * 60000
133
+ : new Date(new Date(ts).toISOString().slice(0, 16) + ":00Z").getTime();
121
134
  minuteGroups.set(minute, (minuteGroups.get(minute) || 0) + 1);
122
135
  }
123
- let peakMinute = "";
136
+ let peakMinute = 0;
124
137
  let peakCount = 0;
138
+ let found = false;
125
139
  for (const [minute, count] of minuteGroups) {
126
140
  if (count > peakCount) {
127
141
  peakCount = count;
128
142
  peakMinute = minute;
143
+ found = true;
129
144
  }
130
145
  }
131
146
  return {
132
- timestamp: peakMinute ? new Date(peakMinute + ":00Z") : new Date(),
147
+ timestamp: found ? new Date(peakMinute) : new Date(),
133
148
  eventsPerMinute: peakCount,
134
149
  };
135
150
  }
136
151
  export function calculateHealthScore(logs) {
137
- const successCount = logs.filter((log) => log.status === "success").length;
138
- const errorCount = logs.filter((log) => log.status === "error").length;
152
+ let successCount = 0;
153
+ let errorCount = 0;
154
+ for (const log of logs) {
155
+ if (log.status === "success")
156
+ successCount++;
157
+ else if (log.status === "error")
158
+ errorCount++;
159
+ }
139
160
  if (successCount + errorCount === 0)
140
161
  return 100;
141
162
  return (successCount / (successCount + errorCount)) * 100;
@@ -175,11 +196,14 @@ export function generateRecommendations(metrics) {
175
196
  }
176
197
  export function generateAlerts(logs) {
177
198
  const alerts = [];
178
- const recentErrors = logs
179
- .filter((log) => log.status === "error")
180
- .slice(0, 3);
181
- for (const error of recentErrors) {
182
- alerts.push(`${error.component}:${error.action} failed`);
199
+ let errorsKept = 0;
200
+ for (const log of logs) {
201
+ if (log.status !== "error")
202
+ continue;
203
+ alerts.push(`${log.component}:${log.action} failed`);
204
+ errorsKept++;
205
+ if (errorsKept === 3)
206
+ break;
183
207
  }
184
208
  const highFrequencyComponents = getHighFrequencyComponents(logs);
185
209
  for (const component of highFrequencyComponents) {
@@ -38,19 +38,21 @@ export class PromptSecurityValidator {
38
38
  violations.push(`Prompt too long: ${prompt.length} > ${this.config.maxPromptLength}`);
39
39
  riskLevel = "medium";
40
40
  }
41
- // Blocked pattern check
41
+ const folded = prompt.toLowerCase();
42
+ // Blocked pattern check. Reset lastIndex so a previous call cannot hide this one.
42
43
  for (const pattern of this.config.blockedPatterns) {
44
+ pattern.lastIndex = 0;
43
45
  if (pattern.test(prompt)) {
44
46
  violations.push(`Blocked pattern detected: ${pattern.source}`);
45
47
  riskLevel = "high";
46
48
  }
47
49
  }
48
50
  // Advanced pattern analysis
49
- if (this.containsSystemPromptOverride(prompt)) {
51
+ if (this.containsSystemPromptOverride(folded)) {
50
52
  violations.push("Potential system prompt override attempt");
51
53
  riskLevel = "critical";
52
54
  }
53
- if (this.containsInjectionAttempts(prompt)) {
55
+ if (this.containsInjectionAttempts(folded)) {
54
56
  violations.push("Potential prompt injection detected");
55
57
  riskLevel = "high";
56
58
  }
@@ -66,7 +68,7 @@ export class PromptSecurityValidator {
66
68
  /**
67
69
  * Check for system prompt override attempts
68
70
  */
69
- containsSystemPromptOverride(prompt) {
71
+ containsSystemPromptOverride(lowerPrompt) {
70
72
  const overrideIndicators = [
71
73
  "you are now",
72
74
  "act as if",
@@ -78,13 +80,12 @@ export class PromptSecurityValidator {
78
80
  "new personality",
79
81
  "different role",
80
82
  ];
81
- const lowerPrompt = prompt.toLowerCase();
82
83
  return overrideIndicators.some((indicator) => lowerPrompt.includes(indicator));
83
84
  }
84
85
  /**
85
86
  * Check for prompt injection attempts
86
87
  */
87
- containsInjectionAttempts(prompt) {
88
+ containsInjectionAttempts(lowerPrompt) {
88
89
  const injectionIndicators = [
89
90
  "ignore previous",
90
91
  "forget instructions",
@@ -94,7 +95,6 @@ export class PromptSecurityValidator {
94
95
  "clear memory",
95
96
  "delete history",
96
97
  ];
97
- const lowerPrompt = prompt.toLowerCase();
98
98
  return injectionIndicators.some((indicator) => lowerPrompt.includes(indicator));
99
99
  }
100
100
  /**
@@ -104,6 +104,7 @@ export class PromptSecurityValidator {
104
104
  let sanitized = prompt;
105
105
  // Remove blocked patterns
106
106
  for (const pattern of this.config.blockedPatterns) {
107
+ pattern.lastIndex = 0;
107
108
  sanitized = sanitized.replace(pattern, "[REDACTED]");
108
109
  }
109
110
  // Additional sanitization based on level