0xray 4.0.42 → 4.1.0

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 (93) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.plugin/plugin.json +1 -1
  3. package/.xray/features.json +5 -5
  4. package/AGENTS-consumer.md +8 -6
  5. package/AGENTS.md +17 -8
  6. package/LICENSE +1 -1
  7. package/README.md +15 -9
  8. package/SKILLS.md +3 -1
  9. package/dist/AGENTS.md +17 -8
  10. package/dist/CHANGELOG.md +83 -11
  11. package/dist/LICENSE +1 -1
  12. package/dist/README.md +15 -9
  13. package/dist/SKILLS.md +3 -1
  14. package/dist/cli/commands/plugin-commands.d.ts +4 -0
  15. package/dist/cli/commands/plugin-commands.js +84 -41
  16. package/dist/core/config-loader.js +4 -4
  17. package/dist/core/features-config.d.ts +1 -1
  18. package/dist/core/features-config.js +3 -3
  19. package/dist/integrations/cursor/hooks/hooks.json +6 -3
  20. package/dist/integrations/cursor/hooks/pre-tool-use.js +100 -37
  21. package/dist/integrations/cursor/hooks/xray-cloud-hook.sh +9 -1
  22. package/dist/integrations/grok/hooks/pre-tool-use.js +2 -58
  23. package/dist/integrations/hermes-agent/bridge.mjs +14 -1
  24. package/dist/integrations/hooks/delegation-gate-runtime.mjs +3 -2
  25. package/dist/integrations/hooks/goggles-mcp.mjs +35 -17
  26. package/dist/integrations/hooks/goggles-pipeline.mjs +46 -1
  27. package/dist/integrations/hooks/goggles-planes.json +7 -6
  28. package/dist/integrations/hooks/pre-tool-speak.mjs +76 -0
  29. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +21 -4
  30. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +17 -2
  31. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  32. package/dist/monitoring/{nudge-watchdog.d.ts → nudge-monitor.d.ts} +14 -5
  33. package/dist/monitoring/{nudge-watchdog.js → nudge-monitor.js} +42 -17
  34. package/dist/plugin/xray-codex-injection.js +18 -0
  35. package/dist/processors/implementations/nudge-processor.d.ts +1 -1
  36. package/dist/processors/implementations/nudge-processor.js +4 -4
  37. package/docs-site/docs/plates/activity-log.md +1 -1
  38. package/docs-site/docs/plates/bifrost.md +48 -0
  39. package/docs-site/docs/plates/boot.md +2 -2
  40. package/docs-site/docs/plates/centrifuge.md +1 -1
  41. package/docs-site/docs/plates/cursor-compact.md +1 -1
  42. package/docs-site/docs/plates/glossary.md +7 -3
  43. package/docs-site/docs/plates/goggles.md +13 -2
  44. package/docs-site/docs/plates/governance.md +1 -1
  45. package/docs-site/docs/plates/grok-compact.md +1 -1
  46. package/docs-site/docs/plates/grokbot.md +3 -1
  47. package/docs-site/docs/plates/host-pack.md +1 -1
  48. package/docs-site/docs/plates/house.md +1 -1
  49. package/docs-site/docs/plates/index.md +4 -2
  50. package/docs-site/docs/plates/inference-files.md +1 -1
  51. package/docs-site/docs/plates/kept-line.md +1 -1
  52. package/docs-site/docs/plates/kits.md +1 -1
  53. package/docs-site/docs/plates/lens-gate.md +3 -1
  54. package/docs-site/docs/plates/lens-page.md +1 -1
  55. package/docs-site/docs/plates/memory-recall.md +1 -1
  56. package/docs-site/docs/plates/notes-page.md +2 -2
  57. package/docs-site/docs/plates/orchestration.md +1 -1
  58. package/docs-site/docs/plates/package-face.md +1 -1
  59. package/docs-site/docs/plates/payload-heat.md +1 -1
  60. package/docs-site/docs/plates/pickup-stamp.md +1 -1
  61. package/docs-site/docs/plates/pre-tool.md +1 -1
  62. package/docs-site/docs/plates/processor.md +1 -1
  63. package/docs-site/docs/plates/record-map.md +1 -1
  64. package/docs-site/docs/plates/reflection-page.md +1 -1
  65. package/docs-site/docs/plates/reporting.md +1 -1
  66. package/docs-site/docs/plates/review.md +1 -1
  67. package/docs-site/docs/plates/routing.md +1 -1
  68. package/docs-site/docs/plates/session-capture.md +1 -1
  69. package/docs-site/docs/plates/site-manual.md +1 -1
  70. package/docs-site/docs/plates/stamp-plate.md +1 -1
  71. package/docs-site/docs/plates/station-card.md +1 -1
  72. package/docs-site/docs/plates/station-heat.md +1 -1
  73. package/docs-site/docs/plates/suit-organs.md +1 -1
  74. package/docs-site/docs/plates/suit-settings.md +1 -1
  75. package/docs-site/docs/plates/suit-wear.md +1 -1
  76. package/docs-site/docs/plates/suit.md +3 -3
  77. package/docs-site/docs/plates/trail-state.md +1 -1
  78. package/docs-site/docs/plates/work-fresh.md +1 -1
  79. package/docs-site/docs/plates/write-home.md +1 -1
  80. package/llms.txt +14 -6
  81. package/package.json +3 -3
  82. package/scripts/foundry/README.md +2 -2
  83. package/scripts/foundry/release-gate.mjs +111 -15
  84. package/scripts/foundry/release.mjs +8 -0
  85. package/scripts/mjs/verify-pipeline-facets.mjs +1 -1
  86. package/scripts/node/install-bridges.cjs +28 -12
  87. package/scripts/node/pack-tmp-suit-proof.mjs +14 -6
  88. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +17 -2
  89. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  90. package/vendor/@0xray/repertoire/README.md +1 -1
  91. package/xray/agents_template.md +2 -2
  92. package/xray/features.json +5 -5
  93. package/docs-site/docs/plates/live-mesh.md +0 -41
@@ -3,10 +3,12 @@
3
3
  * release-gate.mjs — Single release checkpoint.
4
4
  *
5
5
  * Modes:
6
- * (default) build + test + consumer smoke (fresh + upgrade merge)
6
+ * (default) build + test + docs + consumer smoke (fresh + upgrade merge)
7
7
  * --verify-only git + reconcile + release docs + consumer smoke — after push, before publish
8
8
  * --skip-smoke skip consumer install smoke
9
- * --skip-docs skip release docs validation (not recommended for publish)
9
+ * --prove-published Gate D only: npm lists this version, the annotated tag points at HEAD,
10
+ * and the GitHub Release exists. This command does not publish.
11
+ * --skip-docs refused. Gate C runs on every gate.
10
12
  *
11
13
  * Usage:
12
14
  * npx @0xray/foundry gate
@@ -21,7 +23,7 @@ import { isXrayExoRepo, millScript, readRootPackage, resolveMillRoot } from "./m
21
23
  const rootDir = resolveMillRoot();
22
24
  const verifyOnly = process.argv.includes("--verify-only");
23
25
  const skipSmoke = process.argv.includes("--skip-smoke");
24
- const skipDocs = process.argv.includes("--skip-docs");
26
+ const provePublished = process.argv.includes("--prove-published");
25
27
 
26
28
  function step(label, cmd) {
27
29
  console.log(`\n${"─".repeat(60)}\n🔄 ${label}\n${"─".repeat(60)}\n`);
@@ -32,18 +34,114 @@ function docsCheckCmd() {
32
34
  return `${JSON.stringify(process.execPath)} ${JSON.stringify(millScript("validate-release-docs.mjs"))}`;
33
35
  }
34
36
 
37
+
38
+ /** Gate C is the docs check. A ship gate that skips it is not a pass. */
39
+ export function gateCRefusal(argv = process.argv) {
40
+ if (argv.includes("--skip-docs")) {
41
+ return "Gate C cannot be skipped. Remove --skip-docs. The docs check runs on every gate.";
42
+ }
43
+ return null;
44
+ }
45
+
46
+ /**
47
+ * Gate D facts. listed, tagAnnotated, and tagMatchesHead are booleans.
48
+ * releaseUrl is the GitHub Release page, or empty when that page is missing.
49
+ */
50
+ export function provePublishedRelease(proof) {
51
+ const version = proof.version;
52
+ const errors = [];
53
+ if (proof.listed !== true) errors.push(`npm does not list ${version}`);
54
+ if (proof.tagAnnotated !== true) errors.push(`annotated tag v${version} is missing`);
55
+ if (proof.tagMatchesHead !== true) errors.push(`annotated tag v${version} does not point at HEAD`);
56
+ if (typeof proof.releaseUrl !== "string" || proof.releaseUrl.length === 0) {
57
+ errors.push(`GitHub Release v${version} is missing`);
58
+ }
59
+ return errors;
60
+ }
61
+
62
+ function gitOut(cmd) {
63
+ return execSync(cmd, { cwd: rootDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
64
+ }
65
+
66
+ function originSlug() {
67
+ const url = gitOut("git remote get-url origin");
68
+ const match = url.match(/github\.com[:/]([^/]+)\/([^/.]+)(?:\.git)?$/);
69
+ if (!match) throw new Error(`origin is not a GitHub repo: ${url}`);
70
+ return { owner: match[1], repo: match[2] };
71
+ }
72
+
73
+ function collectPublishedProof() {
74
+ const pkg = readRootPackage(rootDir);
75
+ const version = pkg.version;
76
+ const name = pkg.name;
77
+ let listed = false;
78
+ try {
79
+ const view = gitOut(`npm view ${name}@${version} version`);
80
+ listed = view.split(/\s+/).includes(version);
81
+ } catch {
82
+ listed = false;
83
+ }
84
+ let tagAnnotated = false;
85
+ let tagMatchesHead = false;
86
+ try {
87
+ tagAnnotated = gitOut(`git cat-file -t v${version}`) === "tag";
88
+ const tagged = gitOut(`git rev-parse v${version}^{}`);
89
+ const head = gitOut("git rev-parse HEAD");
90
+ tagMatchesHead = tagAnnotated && tagged === head;
91
+ } catch {
92
+ tagAnnotated = false;
93
+ tagMatchesHead = false;
94
+ }
95
+ let releaseUrl = "";
96
+ try {
97
+ const { owner, repo } = originSlug();
98
+ const body = gitOut(
99
+ `curl -fsS -H "Accept: application/vnd.github+json" -H "User-Agent: 0xray-release-gate" https://api.github.com/repos/${owner}/${repo}/releases/tags/v${version}`,
100
+ );
101
+ const parsed = JSON.parse(body);
102
+ releaseUrl = typeof parsed.html_url === "string" ? parsed.html_url : "";
103
+ } catch {
104
+ releaseUrl = "";
105
+ }
106
+ return { version, name, listed, tagAnnotated, tagMatchesHead, releaseUrl };
107
+ }
108
+
109
+ function runProvePublished() {
110
+ const proof = collectPublishedProof();
111
+ const errors = provePublishedRelease(proof);
112
+ process.stdout.write(`\nGate D — ${proof.name}@${proof.version}\n`);
113
+ if (errors.length) {
114
+ for (const error of errors) process.stderr.write(`❌ ${error}\n`);
115
+ process.stderr.write("\n❌ GATE D FAILED — npm listing, annotated tag at HEAD, and GitHub Release are all required\n\n");
116
+ process.exit(1);
117
+ }
118
+ process.stdout.write(`✅ npm lists ${proof.version}\n`);
119
+ process.stdout.write(`✅ annotated tag v${proof.version} points at HEAD\n`);
120
+ process.stdout.write(`✅ GitHub Release ${proof.releaseUrl}\n`);
121
+ process.stdout.write("\n✅ GATE D PROVEN\n\n");
122
+ }
123
+
35
124
  function runLightGate() {
36
125
  const pkg = readRootPackage(rootDir);
37
126
  if (verifyOnly) {
38
- if (!skipDocs) step("1/1 Release docs (light)", docsCheckCmd());
127
+ step("1/1 Release docs (light)", docsCheckCmd());
39
128
  return;
40
129
  }
41
130
  if (pkg.scripts.build) step("1/3 Build", "npm run build");
42
131
  if (pkg.scripts.test) step("2/3 Tests", "npm test");
43
- if (!skipDocs) step("3/3 Release docs (light)", docsCheckCmd());
132
+ step("3/3 Release docs (light)", docsCheckCmd());
44
133
  }
45
134
 
46
135
  function main() {
136
+ const refused = gateCRefusal();
137
+ if (refused) {
138
+ process.stderr.write(`\n❌ ${refused}\n\n`);
139
+ process.exit(1);
140
+ }
141
+ if (provePublished) {
142
+ runProvePublished();
143
+ return;
144
+ }
47
145
  const mode = verifyOnly ? "verify" : "full";
48
146
  console.log("\n╔════════════════════════════════════════════════════════╗");
49
147
  console.log(`║ 🛡️ 0xRay Release Gate (${mode})`.padEnd(57) + "║");
@@ -54,9 +152,7 @@ function main() {
54
152
  runLightGate();
55
153
  } else if (verifyOnly) {
56
154
  step("1/6 Git + reconcile", "node scripts/node/pre-publish-guard.js --verify-only");
57
- if (!skipDocs) {
58
- step("2/6 Release docs", docsCheckCmd());
59
- }
155
+ step("2/6 Release docs", docsCheckCmd());
60
156
  step(
61
157
  "3/6 Plugin infrastructure",
62
158
  "node scripts/test/validate-plugins-e2e.cjs --structural-only",
@@ -75,9 +171,7 @@ function main() {
75
171
  `${JSON.stringify(process.execPath)} ${JSON.stringify(millScript("assert-packed-dist-cli.mjs"))}`,
76
172
  );
77
173
  step("3/8 Tests", "npm run test:comprehensive");
78
- if (!skipDocs) {
79
- step("4/8 Release docs", docsCheckCmd());
80
- }
174
+ step("4/8 Release docs", docsCheckCmd());
81
175
  step(
82
176
  "5/8 Consumer hook verifiers",
83
177
  "npm run verify:pre-commit-diff && npm run verify:pre-push-diff && node scripts/mjs/verify-delegation-gate-core.mjs --host=grok && node scripts/mjs/verify-delegation-gate-core.mjs --host=hermes && node scripts/mjs/verify-delegation-gate-core.mjs --host=opencode && node scripts/mjs/verify-hermes-session-start.mjs && node scripts/mjs/verify-confer-core.mjs && npm run verify:user-aside && npm run build && node scripts/mjs/verify-pipeline-facets.mjs --package-only",
@@ -91,9 +185,11 @@ function main() {
91
185
  }
92
186
  }
93
187
 
94
- console.log("\n╔════════════════════════════════════════════════════════╗");
95
- console.log("║ ✅ RELEASE GATE PASSED ║");
96
- console.log("╚════════════════════════════════════════════════════════╝\n");
188
+ process.stdout.write("\n╔════════════════════════════════════════════════════════╗\n");
189
+ process.stdout.write("║ ✅ GATE A–C LOCAL PASS ║\n");
190
+ process.stdout.write("║ Gate D is not proven by this command. ║\n");
191
+ process.stdout.write("╚════════════════════════════════════════════════════════╝\n");
192
+ process.stdout.write("Prove D after publish: npx @0xray/foundry gate --prove-published\n\n");
97
193
  } catch {
98
194
  console.error("\n❌ RELEASE GATE FAILED — do not tag or publish\n");
99
195
  process.exit(1);
@@ -105,4 +201,4 @@ const isMain =
105
201
 
106
202
  if (isMain) {
107
203
  main();
108
- }
204
+ }
@@ -191,6 +191,8 @@ async function main() {
191
191
  }
192
192
  }
193
193
  run(`git push origin v${version}`, "push tag");
194
+ say("Step 8: Gate D");
195
+ runMill("release-gate.mjs", ["--prove-published"], "gate D");
194
196
  return;
195
197
  }
196
198
 
@@ -281,6 +283,12 @@ async function main() {
281
283
  console.log(` would: git tag -a v${newVersion}`);
282
284
  }
283
285
  run(`git push origin v${newVersion}`, "push tag");
286
+ say("Step 8: Gate D");
287
+ runMill("release-gate.mjs", ["--prove-published"], "gate D");
288
+ if (dryRun) {
289
+ say("Dry run did not prove Gate D.");
290
+ return;
291
+ }
284
292
 
285
293
  console.log("\n╔════════════════════════════════════════════════════════╗");
286
294
  console.log("║ ✅ Release Complete! ║");
@@ -110,7 +110,7 @@ if (packageOnly) {
110
110
  }
111
111
 
112
112
  // P1 — autonomous reporting
113
- if (features?.autonomous_reporting?.enabled === true) {
113
+ if (features?.autonomous_reporting?.enabled === false) {
114
114
  pass('autonomous_reporting.enabled');
115
115
  } else {
116
116
  fail('autonomous_reporting.enabled');
@@ -1016,16 +1016,18 @@ const CURSOR_HOOK_EVENTS = [
1016
1016
  ["beforeReadFile", "before-read-file.sh"],
1017
1017
  ];
1018
1018
 
1019
+ const CURSOR_GATE_EVENTS = new Set(["preToolUse", "beforeShellExecution", "beforeReadFile"]);
1020
+
1019
1021
  const CURSOR_WEAR_BACKUP = "hooks.json.xray-before";
1020
1022
 
1021
1023
  const CLOUD_SAFE_CURSOR_HOOKS = {
1022
1024
  version: 1,
1023
1025
  hooks: {
1024
- preToolUse: [{ command: ".cursor/hooks/pre-tool-use.sh" }],
1026
+ preToolUse: [{ command: ".cursor/hooks/pre-tool-use.sh", failClosed: true }],
1025
1027
  preCompact: [{ command: ".cursor/hooks/pre-compact.sh" }],
1026
1028
  afterFileEdit: [{ command: ".cursor/hooks/after-file-edit.sh" }],
1027
- beforeShellExecution: [{ command: ".cursor/hooks/before-shell-execution.sh" }],
1028
- beforeReadFile: [{ command: ".cursor/hooks/before-read-file.sh" }],
1029
+ beforeShellExecution: [{ command: ".cursor/hooks/before-shell-execution.sh", failClosed: true }],
1030
+ beforeReadFile: [{ command: ".cursor/hooks/before-read-file.sh", failClosed: true }],
1029
1031
  },
1030
1032
  };
1031
1033
 
@@ -1071,6 +1073,9 @@ function mergeCloudSafeCursorHooks(existing) {
1071
1073
  !Array.isArray(current) || !current.some((h) => isRelativeCursorHookCommand(h && h.command));
1072
1074
  if (!current || leftover || missingRelative) {
1073
1075
  next.hooks[name] = entries;
1076
+ } else if (entries[0].failClosed) {
1077
+ // Gate events fail closed: our relative entry gets failClosed even on an existing hooks.json (re-wear upgrades it).
1078
+ next.hooks[name] = current.map((h) => (h && isRelativeCursorHookCommand(h.command) ? { ...h, failClosed: true } : h));
1074
1079
  }
1075
1080
  }
1076
1081
  return next;
@@ -1279,7 +1284,10 @@ function installedHookCommand(fromDir, packageRoot, scriptName) {
1279
1284
  function installedCursorHookEntries(fromDir, packageRoot) {
1280
1285
  const hooks = {};
1281
1286
  for (const [event, script] of CURSOR_HOOK_EVENTS) {
1282
- hooks[event] = [{ command: installedHookCommand(fromDir, packageRoot, script) }];
1287
+ const entry = { command: installedHookCommand(fromDir, packageRoot, script) };
1288
+ // Gate events fail closed: Cursor denies when the hook crashes or prints no JSON.
1289
+ if (CURSOR_GATE_EVENTS.has(event)) entry.failClosed = true;
1290
+ hooks[event] = [entry];
1283
1291
  }
1284
1292
  return hooks;
1285
1293
  }
@@ -1610,8 +1618,10 @@ function ownedDeletionEdits(tokens, elements) {
1610
1618
  return edits;
1611
1619
  }
1612
1620
 
1613
- function renderEventEntry(command) {
1614
- return `{ "command": ${JSON.stringify(command)} }`;
1621
+ function renderEventEntry(command, failClosed) {
1622
+ return failClosed
1623
+ ? `{ "command": ${JSON.stringify(command)}, "failClosed": true }`
1624
+ : `{ "command": ${JSON.stringify(command)} }`;
1615
1625
  }
1616
1626
 
1617
1627
  function editJsoncHooks(text, xrayByEvent, targetDir) {
@@ -1627,7 +1637,7 @@ function editJsoncHooks(text, xrayByEvent, targetDir) {
1627
1637
  const hooksPair = root.pairs.find((pair) => pair.key === "hooks");
1628
1638
  if (!hooksPair) {
1629
1639
  const inner = Object.entries(xrayByEvent)
1630
- .map(([event, entries]) => `"${event}": [${renderEventEntry(entries[0].command)}]`)
1640
+ .map(([event, entries]) => `"${event}": [${renderEventEntry(entries[0].command, entries[0].failClosed)}]`)
1631
1641
  .join(", ");
1632
1642
  const needsComma = root.pairs.length > 0;
1633
1643
  edits.push({
@@ -1642,9 +1652,10 @@ function editJsoncHooks(text, xrayByEvent, targetDir) {
1642
1652
  const missing = [];
1643
1653
  for (const [event, entries] of Object.entries(xrayByEvent)) {
1644
1654
  const command = entries[0].command;
1655
+ const failClosed = entries[0].failClosed === true;
1645
1656
  const pair = hooksObj.pairs.find((item) => item.key === event);
1646
1657
  if (!pair) {
1647
- missing.push([event, command]);
1658
+ missing.push([event, command, failClosed]);
1648
1659
  continue;
1649
1660
  }
1650
1661
  if (tokens[pair.valIdx].type !== "[") throw new Error(`${event} is not an array`);
@@ -1666,7 +1677,7 @@ function editJsoncHooks(text, xrayByEvent, targetDir) {
1666
1677
  edits.push({
1667
1678
  start: tokens[pair.valEnd].start,
1668
1679
  end: tokens[pair.valEnd].start,
1669
- insert: `${elements.length > 0 ? ", " : ""}${renderEventEntry(command)}`,
1680
+ insert: `${elements.length > 0 ? ", " : ""}${renderEventEntry(command, failClosed)}`,
1670
1681
  });
1671
1682
  continue;
1672
1683
  }
@@ -1678,13 +1689,17 @@ function editJsoncHooks(text, xrayByEvent, targetDir) {
1678
1689
  insert: JSON.stringify(command),
1679
1690
  });
1680
1691
  }
1692
+ if (failClosed && !objectPairs(tokens, first.el.startIdx).pairs.some((p) => p.key === "failClosed")) {
1693
+ // Re-wear upgrades an older entry: gate events fail closed.
1694
+ edits.push({ start: first.cmd.token.end, end: first.cmd.token.end, insert: ', "failClosed": true' });
1695
+ }
1681
1696
  if (owned.length > 1) {
1682
1697
  edits.push(...ownedDeletionEdits(tokens, owned.slice(1).map((item) => item.el)));
1683
1698
  }
1684
1699
  }
1685
1700
  if (missing.length > 0) {
1686
1701
  const rendered = missing
1687
- .map(([event, command]) => `"${event}": [${renderEventEntry(command)}]`)
1702
+ .map(([event, command, failClosed]) => `"${event}": [${renderEventEntry(command, failClosed)}]`)
1688
1703
  .join(", ");
1689
1704
  edits.push({
1690
1705
  start: tokens[hooksObj.closeIdx].start,
@@ -1778,20 +1793,21 @@ function mergeInstalledCursorHooks(existing, xrayByEvent) {
1778
1793
  continue;
1779
1794
  }
1780
1795
  const command = shipped[0].command;
1796
+ const extra = shipped[0].failClosed ? { failClosed: true } : {};
1781
1797
  let replaced = false;
1782
1798
  const next = [];
1783
1799
  for (const entry of current) {
1784
1800
  const cmd = entry && typeof entry === "object" ? entry.command : "";
1785
1801
  if (isXrayHookCommand(cmd)) {
1786
1802
  if (!replaced) {
1787
- next.push({ ...entry, command });
1803
+ next.push({ ...entry, command, ...extra });
1788
1804
  replaced = true;
1789
1805
  }
1790
1806
  continue;
1791
1807
  }
1792
1808
  next.push(entry);
1793
1809
  }
1794
- if (!replaced) next.push({ command });
1810
+ if (!replaced) next.push({ command, ...extra });
1795
1811
  hooks[name] = next;
1796
1812
  }
1797
1813
  const doc = {};
@@ -39,17 +39,25 @@ export function foundryCliPath(millRoot) {
39
39
  return path.join(millRoot, "cli.js");
40
40
  }
41
41
 
42
- export function runFoundryCli(millRoot, consumerRoot, args) {
42
+ /** Clean-room foundry env. The machine home is empty so a plugin on the real machine cannot fail the seat inspect. */
43
+ export function proofFoundryEnv(consumerRoot, baseEnv = process.env) {
43
44
  const isolatedHome = path.join(consumerRoot, ".proof-home");
45
+ const machineHome = path.join(consumerRoot, ".proof-machine");
44
46
  fs.mkdirSync(isolatedHome, { recursive: true });
47
+ fs.mkdirSync(machineHome, { recursive: true });
48
+ return {
49
+ ...baseEnv,
50
+ FOUNDRY_ROOT: consumerRoot,
51
+ HOME: isolatedHome,
52
+ FOUNDRY_MACHINE_HOME: machineHome,
53
+ };
54
+ }
55
+
56
+ export function runFoundryCli(millRoot, consumerRoot, args) {
45
57
  return spawnSync(process.execPath, [foundryCliPath(millRoot), ...args], {
46
58
  cwd: consumerRoot,
47
59
  encoding: "utf8",
48
- env: {
49
- ...process.env,
50
- FOUNDRY_ROOT: consumerRoot,
51
- HOME: isolatedHome,
52
- },
60
+ env: proofFoundryEnv(consumerRoot),
53
61
  });
54
62
  }
55
63
 
@@ -2,7 +2,7 @@ import { createRequire } from 'node:module';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
6
  import { definePluginEntry } from 'openclaw/plugin-sdk/plugin-entry';
7
7
 
8
8
  const require = createRequire(import.meta.url);
@@ -39,7 +39,7 @@ export default definePluginEntry({
39
39
  register(api) {
40
40
  api.on(
41
41
  'before_tool_call',
42
- (event, ctx) => {
42
+ async (event, ctx) => {
43
43
  try {
44
44
  const xrayRoot = resolveXrayRoot();
45
45
  if (!xrayRoot) {
@@ -61,6 +61,21 @@ export default definePluginEntry({
61
61
  if (!outcome.allow) {
62
62
  return { block: true, blockReason: outcome.reason || '0xRay constitution' };
63
63
  }
64
+ const spoken = `${event.toolName} ${JSON.stringify(event.params || {})}`.slice(0, 500);
65
+ const speakPath = [
66
+ join(xrayRoot, 'dist/integrations/hooks/pre-tool-speak.mjs'),
67
+ join(xrayRoot, 'src/integrations/hooks/pre-tool-speak.mjs'),
68
+ ].find((candidate) => existsSync(candidate));
69
+ if (!speakPath) return undefined;
70
+ const speak = await import(pathToFileURL(speakPath).href);
71
+ const lens = speak.lensDecision(projectRoot, event.toolName, spoken);
72
+ if (lens) {
73
+ return { block: true, blockReason: lens.reason };
74
+ }
75
+ const route = speak.repertoireRoute(spoken);
76
+ if (route?.hookSpecificOutput) {
77
+ return { block: false, hookSpecificOutput: route.hookSpecificOutput };
78
+ }
64
79
  return undefined;
65
80
  } catch (err) {
66
81
  return {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xray-pre-tool",
3
- "version": "4.0.42",
3
+ "version": "4.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "0xRay constitution + temperament PreToolUse for OpenClaw",
@@ -2,7 +2,7 @@
2
2
 
3
3
  Factory memory organ for [0xRay](https://github.com/0xRayAI/xray): seed primitives, in-process memory routing, extra MCP (not an 8th `xray-*` server).
4
4
 
5
- **v0.2.9** · 0xRay **4.0** wear. Runtime does **not** depend on `0xray` (optional peer for suit-verify scripts).
5
+ **v0.2.9** · 0xRay **4.1** wear. Runtime does **not** depend on `0xray` (optional peer for suit-verify scripts).
6
6
 
7
7
  ## Install
8
8
 
@@ -1,6 +1,6 @@
1
1
  # 0xRay AI Agents
2
2
 
3
- Quick reference for the 0xRay AI orchestration framework (**4.0** — 7 consumer MCP servers).
3
+ Quick reference for the 0xRay suit (**4.1** — 7 consumer MCP servers). 0xRay is a three-subsystem autonomous engine. The suit is the wear.
4
4
 
5
5
  ## Available MCP Servers
6
6
 
@@ -28,7 +28,7 @@ All seven use `npx -y 0xray mcp <cmd>`:
28
28
 
29
29
  ## Governance
30
30
 
31
- xray operates under the three-subsystem model: Inference + External Governance (Dynamo Solar SSOT) + Autonomous Engine (thinDispatch 7-flow in MCP orchestrator). All actions are validated against the Universal Development Codex before execution.
31
+ 0xRay is a three-subsystem autonomous engine: Inference + External Governance (Dynamo Solar SSOT) + Autonomous Engine (thinDispatch 7-flow in MCP orchestrator). The suit is the wear. All actions are validated against the Universal Development Codex before execution.
32
32
 
33
33
  **Codex**: `.xray/codex.json` — **70 terms** across all agent interactions.
34
34
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./features.schema.json",
3
- "version": "4.0.42",
3
+ "version": "4.1.0",
4
4
  "description": "0xRay Framework - Unified Feature Configuration",
5
5
  "suit_temperament": {
6
6
  "profile": "auto",
@@ -54,16 +54,16 @@
54
54
  "auto_provision_worktree": false
55
55
  }
56
56
  },
57
- "grok_postprocessor_light": true,
57
+ "grok_postprocessor_light": false,
58
58
  "inference_session_capture": {
59
59
  "enabled": true,
60
60
  "min_commits": 3,
61
61
  "lookback_commits": 20
62
62
  },
63
63
  "autonomous_reporting": {
64
- "enabled": true,
64
+ "enabled": false,
65
65
  "interval_minutes": 60,
66
- "auto_schedule": true,
66
+ "auto_schedule": false,
67
67
  "include_health_assessment": true,
68
68
  "include_agent_activities": true,
69
69
  "include_pipeline_operations": true,
@@ -174,7 +174,7 @@
174
174
  "every_n_todos_completed": 0
175
175
  },
176
176
  "inference": {
177
- "description": "Self-improvement cycle that grades a new named session into repertoire",
177
+ "description": "The cycle runs from the CLI command inference:run and the session wake does not start it.",
178
178
  "enabled": true,
179
179
  "workflow_dir": ".xray/inference",
180
180
  "reports_dir": ".xray/reports",
@@ -1,41 +0,0 @@
1
- ---
2
- title: Live mesh
3
- sidebar_label: Live mesh
4
- plate_type: pipeline
5
- ---
6
-
7
- # Live mesh
8
-
9
- Ping-pong eng events become one ordered JSON feed, then a mesh render. Markdown is never the source.
10
-
11
- ```
12
- ┌────────────────────────────────────────────────────────────┐
13
- │ INPUT LAYER │
14
- │ ┌──────────────┐ ┌──────────┐ ┌─────────┐ ┌───────────┐ │
15
- │ │ GitHub REST │ │ deploy │ │ /health │ │ X ledger │ │
16
- │ │ PR/issue │ │ muse │ │ probe │ │ herald │ │
17
- │ └──────┬───────┘ └────┬─────┘ └────┬────┘ └─────┬─────┘ │
18
- │ └──────────────┴────────────┴────────────┘ │
19
- └─────────────────────────────┬──────────────────────────────┘
20
- v
21
- ┌─────────────────────────────┼──────────────────────────────┐
22
- │ PROCESSING LAYER v │
23
- │ ┌────────────────────────────────────────────────────┐ │
24
- │ │ fetch_feed.py │ │
25
- │ │ poll → dedupe on id → rewrite live-events.json │ │
26
- │ │ --watch keeps the feed warm │ │
27
- │ └──────────────────────────┬─────────────────────────┘ │
28
- └──────────────────────────────┼────────────────────────────┘
29
- v
30
- ┌──────────────────────────────┼────────────────────────────┐
31
- │ OUTPUT LAYER v │
32
- │ ┌──────────────────────┐ ┌──────────────────────────┐ │
33
- │ │ live-events.json │ │ render_mesh.py │ │
34
- │ │ (+ jsonl append) │ │ mp4 / --follow │ │
35
- │ └──────────────────────┘ └──────────────────────────┘ │
36
- └────────────────────────────────────────────────────────────┘
37
- ```
38
-
39
- Paths live under `house/live-mesh/`. Schema is `schema.json`. The plate that belongs here is house.
40
-
41
- stamped · 0xray 4.0.42