0xray 4.0.30 → 4.0.31

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 (39) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.opencode/skills/orchestrator/SKILL.md +12 -0
  3. package/.xray/features.json +1 -1
  4. package/dist/CHANGELOG.md +9 -0
  5. package/dist/cli/commands/validate.js +5 -0
  6. package/dist/cli/index.js +16 -1
  7. package/dist/integrations/grok/hooks/grok-hook-utils.js +24 -4
  8. package/dist/integrations/grok/hooks/post-tool-use.js +2 -1
  9. package/dist/integrations/grok/hooks/pre-tool-use.js +90 -5
  10. package/dist/integrations/grok/hooks/session-start.js +7 -2
  11. package/dist/integrations/hooks/goggles-pipeline.mjs +568 -0
  12. package/dist/integrations/hooks/goggles-planes.json +28 -0
  13. package/dist/integrations/hooks/plates.cjs +23 -0
  14. package/dist/integrations/hooks/station-hook-runtime.cjs +40 -11
  15. package/dist/integrations/hooks/station-memory-ingest.mjs +3 -0
  16. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  17. package/dist/mcps/orchestrator/config/agent-capabilities.js +7 -0
  18. package/dist/memory-routing/plates.d.ts +1 -1
  19. package/dist/memory-routing/plates.js +5 -0
  20. package/dist/memory-routing/provider-loader.js +2 -0
  21. package/dist/nucleus/thin-dispatch.d.ts +1 -0
  22. package/dist/nucleus/thin-dispatch.js +4 -2
  23. package/dist/skills/orchestrator/SKILL.md +12 -0
  24. package/docs-site/docs/plates/glossary.md +27 -0
  25. package/docs-site/docs/plates/goggles.md +18 -0
  26. package/docs-site/docs/plates/grokbot.md +41 -0
  27. package/docs-site/docs/plates/host-pack.md +32 -0
  28. package/docs-site/docs/plates/house.md +42 -39
  29. package/docs-site/docs/plates/index.md +13 -1
  30. package/docs-site/docs/plates/kits.md +28 -0
  31. package/docs-site/docs/plates/suit.md +40 -0
  32. package/package.json +1 -1
  33. package/scripts/foundry/assert-packed-dist-cli.mjs +5 -0
  34. package/scripts/node/consumer-install-smoke.mjs +5 -0
  35. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  36. package/src/mcps/orchestrator/config/agent-capabilities.ts +8 -0
  37. package/src/skills/orchestrator/SKILL.md +12 -0
  38. package/vendor/@0xray/repertoire/dist/paths.js +4 -0
  39. package/xray/features.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "4.0.30",
3
+ "version": "4.0.31",
4
4
  "description": "0xRay suit for Grok, Hermes, OpenClaw, OpenCode: mill+inspect, Codex, 7 MCP servers. Costume 45/42 is opt-in foundry, not the default wear.",
5
5
  "author": {
6
6
  "name": "0xRay AI",
@@ -53,6 +53,18 @@ When `multi_agent_orchestration.lead_dev_mode` is true in `features.json` (defau
53
53
  | Test failures | bug-triage |
54
54
  | Review | code-review |
55
55
 
56
+ ## Goggles
57
+
58
+ Pushed on `feat/goggles-look`. Not merged. Not published. Grok CLI has not shipped this. Wear the local build. From the worn checkout run `node dist/integrations/hooks/goggles-pipeline.mjs`. Do not run `npx 0xray goggles`. If `dist` is older than `src/integrations/hooks/goggles-pipeline.mjs`, build that tree and wear that `dist`. A subagent wears this same local build.
59
+
60
+ Kind 0 is actuality. It compares the list in the file to the same six names in the code. It speaks only when those lists differ. A match is quiet. It does not read the suit that is running. It is not a plate type.
61
+
62
+ Kind 1 names one plane: dichotomy, syncopate, synthesis, or loop. The lens holds that plane. An action on that plane is quiet. An action on another plane, or on a pipeline drawing, is stopped. A pipeline drawing is not a scope stop. Ground is not a scope stop. Scope, when named, is ecosystem, part, one flow, or one artifact, and an action wider than that scope is stopped. One kind and one scope level at a time. Two planes, calling, ground, and a pipeline name do not arm the lens. `outer` without one plane is `Name one plane.` After 1 is empty. Nothing is picked. Calling stays off.
63
+
64
+ `digest` and `triage` return the card already built by `assemblePlane`. The fields are from, digest, plate, entry, exit, files, skills, setup, teardown, and worn. Empty stays empty. The rows are ground, routing, house, boot, governance, memory-recall, orchestration, processor, and reporting. Those nine are not Kind 0. `digest` fills the card. Entry and exit are the plate's input and output box titles when that plate has them. Setup and teardown stay empty unless the row names them. The mill path is `scripts/foundry`. `triage` adds the empty fields and whether the row holds. Zoom after the card is the same ladder: ecosystem, part, one flow, one artifact. One file is one artifact. Several files are not narrowed to a guessed file.
65
+
66
+ The hold lasts for the work that named the plane. It does not stop one tool and then forget. Work that says goggles clears the hold and stays quiet. The suit does not invent the plane.
67
+
56
68
  ## Hooks (rewired, not new MCPs)
57
69
 
58
70
  - **SessionStart** → `session-start.js` boots lead_dev_mode
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./features.schema.json",
3
- "version": "4.0.30",
3
+ "version": "4.0.31",
4
4
  "description": "0xRay Framework - Unified Feature Configuration",
5
5
  "suit_temperament": {
6
6
  "profile": "auto",
package/dist/CHANGELOG.md CHANGED
@@ -7,6 +7,7 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
7
7
  ## [Unreleased]
8
8
 
9
9
  ### Features
10
+ - **Goggles.** Proposed plates for goggles, suit, kits, host pack, and glossary. Goggles views one plane. Recall opens a plate when speech names that plate. Plates only. Not a new skill.
10
11
  - **Plates.** Pipeline schematics live in `docs-site/docs/plates/` (routing, governance, boot, orchestration, processor, reporting, memory-recall). Station keeps one pointer line. Pre-compact and the `MEMORY_ROUTING` inject carry that one schematic. A lesson is an episode. A plate is the machine. Not a new skill.
11
12
  - **Vendored Repertoire 0.2.8.** `vendor/@0xray/repertoire` is that package. Lesson lines and retained ids stay at 20. Speech that names nothing mints a learned signal, and only those mints evict past 24. Not a new skill.
12
13
  - **Feat rebase.** When the mandated work turns into mantra, or the job has split across planes that no longer share one present, stop and answer the original message again from this seat. On `LEAD-CADENCE.md`, the orchestrator skill, and the ship-ready mill gate. Project dest grows the name `feat-rebase` from the stack overlay. Not a new skill.
@@ -41,6 +42,14 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
41
42
  - Archive root `integrations/` costume SKILL dump → `docs/archive/integrations-costume/`. Live wear is `src/integrations`.
42
43
  - Archive root `commands/` stale copy → `docs/archive/commands-copy/`. Live wear is `src/opencode/commands`.
43
44
 
45
+ ## [4.0.31] - 2026-10-01
46
+
47
+ Next patch after npm `4.0.30`. Not published from this commit.
48
+
49
+ Goggles looks at one pipeline. Kind 0 name-checks the six outer names and stays quiet when the worn list matches the map. Digest and triage return one card. A leave is denied. Session boot hands the card through `handCard` and normalizes the workspace root, including a trailing slash. The mill path is `scripts/foundry`. Run `node dist/integrations/hooks/goggles-pipeline.mjs`. This cut also carries the plate docs and the live project-file signal count that landed on main after 4.0.30. Calling and confer stay off. Companion `@0xray/grok-bot` is unchanged at 0.1.8.
50
+
51
+ ---
52
+
44
53
  ## [4.0.30] - 2026-09-28
45
54
 
46
55
  Next patch after npm `4.0.29`. Released 2026-09-28.
@@ -25,6 +25,11 @@ export const VALIDATE_PACK_PATHS = [
25
25
  "dist/integrations/hooks/plates.cjs",
26
26
  "docs-site/docs/plates/memory-recall.md",
27
27
  "docs-site/docs/plates/processor.md",
28
+ "docs-site/docs/plates/goggles.md",
29
+ "docs-site/docs/plates/suit.md",
30
+ "docs-site/docs/plates/kits.md",
31
+ "docs-site/docs/plates/host-pack.md",
32
+ "docs-site/docs/plates/glossary.md",
28
33
  ];
29
34
  export const CONSUMER_WEAR_PATHS = [
30
35
  "AGENTS.md",
package/dist/cli/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * Provides commands for installing and managing xray framework
6
6
  */
7
7
  import { Command } from "commander";
8
- import { execSync, spawn } from "child_process";
8
+ import { execSync, spawn, spawnSync } from "child_process";
9
9
  import { join, resolve } from "path";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { readFileSync, existsSync } from "fs";
@@ -76,6 +76,21 @@ program
76
76
  process.exit(1);
77
77
  }
78
78
  });
79
+ program
80
+ .command("goggles")
81
+ .argument("[args...]", "plane, then examine, triage, cascade, or teardown")
82
+ .description("Glimpse every plane, or one plane's way in. A hit does not open the plane.")
83
+ .action((args) => {
84
+ const built = join(packageRoot, "dist", "integrations", "hooks", "goggles-pipeline.mjs");
85
+ const source = join(packageRoot, "src", "integrations", "hooks", "goggles-pipeline.mjs");
86
+ const script = existsSync(built) ? built : source;
87
+ const result = spawnSync(process.execPath, [script, ...(args ?? [])], { encoding: "utf8" });
88
+ if (result.stdout)
89
+ process.stdout.write(result.stdout);
90
+ if (result.stderr)
91
+ process.stderr.write(result.stderr);
92
+ process.exit(result.status ?? 1);
93
+ });
79
94
  program
80
95
  .command("wear")
81
96
  .description("Wear the 0xray suit in this git checkout")
@@ -28,6 +28,7 @@ import {
28
28
  retainCompactFields,
29
29
  writeStationMarkdown,
30
30
  } from '../../hooks/station-hook-runtime.mjs';
31
+ import { handCard } from '../../hooks/goggles-pipeline.mjs';
31
32
 
32
33
  export {
33
34
  checkPendingDelegationGate,
@@ -86,12 +87,18 @@ const WRITE_TOOLS = new Set([
86
87
  'EditNotebook',
87
88
  ]);
88
89
 
90
+ export function normalizeWorkspaceRoot(root) {
91
+ const raw = String(root || '').trim();
92
+ if (!raw) return raw;
93
+ return path.resolve(raw);
94
+ }
95
+
89
96
  export function workspaceRoot() {
90
- return (
97
+ return normalizeWorkspaceRoot(
91
98
  process.env.GROK_WORKSPACE_ROOT ||
92
99
  process.env.CLAUDE_PROJECT_DIR ||
93
100
  process.env.XRAY_ROOT ||
94
- process.cwd()
101
+ process.cwd(),
95
102
  );
96
103
  }
97
104
 
@@ -198,6 +205,7 @@ export function extractToolContext(event) {
198
205
 
199
206
  if (toolInput.path) paths.push(String(toolInput.path));
200
207
  if (toolInput.file_path) paths.push(String(toolInput.file_path));
208
+ if (toolInput.target_file) paths.push(String(toolInput.target_file));
201
209
  if (toolInput.target_notebook) paths.push(String(toolInput.target_notebook));
202
210
  if (Array.isArray(toolInput.paths)) paths.push(...toolInput.paths.map(String));
203
211
 
@@ -301,7 +309,7 @@ export function buildSessionBootPayload(root, source = '0xray/grok-session-start
301
309
  ? 'xray-orchestrator analyze-complexity optional on frontier (spawn warns, does not deny)'
302
310
  : 'xray-orchestrator → analyze-complexity (required before spawn_subagent)',
303
311
  enforcement: 'PreToolUse hook — Codex constitution always on; ceremony scales by suit_temperament',
304
- workspaceRoot: root,
312
+ workspaceRoot: normalizeWorkspaceRoot(root),
305
313
  ...heat,
306
314
  ...(siblingRoots.length > 0 ? { siblingWorkspaceRoots: siblingRoots } : {}),
307
315
  ...(conferPending ? { conferPending: true, conferTrigger: 'analyze-complexity at synthesis checkpoint' } : {}),
@@ -322,6 +330,11 @@ export function writeSessionBoot(root, payload) {
322
330
  fs.mkdirSync(stateDir, { recursive: true });
323
331
  fs.writeFileSync(sessionBootPath(root), JSON.stringify(payload, null, 2));
324
332
  writeStationMarkdown(root, payload);
333
+ try {
334
+ handCard(root, payload && (payload.cardText || payload.intent));
335
+ } catch {
336
+ /* a missed card must not fail the boot */
337
+ }
325
338
  return sessionBootPath(root);
326
339
  } catch {
327
340
  return null;
@@ -333,9 +346,16 @@ export function sessionBootNeedsRefresh(existing, root) {
333
346
  if (existing.lead_dev_mode === undefined) return true;
334
347
  if (existing.host !== 'grok') return true;
335
348
  if (!existing.suit_profile) return true;
336
- if (existing.workspaceRoot && existing.workspaceRoot !== root) return true;
349
+ if (
350
+ existing.workspaceRoot &&
351
+ normalizeWorkspaceRoot(existing.workspaceRoot) !== normalizeWorkspaceRoot(root)
352
+ ) {
353
+ return true;
354
+ }
337
355
  if (!existing.repertoireResume) return true;
338
356
  if (!existing.stationLine) return true;
357
+ const liveResume = buildRepertoireResume(root);
358
+ if (liveResume && existing.repertoireResume !== liveResume) return true;
339
359
  return false;
340
360
  }
341
361
 
@@ -10,6 +10,7 @@ import {
10
10
  isSubagentTool,
11
11
  isWriteTool,
12
12
  loadFeatures,
13
+ normalizeWorkspaceRoot,
13
14
  readStdinJson,
14
15
  resolveSessionId,
15
16
  workspaceRoot,
@@ -33,7 +34,7 @@ function extractToolOutput(event) {
33
34
  }
34
35
 
35
36
  export function handlePostToolUse(event, root = workspaceRoot()) {
36
- const eventRoot = event.workspaceRoot || event.cwd || root;
37
+ const eventRoot = normalizeWorkspaceRoot(event.workspaceRoot || event.cwd || root);
37
38
  const sessionId = resolveSessionId(event);
38
39
  const { toolName, toolInput } = extractToolContext(event);
39
40
 
@@ -15,11 +15,72 @@ import {
15
15
  isShellTool,
16
16
  isWriteTool,
17
17
  loadFeatures,
18
+ normalizeWorkspaceRoot,
18
19
  readStdinJson,
19
20
  resolveSessionId,
20
21
  workspaceRoot,
21
22
  } from './grok-hook-utils.js';
22
23
  import { appendHookActivity } from './grok-hook-activity.js';
24
+ import { cardStop } from '../../hooks/goggles-pipeline.mjs';
25
+ import { createRequire } from 'node:module';
26
+ import { existsSync } from 'node:fs';
27
+ import { dirname, join } from 'node:path';
28
+ import { fileURLToPath } from 'node:url';
29
+
30
+ const hookDir = dirname(fileURLToPath(import.meta.url));
31
+ let scoreAndRouteFn;
32
+
33
+ function loadScoreAndRoute() {
34
+ if (scoreAndRouteFn !== undefined) return scoreAndRouteFn;
35
+ scoreAndRouteFn = null;
36
+ const require = createRequire(import.meta.url);
37
+ const candidates = [
38
+ join(hookDir, '../../../nucleus/thin-dispatch.js'),
39
+ join(hookDir, '../../../../dist/nucleus/thin-dispatch.js'),
40
+ ];
41
+ for (const candidate of candidates) {
42
+ if (!existsSync(candidate)) continue;
43
+ try {
44
+ const loaded = require(candidate);
45
+ if (typeof loaded.scoreAndRoute === 'function') {
46
+ scoreAndRouteFn = loaded.scoreAndRoute;
47
+ return scoreAndRouteFn;
48
+ }
49
+ } catch {
50
+ /* next built copy */
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+
56
+ /** Speak only when the organ changes the agent. A match that leaves the agent is quiet. */
57
+ export function repertoireDecisionFields(routed) {
58
+ if (!routed?.memoryRouting?.overridden || !routed.agent) return null;
59
+ const signals = Array.isArray(routed.memoryRouting.signals)
60
+ ? routed.memoryRouting.signals.slice(0, 4).filter(Boolean)
61
+ : [];
62
+ const why = signals.length ? ` on ${signals.join(', ')}` : '';
63
+ return {
64
+ agent: routed.agent,
65
+ gate: 'repertoire',
66
+ hookSpecificOutput: {
67
+ hookEventName: 'PreToolUse',
68
+ additionalContext: `Repertoire changed the route to ${routed.agent}${why}. Follow that agent for this work.`,
69
+ },
70
+ };
71
+ }
72
+
73
+ function repertoireRoute(operation) {
74
+ const text = String(operation || '').slice(0, 500).trim();
75
+ if (!text) return null;
76
+ try {
77
+ const scoreAndRoute = loadScoreAndRoute();
78
+ if (!scoreAndRoute) return null;
79
+ return repertoireDecisionFields(scoreAndRoute(text, {}));
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
23
84
 
24
85
  function finish(root, decision, reason, hint, toolName, extra = {}) {
25
86
  const out = { decision, ...extra };
@@ -30,6 +91,7 @@ function finish(root, decision, reason, hint, toolName, extra = {}) {
30
91
  tool: toolName,
31
92
  reason: reason || hint || null,
32
93
  gate: extra.gate || null,
94
+ agent: extra.agent || null,
33
95
  livePath: true,
34
96
  });
35
97
  process.exit(0);
@@ -41,7 +103,7 @@ async function main() {
41
103
 
42
104
  try {
43
105
  const event = await readStdinJson();
44
- const eventRoot = event.workspaceRoot || event.cwd || root;
106
+ const eventRoot = normalizeWorkspaceRoot(event.workspaceRoot || event.cwd || root);
45
107
  ensureSessionBoot(eventRoot, '0xray/grok-pre-tool-use-boot');
46
108
 
47
109
  const features = loadFeatures(eventRoot);
@@ -75,9 +137,28 @@ async function main() {
75
137
  if (extraBlock) finish(eventRoot, 'deny', extraBlock, null, toolName);
76
138
  }
77
139
 
140
+ const stop = cardStop(
141
+ eventRoot,
142
+ toolName,
143
+ [cmd, content, toolInput.pattern, toolInput.query, ...(ctx.paths || [])].filter(Boolean).join('\n'),
144
+ ctx.paths || [],
145
+ toolInput,
146
+ );
147
+ if (stop?.decision === 'deny') {
148
+ finish(eventRoot, 'deny', stop.reason, null, toolName, { gate: stop.gate });
149
+ }
150
+ if (stop?.decision === 'allow') {
151
+ finish(eventRoot, 'allow', null, null, toolName, {
152
+ gate: stop.gate,
153
+ hookSpecificOutput: stop.hookSpecificOutput,
154
+ });
155
+ }
156
+
157
+ const route = repertoireRoute([toolName, cmd, content].filter(Boolean).join(' '));
158
+
78
159
  if (isShellTool(toolName) && cmd) {
79
160
  const testHint = checkFullTestSuite(cmd, features);
80
- if (testHint) finish(eventRoot, 'allow', null, testHint, toolName);
161
+ if (testHint) finish(eventRoot, 'allow', null, testHint, toolName, route || {});
81
162
  }
82
163
 
83
164
  if (gateBlock.reason) {
@@ -87,11 +168,11 @@ async function main() {
87
168
  gateBlock.reason,
88
169
  gateBlock.hint,
89
170
  toolName,
90
- { gate: gateBlock.gate, warn: true },
171
+ { gate: gateBlock.gate, warn: true, ...(route || {}) },
91
172
  );
92
173
  }
93
174
 
94
- finish(eventRoot, 'allow', null, null, toolName);
175
+ finish(eventRoot, 'allow', null, null, toolName, route || {});
95
176
  } catch (err) {
96
177
  appendHookActivity(root, 'grok-pre-tool-use', 'hook-error', 'error', {
97
178
  tool: toolName,
@@ -116,6 +197,7 @@ function extractFromEvent(event) {
116
197
 
117
198
  if (toolInput.path) paths.push(String(toolInput.path));
118
199
  if (toolInput.file_path) paths.push(String(toolInput.file_path));
200
+ if (toolInput.target_file) paths.push(String(toolInput.target_file));
119
201
  if (toolInput.target_notebook) paths.push(String(toolInput.target_notebook));
120
202
  if (Array.isArray(toolInput.paths)) paths.push(...toolInput.paths.map(String));
121
203
 
@@ -133,4 +215,7 @@ function extractFromEvent(event) {
133
215
  };
134
216
  }
135
217
 
136
- main();
218
+ const launchedAsCli = Boolean(process.argv[1] && process.argv[1].endsWith('pre-tool-use.js'));
219
+ if (launchedAsCli) {
220
+ main();
221
+ }
@@ -10,6 +10,7 @@ import {
10
10
  ensureSessionBoot,
11
11
  readStdinJson,
12
12
  resolveSessionId,
13
+ normalizeWorkspaceRoot,
13
14
  workspaceRoot,
14
15
  writeSessionBoot,
15
16
  } from './grok-hook-utils.js';
@@ -21,6 +22,7 @@ import {
21
22
  scheduleAutonomousReportingMarker,
22
23
  runInferenceImprovementLight,
23
24
  } from '../../hooks/pipeline-hook-runtime.mjs';
25
+ import { maintainLens } from '../../hooks/goggles-pipeline.mjs';
24
26
 
25
27
  function resolveHookEvent(event) {
26
28
  if (process.env.GROK_HOOK_EVENT) return process.env.GROK_HOOK_EVENT;
@@ -115,7 +117,7 @@ async function main() {
115
117
  try {
116
118
  const event = await readStdinJson();
117
119
  HOOK_EVENT = resolveHookEvent(event);
118
- const eventRoot = event.workspaceRoot || event.cwd || root;
120
+ const eventRoot = normalizeWorkspaceRoot(event.workspaceRoot || event.cwd || root);
119
121
  const sessionId = resolveSessionId(event);
120
122
  if (HOOK_EVENT === 'user_prompt_submit' && sessionId) {
121
123
  recordSynthesisTurnSlice(eventRoot, sessionId);
@@ -149,7 +151,7 @@ async function main() {
149
151
  const payload = buildSessionBootPayload(eventRoot, source, {
150
152
  hookEvent: HOOK_EVENT,
151
153
  sessionId: event.sessionId || process.env.GROK_SESSION_ID || null,
152
- ...(intent ? { intent } : {}),
154
+ ...(intent ? { intent, cardText: intent } : {}),
153
155
  ...(matchedSignals.length ? { matchedSignals } : {}),
154
156
  });
155
157
 
@@ -167,6 +169,9 @@ async function main() {
167
169
 
168
170
  try {
169
171
  scheduleAutonomousReportingMarker(eventRoot);
172
+ if (HOOK_EVENT === 'pre_compact' || HOOK_EVENT === 'post_compact') {
173
+ maintainLens(eventRoot);
174
+ }
170
175
  if (HOOK_EVENT === 'session_start') {
171
176
  maybeRunReflectionStub(eventRoot);
172
177
  runInferenceImprovementLight(eventRoot);