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.
Files changed (173) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.xray/features.json +1 -1
  3. package/dist/CHANGELOG.md +25 -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/grok/plugin/0xray/hooks/hooks.json +6 -6
  41. package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
  42. package/dist/integrations/hooks/goggles-pipeline.mjs +221 -44
  43. package/dist/integrations/hooks/goggles-planes.json +172 -8
  44. package/dist/integrations/hooks/plates.cjs +138 -0
  45. package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
  46. package/dist/integrations/hooks/write-home.cjs +59 -0
  47. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  48. package/dist/mcps/architect-tools.server.d.ts +6 -0
  49. package/dist/mcps/architect-tools.server.js +19 -18
  50. package/dist/mcps/orchestrator/server.d.ts +4 -3
  51. package/dist/mcps/orchestrator/server.js +29 -17
  52. package/dist/memory-routing/lookup-cache.d.ts +8 -0
  53. package/dist/memory-routing/lookup-cache.js +72 -0
  54. package/dist/memory-routing/plates.d.ts +1 -1
  55. package/dist/memory-routing/plates.js +26 -0
  56. package/dist/memory-routing/provider-loader.js +3 -0
  57. package/dist/metrics/agent-metrics.d.ts +14 -5
  58. package/dist/metrics/agent-metrics.js +362 -224
  59. package/dist/nucleus/synthesis-consult-receipt.js +38 -16
  60. package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
  61. package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
  62. package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
  63. package/dist/orchestrator/orchestrator.js +1 -2
  64. package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
  65. package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
  66. package/dist/processors/implementations/console-log-guard-processor.js +39 -50
  67. package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
  68. package/dist/processors/implementations/performance-budget-processor.js +29 -38
  69. package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
  70. package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
  71. package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
  72. package/dist/reporting/framework-reporting-system.d.ts +3 -0
  73. package/dist/reporting/framework-reporting-system.js +61 -8
  74. package/dist/reporting/log-parser.js +188 -105
  75. package/dist/reporting/metrics.js +42 -18
  76. package/dist/security/prompt-security-validator.js +8 -7
  77. package/dist/security/security-auditor.d.ts +4 -0
  78. package/dist/security/security-auditor.js +132 -57
  79. package/dist/security/security-hardener.d.ts +2 -0
  80. package/dist/security/security-hardener.js +31 -6
  81. package/dist/security/security-headers.d.ts +2 -0
  82. package/dist/security/security-headers.js +23 -21
  83. package/dist/security/security-scanner.js +18 -4
  84. package/dist/session/session-cleanup-manager.d.ts +2 -0
  85. package/dist/session/session-cleanup-manager.js +29 -8
  86. package/dist/session/session-monitor.d.ts +14 -4
  87. package/dist/session/session-monitor.js +208 -106
  88. package/dist/session/session-state-manager.d.ts +11 -0
  89. package/dist/session/session-state-manager.js +90 -8
  90. package/docs-site/docs/plates/activity-log.md +38 -0
  91. package/docs-site/docs/plates/cursor-compact.md +35 -0
  92. package/docs-site/docs/plates/grok-compact.md +35 -0
  93. package/docs-site/docs/plates/index.md +25 -3
  94. package/docs-site/docs/plates/inference-files.md +35 -0
  95. package/docs-site/docs/plates/kept-line.md +35 -0
  96. package/docs-site/docs/plates/lens-gate.md +35 -0
  97. package/docs-site/docs/plates/lens-page.md +35 -0
  98. package/docs-site/docs/plates/notes-page.md +35 -0
  99. package/docs-site/docs/plates/package-face.md +35 -0
  100. package/docs-site/docs/plates/payload-heat.md +35 -0
  101. package/docs-site/docs/plates/pickup-stamp.md +35 -0
  102. package/docs-site/docs/plates/pre-tool.md +35 -0
  103. package/docs-site/docs/plates/record-map.md +41 -0
  104. package/docs-site/docs/plates/reflection-page.md +35 -0
  105. package/docs-site/docs/plates/review.md +57 -0
  106. package/docs-site/docs/plates/session-capture.md +38 -0
  107. package/docs-site/docs/plates/site-manual.md +35 -0
  108. package/docs-site/docs/plates/stamp-plate.md +35 -0
  109. package/docs-site/docs/plates/station-card.md +35 -0
  110. package/docs-site/docs/plates/station-heat.md +35 -0
  111. package/docs-site/docs/plates/suit-organs.md +38 -0
  112. package/docs-site/docs/plates/suit-settings.md +35 -0
  113. package/docs-site/docs/plates/suit-wear.md +37 -0
  114. package/docs-site/docs/plates/trail-state.md +35 -0
  115. package/docs-site/docs/plates/work-fresh.md +35 -0
  116. package/docs-site/docs/plates/write-home.md +38 -0
  117. package/package.json +4 -2
  118. package/scripts/mjs/run-grok-hook.mjs +50 -0
  119. package/scripts/node/install-bridges.cjs +305 -81
  120. package/scripts/node/postinstall.cjs +14 -3
  121. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
  122. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  123. package/src/mcps/architect-tools.server.ts +25 -20
  124. package/src/mcps/orchestrator/server.ts +33 -19
  125. package/xray/features.json +1 -1
  126. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
  127. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
  128. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
  129. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
  130. package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
  131. package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
  132. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
  133. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
  134. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
  135. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
  136. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
  137. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
  138. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
  139. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
  140. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
  141. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
  142. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
  143. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
  144. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
  145. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
  146. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
  147. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
  148. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
  149. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
  150. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
  151. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
  152. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
  153. package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
  154. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
  155. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
  156. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
  157. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
  158. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
  159. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
  160. package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
  161. package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
  162. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
  163. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
  164. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
  165. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
  166. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
  167. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
  168. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
  169. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
  170. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
  171. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
  172. package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
  173. package/vendor/@0xray/repertoire/dist/types.js.map +0 -1
@@ -278,6 +278,108 @@ export class ErrorResolutionValidator extends BaseValidator {
278
278
  return this.createSuccessResult("Error resolution patterns are properly implemented");
279
279
  }
280
280
  }
281
+ /** Code points matched by a non-unicode `\s` in this Node. */
282
+ function isCallWhitespace(ch) {
283
+ const c = ch.charCodeAt(0);
284
+ if (c <= 32) {
285
+ return c === 9 || c === 10 || c === 11 || c === 12 || c === 13 || c === 32;
286
+ }
287
+ if (c >= 0x2000 && c <= 0x200a)
288
+ return true;
289
+ return (c === 0xa0 ||
290
+ c === 0x1680 ||
291
+ c === 0x2028 ||
292
+ c === 0x2029 ||
293
+ c === 0x202f ||
294
+ c === 0x205f ||
295
+ c === 0x3000 ||
296
+ c === 0xfeff);
297
+ }
298
+ /** Per-name scans are cheaper below this; one automaton wins above it. */
299
+ const CALL_SCAN_NAME_THRESHOLD = 48;
300
+ /**
301
+ * Counts `name\\s*\\(` for every name.
302
+ * Suffix hits count (`foo` inside `myfoo(`), matching a separate global regex per name.
303
+ */
304
+ function countNameCallSites(code, names) {
305
+ if (names.length < CALL_SCAN_NAME_THRESHOLD) {
306
+ return names.map((funcName) => (code.match(new RegExp(`${funcName}\\s*\\(`, "g")) || []).length);
307
+ }
308
+ const counts = new Array(names.length).fill(0);
309
+ if (code.length === 0)
310
+ return counts;
311
+ const nodes = [{ next: new Map(), fail: 0, out: [] }];
312
+ for (let nameIndex = 0; nameIndex < names.length; nameIndex++) {
313
+ const name = names[nameIndex] ?? "";
314
+ let state = 0;
315
+ for (let i = 0; i < name.length; i++) {
316
+ const ch = name.charAt(i);
317
+ const current = nodes[state];
318
+ if (!current)
319
+ break;
320
+ let next = current.next.get(ch);
321
+ if (next === undefined) {
322
+ next = nodes.length;
323
+ nodes.push({ next: new Map(), fail: 0, out: [] });
324
+ current.next.set(ch, next);
325
+ }
326
+ state = next;
327
+ }
328
+ nodes[state]?.out.push(nameIndex);
329
+ }
330
+ const queue = [];
331
+ const root = nodes[0];
332
+ if (!root)
333
+ return counts;
334
+ for (const next of root.next.values())
335
+ queue.push(next);
336
+ let head = 0;
337
+ while (head < queue.length) {
338
+ const currentIndex = queue[head++];
339
+ const current = currentIndex === undefined ? undefined : nodes[currentIndex];
340
+ if (!current)
341
+ continue;
342
+ for (const [ch, childIndex] of current.next) {
343
+ queue.push(childIndex);
344
+ let fail = current.fail;
345
+ while (fail !== 0 && !nodes[fail]?.next.has(ch)) {
346
+ fail = nodes[fail]?.fail ?? 0;
347
+ }
348
+ const down = nodes[fail]?.next.get(ch);
349
+ const child = nodes[childIndex];
350
+ if (!child)
351
+ continue;
352
+ child.fail = down !== undefined && down !== childIndex ? down : 0;
353
+ const failOut = nodes[child.fail]?.out;
354
+ if (failOut && failOut.length > 0) {
355
+ child.out = child.out.concat(failOut);
356
+ }
357
+ }
358
+ }
359
+ let state = 0;
360
+ for (let i = 0; i < code.length; i++) {
361
+ const ch = code.charAt(i);
362
+ while (state !== 0 && !nodes[state]?.next.has(ch)) {
363
+ state = nodes[state]?.fail ?? 0;
364
+ }
365
+ const down = nodes[state]?.next.get(ch);
366
+ state = down === undefined ? 0 : down;
367
+ const outs = nodes[state]?.out;
368
+ if (!outs || outs.length === 0)
369
+ continue;
370
+ let k = i + 1;
371
+ while (k < code.length && isCallWhitespace(code.charAt(k)))
372
+ k++;
373
+ if (code.charAt(k) !== "(")
374
+ continue;
375
+ for (const nameIndex of outs) {
376
+ const current = counts[nameIndex];
377
+ if (current !== undefined)
378
+ counts[nameIndex] = current + 1;
379
+ }
380
+ }
381
+ return counts;
382
+ }
281
383
  /**
282
384
  * Validates loop safety (Codex Term #8).
283
385
  * Prevents infinite loops by checking for proper termination conditions.
@@ -322,15 +424,19 @@ export class LoopSafetyValidator extends BaseValidator {
322
424
  // Check for recursion without base case detection (basic)
323
425
  const functionMatches = newCode.match(/function\s+\w+\s*\([^)]*\)/g);
324
426
  if (functionMatches) {
325
- const functionNames = functionMatches
326
- .map((match) => {
327
- const nameMatch = match.match(/function\s+(\w+)/);
328
- return nameMatch ? nameMatch[1] : null;
329
- })
330
- .filter(Boolean);
331
- for (const funcName of functionNames) {
332
- // Check if function calls itself (basic recursion detection)
333
- const selfCalls = (newCode.match(new RegExp(`${funcName}\\s*\\(`, "g")) || []).length;
427
+ const functionNames = [];
428
+ for (const match of functionMatches) {
429
+ const name = match.match(/function\s+(\w+)/)?.[1];
430
+ if (name)
431
+ functionNames.push(name);
432
+ }
433
+ const callCounts = countNameCallSites(newCode, functionNames);
434
+ for (let i = 0; i < functionNames.length; i++) {
435
+ const funcName = functionNames[i];
436
+ if (!funcName)
437
+ continue;
438
+ // Same count a per-name /name\s*\(/g scan would return, including suffix hits.
439
+ const selfCalls = callCounts[i] ?? 0;
334
440
  if (selfCalls > 1) {
335
441
  // More than just the function definition
336
442
  // Allow recursive functions with proper base cases (edge case)
@@ -15,7 +15,8 @@
15
15
  * - Returns ActiveCodexSnapshot compatible with the existing get_active_codex MCP tool.
16
16
  * - Provides getTermCount() with safe 60-term fallback (preserves prior behavior of bypasses).
17
17
  * - Full frameworkLogger discipline on every load/decision/error.
18
- * - No caching in v1 skeleton (additive later); no mutation; no enforcement.
18
+ * - Reuses one parsed codex while that file still wins and its mtime and size are unchanged.
19
+ * - No mutation of the cached codex; no enforcement.
19
20
  *
20
21
  * First wired consumer: src/mcps/enforcer-tools.server.ts (getCodexTermCount bypass removed;
21
22
  * now delegates through this service).
@@ -34,9 +35,18 @@ import type { ActiveCodexSnapshot, ICodexPolicyProvider } from './governance-typ
34
35
  */
35
36
  export declare class CodexPolicyService implements ICodexPolicyProvider {
36
37
  private readonly component;
38
+ /**
39
+ * Winning parse. Invalid when a higher-priority path appears or mtime/size changes,
40
+ * so the next check still parses the bytes a fresh load would have read.
41
+ */
42
+ private codexCache;
43
+ private candidateKey;
44
+ private fileIdentity;
45
+ private cachedRaw;
37
46
  /**
38
47
  * Internal loader: resolves candidates, picks first existing, reads + parses.
39
- * Always logs via frameworkLogger (success, error, fallback).
48
+ * Repeat calls skip the read and JSON.parse while the winning file is unchanged.
49
+ * Logs via frameworkLogger on load, error, and fallback (not on a cache hit).
40
50
  */
41
51
  private loadRaw;
42
52
  private computeTermCount;
@@ -15,7 +15,8 @@
15
15
  * - Returns ActiveCodexSnapshot compatible with the existing get_active_codex MCP tool.
16
16
  * - Provides getTermCount() with safe 60-term fallback (preserves prior behavior of bypasses).
17
17
  * - Full frameworkLogger discipline on every load/decision/error.
18
- * - No caching in v1 skeleton (additive later); no mutation; no enforcement.
18
+ * - Reuses one parsed codex while that file still wins and its mtime and size are unchanged.
19
+ * - No mutation of the cached codex; no enforcement.
19
20
  *
20
21
  * First wired consumer: src/mcps/enforcer-tools.server.ts (getCodexTermCount bypass removed;
21
22
  * now delegates through this service).
@@ -29,51 +30,125 @@
29
30
  */
30
31
  import { frameworkLogger } from '../core/framework-logger.js';
31
32
  import { resolveCodexPath } from '../core/config-paths.js';
32
- import { existsSync } from 'fs';
33
+ import { existsSync, statSync } from 'fs';
33
34
  import * as fs from 'fs/promises';
35
+ /** mtimeNs when the stat has it. mtimeMs only when it does not. */
36
+ function codexClock(fileStat) {
37
+ const nanos = fileStat.mtimeNs;
38
+ if (typeof nanos === 'bigint')
39
+ return nanos.toString();
40
+ if (typeof nanos === 'number' && Number.isFinite(nanos))
41
+ return String(nanos);
42
+ if (typeof fileStat.mtimeMs === 'number' && Number.isFinite(fileStat.mtimeMs)) {
43
+ return String(fileStat.mtimeMs);
44
+ }
45
+ return null;
46
+ }
34
47
  /**
35
48
  * Canonical implementation of the Governance Codex/Policy provider.
36
49
  * Single owner of the "first read" of any codex.json for the framework.
37
50
  */
38
51
  export class CodexPolicyService {
39
52
  component = 'codex-policy-service';
53
+ /**
54
+ * Winning parse. Invalid when a higher-priority path appears or mtime/size changes,
55
+ * so the next check still parses the bytes a fresh load would have read.
56
+ */
57
+ codexCache = null;
58
+ candidateKey(candidates) {
59
+ return JSON.stringify(candidates);
60
+ }
61
+ fileIdentity(sourcePath) {
62
+ try {
63
+ const fileStat = statSync(sourcePath);
64
+ if (!fileStat.isFile())
65
+ return null;
66
+ const clock = codexClock(fileStat);
67
+ if (clock === null)
68
+ return null;
69
+ return `${clock}:${fileStat.size}`;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ cachedRaw(candidates) {
76
+ const cached = this.codexCache;
77
+ if (!cached || cached.candidateKey !== this.candidateKey(candidates))
78
+ return null;
79
+ for (let i = 0; i < cached.index; i++) {
80
+ const higher = candidates[i];
81
+ if (higher && existsSync(higher))
82
+ return null;
83
+ }
84
+ const identity = this.fileIdentity(cached.sourcePath);
85
+ if (!identity || identity !== cached.identity)
86
+ return null;
87
+ return cached.raw;
88
+ }
40
89
  /**
41
90
  * Internal loader: resolves candidates, picks first existing, reads + parses.
42
- * Always logs via frameworkLogger (success, error, fallback).
91
+ * Repeat calls skip the read and JSON.parse while the winning file is unchanged.
92
+ * Logs via frameworkLogger on load, error, and fallback (not on a cache hit).
43
93
  */
44
94
  async loadRaw() {
45
- const candidates = resolveCodexPath();
46
- const resolvedPath = Array.isArray(candidates)
47
- ? candidates.find((p) => existsSync(p)) || candidates[0] || null
48
- : candidates || null;
49
- if (!resolvedPath || !existsSync(resolvedPath)) {
95
+ const resolved = resolveCodexPath();
96
+ const candidates = Array.isArray(resolved)
97
+ ? resolved
98
+ : resolved
99
+ ? [resolved]
100
+ : [];
101
+ const hit = this.cachedRaw(candidates);
102
+ if (hit)
103
+ return hit;
104
+ const index = candidates.findIndex((candidate) => existsSync(candidate));
105
+ const indexed = index >= 0 ? candidates[index] : undefined;
106
+ const sourcePath = indexed ?? candidates[0] ?? null;
107
+ if (index < 0 || !sourcePath) {
108
+ this.codexCache = null;
50
109
  await frameworkLogger.log(this.component, 'codex-not-found', 'warning', {
51
110
  candidates,
52
- resolvedPath,
111
+ resolvedPath: sourcePath,
53
112
  message: 'No codex.json found in standard locations; using fallback behavior',
54
113
  });
55
- return { path: resolvedPath, data: this.getBuiltinFallback(), isFallback: true };
114
+ return { path: sourcePath, data: this.getBuiltinFallback(), isFallback: true };
56
115
  }
57
116
  try {
58
- const content = await fs.readFile(resolvedPath, 'utf-8');
117
+ const before = this.fileIdentity(sourcePath);
118
+ const content = await fs.readFile(sourcePath, 'utf-8');
59
119
  const data = JSON.parse(content);
60
120
  const termCount = this.computeTermCount(data);
121
+ const raw = { path: sourcePath, data, isFallback: false };
122
+ const after = this.fileIdentity(sourcePath);
123
+ if (before && after && before === after) {
124
+ this.codexCache = {
125
+ candidateKey: this.candidateKey(candidates),
126
+ index,
127
+ sourcePath,
128
+ identity: after,
129
+ raw,
130
+ };
131
+ }
132
+ else {
133
+ this.codexCache = null;
134
+ }
61
135
  await frameworkLogger.log(this.component, 'codex-loaded', 'success', {
62
- source: resolvedPath,
136
+ source: sourcePath,
63
137
  version: data?.version || 'unknown',
64
138
  termCount,
65
139
  lastUpdated: data?.lastUpdated,
66
140
  });
67
- return { path: resolvedPath, data, isFallback: false };
141
+ return raw;
68
142
  }
69
143
  catch (error) {
144
+ this.codexCache = null;
70
145
  const msg = error instanceof Error ? error.message : String(error);
71
146
  await frameworkLogger.log(this.component, 'codex-load-error', 'error', {
72
- source: resolvedPath,
147
+ source: sourcePath,
73
148
  error: msg,
74
149
  });
75
150
  // Safe fallback (never break callers)
76
- return { path: resolvedPath, data: this.getBuiltinFallback(), isFallback: true };
151
+ return { path: sourcePath, data: this.getBuiltinFallback(), isFallback: true };
77
152
  }
78
153
  }
79
154
  computeTermCount(data) {
@@ -121,7 +196,7 @@ export class CodexPolicyService {
121
196
  ? 'Governance CodexPolicyService — builtin fallback (no external codex.json resolved)'
122
197
  : 'Returned via Governance CodexPolicyService — V2 Single Source of Truth (S02-REAL)',
123
198
  dynamo_required: true,
124
- ...(includeRaw ? { codex: data } : {}),
199
+ ...(includeRaw ? { codex: structuredClone(data) } : {}),
125
200
  };
126
201
  await frameworkLogger.log(this.component, 'get-current-codex', 'info', {
127
202
  source: path,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Grok SessionStart / UserPromptSubmit / PreCompact / PostCompact.
4
- * Grok ignores stdout here; station heat is session-boot.json + STATION.md.
4
+ * Compact prints nothing. Station heat is session-boot.json + STATION.md.
5
5
  */
6
6
 
7
7
  import {
@@ -148,9 +148,10 @@ async function main() {
148
148
  : '0xray/grok-session-start';
149
149
 
150
150
  const intent = extractIntent(event);
151
- const matchedSignals =
152
- HOOK_EVENT === 'user_prompt_submit' ? [] : await matchStationSignals(eventRoot, intent);
153
151
  const compact = COMPACT_EVENTS.has(HOOK_EVENT);
152
+ // The compact payload is the whole conversation. Do not match it and do not print it.
153
+ const matchedSignals =
154
+ compact || HOOK_EVENT === 'user_prompt_submit' ? [] : await matchStationSignals(eventRoot, intent);
154
155
  let freshnessLine = null;
155
156
  if (!compact) {
156
157
  try {
@@ -164,8 +165,7 @@ async function main() {
164
165
  const payload = buildSessionBootPayload(eventRoot, source, {
165
166
  hookEvent: HOOK_EVENT,
166
167
  sessionId: event.sessionId || process.env.GROK_SESSION_ID || null,
167
- ...(intent ? { intent } : {}),
168
- ...(!compact && intent ? { cardText: intent } : {}),
168
+ ...(!compact && intent ? { intent, cardText: intent } : {}),
169
169
  ...(matchedSignals.length ? { matchedSignals } : {}),
170
170
  ...(freshnessLine ? { freshnessLine } : {}),
171
171
  });
@@ -195,7 +195,7 @@ async function main() {
195
195
  /* non-blocking pipeline facets */
196
196
  }
197
197
 
198
- console.log(JSON.stringify(payload));
198
+ if (!compact) console.log(JSON.stringify(payload));
199
199
  process.exit(0);
200
200
  } catch (err) {
201
201
  appendHookActivity(root, 'grok-session-start', 'session-boot-error', 'error', {
@@ -5,7 +5,7 @@
5
5
  "hooks": [
6
6
  {
7
7
  "type": "command",
8
- "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/pre-tool-use.js\"",
8
+ "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" pre-tool-use.js",
9
9
  "timeout": 30
10
10
  }
11
11
  ]
@@ -16,7 +16,7 @@
16
16
  "hooks": [
17
17
  {
18
18
  "type": "command",
19
- "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js\"",
19
+ "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js",
20
20
  "timeout": 30
21
21
  }
22
22
  ]
@@ -27,7 +27,7 @@
27
27
  "hooks": [
28
28
  {
29
29
  "type": "command",
30
- "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js\" --hook-event=user_prompt_submit",
30
+ "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=user_prompt_submit",
31
31
  "timeout": 30
32
32
  }
33
33
  ]
@@ -38,7 +38,7 @@
38
38
  "hooks": [
39
39
  {
40
40
  "type": "command",
41
- "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/post-tool-use.js\"",
41
+ "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" post-tool-use.js",
42
42
  "timeout": 30
43
43
  }
44
44
  ]
@@ -49,7 +49,7 @@
49
49
  "hooks": [
50
50
  {
51
51
  "type": "command",
52
- "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js\" --hook-event=pre_compact",
52
+ "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=pre_compact",
53
53
  "timeout": 30
54
54
  }
55
55
  ]
@@ -60,7 +60,7 @@
60
60
  "hooks": [
61
61
  {
62
62
  "type": "command",
63
- "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js\" --hook-event=post_compact",
63
+ "command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=post_compact",
64
64
  "timeout": 30
65
65
  }
66
66
  ]
@@ -2,7 +2,7 @@
2
2
  * Goggles MCP. Stdio. Server name `goggles`.
3
3
  * Tools: look, status_lens. Same organ as the CLI.
4
4
  */
5
- import { existsSync, readFileSync } from 'node:fs';
5
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
6
6
  import { basename, join, resolve } from 'node:path';
7
7
  import { pathToFileURL, fileURLToPath } from 'node:url';
8
8
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
@@ -328,9 +328,16 @@ async function main() {
328
328
  await server.connect(transport);
329
329
  }
330
330
 
331
+ function sameInvoked(argvPath, modulePath) {
332
+ try {
333
+ return realpathSync(argvPath) === realpathSync(modulePath);
334
+ } catch {
335
+ return resolve(argvPath) === resolve(modulePath);
336
+ }
337
+ }
338
+
331
339
  const thisFile = fileURLToPath(import.meta.url);
332
- const invoked = process.argv[1] ? resolve(process.argv[1]) : '';
333
- if (invoked && resolve(thisFile) === invoked) {
340
+ if (process.argv[1] && sameInvoked(process.argv[1], thisFile)) {
334
341
  main().catch((err) => {
335
342
  process.stderr.write(`goggles MCP failed: ${err instanceof Error ? err.message : String(err)}\n`);
336
343
  process.exit(1);