0xray 3.5.1 → 3.5.2
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.
- package/.gitignore.default +8 -0
- package/.opencode/hooks/post-commit +7 -27
- package/.xray/features.json +6 -0
- package/.xray/features.schema.json +17 -0
- package/AGENTS-consumer.md +1 -1
- package/AGENTS.md +1 -1
- package/README.md +1 -1
- package/SKILLS.md +1 -1
- package/dist/.gitignore.default +8 -0
- package/dist/AGENTS.md +1 -1
- package/dist/CHANGELOG.md +16 -0
- package/dist/README.md +1 -1
- package/dist/SKILLS.md +1 -1
- package/dist/core/bridge.mjs +10 -32
- package/dist/core/features-config.d.ts +16 -0
- package/dist/core/features-config.js +24 -0
- package/dist/integrations/grok/hooks/grok-hook-utils.js +2 -2
- package/dist/integrations/grok/hooks/post-tool-use.js +1 -1
- package/dist/integrations/grok/hooks/pre-tool-use.js +23 -32
- package/dist/integrations/grok/hooks/session-start.js +20 -1
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +2 -1
- package/dist/integrations/hermes-agent/bridge.mjs +22 -50
- package/dist/integrations/hooks/synthesis-hook-runtime.mjs +50 -0
- package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +3 -0
- package/dist/mcps/orchestrator/handlers/complexity-handler.js +25 -10
- package/dist/mcps/orchestrator/server.js +124 -4
- package/dist/memory-routing/types.d.ts +7 -0
- package/dist/nucleus/autonomy-kernel.d.ts +3 -0
- package/dist/nucleus/autonomy-kernel.js +40 -0
- package/dist/nucleus/delegation-gate.d.ts +6 -4
- package/dist/nucleus/delegation-gate.js +77 -5
- package/dist/nucleus/lead-dev-plan-persistence.d.ts +3 -0
- package/dist/nucleus/lead-dev-plan-persistence.js +44 -1
- package/dist/nucleus/synthesis-completion.d.ts +6 -0
- package/dist/nucleus/synthesis-completion.js +29 -0
- package/dist/nucleus/synthesis-context.d.ts +31 -0
- package/dist/nucleus/synthesis-context.js +86 -0
- package/dist/nucleus/synthesis.d.ts +54 -0
- package/dist/nucleus/synthesis.js +196 -0
- package/dist/plugin/xray-codex-injection.js +17 -0
- package/package.json +7 -3
- package/scripts/helpers/find-project-root.mjs +92 -0
- package/scripts/hooks/install-hooks.cjs +136 -36
- package/scripts/hooks/run-hook.js +46 -9
- package/scripts/mjs/delegation-gate-fixture.mjs +37 -0
- package/scripts/node/auto-reflection-generator.mjs +7 -0
- package/scripts/node/consumer-gitignore.cjs +101 -0
- package/scripts/node/load-reflection-config.mjs +77 -0
- package/scripts/node/postinstall.cjs +9 -6
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +2 -1
- package/src/mcps/orchestrator/handlers/complexity-handler.ts +33 -8
- package/src/mcps/orchestrator/server.ts +181 -5
- package/xray/features.json +6 -0
- package/xray/features.schema.json +17 -0
package/.gitignore.default
CHANGED
|
@@ -20,6 +20,13 @@ credentials.json
|
|
|
20
20
|
*.pem
|
|
21
21
|
*.key
|
|
22
22
|
|
|
23
|
+
# 0xray suit artifacts (postinstall / bridge install)
|
|
24
|
+
.grok/
|
|
25
|
+
.mcp.json
|
|
26
|
+
opencode.json
|
|
27
|
+
AGENTS.md
|
|
28
|
+
SKILLS.md
|
|
29
|
+
|
|
23
30
|
# Runtime state (xray inference workflows, session state)
|
|
24
31
|
.xray/
|
|
25
32
|
.strray/
|
|
@@ -41,6 +48,7 @@ xray/inference/
|
|
|
41
48
|
|
|
42
49
|
# Logs
|
|
43
50
|
logs/
|
|
51
|
+
logs/framework/
|
|
44
52
|
*.log
|
|
45
53
|
|
|
46
54
|
# OS files
|
|
@@ -20,39 +20,19 @@ BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
|
20
20
|
fi
|
|
21
21
|
) &
|
|
22
22
|
|
|
23
|
-
# Load
|
|
24
|
-
CONFIG_FILE="$PROJECT_ROOT/.xray/features.json"
|
|
23
|
+
# Load reflection config — synthesis.reflection SSOT, fallback auto_reflection
|
|
25
24
|
MODE="minimal"
|
|
26
25
|
COMMIT_THRESHOLD=25
|
|
27
26
|
DAYS_THRESHOLD=14
|
|
28
27
|
AUTO_GENERATE=true
|
|
29
28
|
PROMPT_USER=true
|
|
30
29
|
|
|
31
|
-
if
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
full)
|
|
38
|
-
COMMIT_THRESHOLD=10
|
|
39
|
-
DAYS_THRESHOLD=5
|
|
40
|
-
AUTO_GENERATE=true
|
|
41
|
-
PROMPT_USER=true
|
|
42
|
-
;;
|
|
43
|
-
minimal)
|
|
44
|
-
COMMIT_THRESHOLD=25
|
|
45
|
-
DAYS_THRESHOLD=14
|
|
46
|
-
AUTO_GENERATE=true
|
|
47
|
-
PROMPT_USER=true
|
|
48
|
-
;;
|
|
49
|
-
off)
|
|
50
|
-
COMMIT_THRESHOLD=999
|
|
51
|
-
DAYS_THRESHOLD=365
|
|
52
|
-
AUTO_GENERATE=false
|
|
53
|
-
PROMPT_USER=false
|
|
54
|
-
;;
|
|
55
|
-
esac
|
|
30
|
+
if command -v node >/dev/null 2>&1; then
|
|
31
|
+
LOAD_SCRIPT="$PROJECT_ROOT/scripts/node/load-reflection-config.mjs"
|
|
32
|
+
if [ -f "$LOAD_SCRIPT" ]; then
|
|
33
|
+
export PROJECT_ROOT="$PROJECT_ROOT"
|
|
34
|
+
eval "$(node "$LOAD_SCRIPT")"
|
|
35
|
+
fi
|
|
56
36
|
fi
|
|
57
37
|
|
|
58
38
|
# Check if this was a significant commit - prompt for reflection
|
package/.xray/features.json
CHANGED
|
@@ -141,6 +141,12 @@
|
|
|
141
141
|
"module_path": "",
|
|
142
142
|
"config": {}
|
|
143
143
|
},
|
|
144
|
+
"synthesis": {
|
|
145
|
+
"enabled": false,
|
|
146
|
+
"every_n_gates": 12,
|
|
147
|
+
"every_n_turns": 0,
|
|
148
|
+
"every_n_todos_completed": 0
|
|
149
|
+
},
|
|
144
150
|
"inference_governance": {
|
|
145
151
|
"enabled": false,
|
|
146
152
|
"request_timeout_ms": 10000,
|
|
@@ -27,6 +27,23 @@
|
|
|
27
27
|
"then": {
|
|
28
28
|
"required": ["module_path"]
|
|
29
29
|
}
|
|
30
|
+
},
|
|
31
|
+
"synthesis": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"enabled": { "type": "boolean" },
|
|
35
|
+
"every_n_gates": { "type": "integer", "minimum": 0 },
|
|
36
|
+
"every_n_turns": { "type": "integer", "minimum": 0 },
|
|
37
|
+
"every_n_todos_completed": { "type": "integer", "minimum": 0 },
|
|
38
|
+
"reflection": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"properties": {
|
|
41
|
+
"mode": { "type": "string", "enum": ["full", "minimal", "off"] },
|
|
42
|
+
"triggers": { "type": "object" },
|
|
43
|
+
"thresholds": { "type": "object" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
30
47
|
}
|
|
31
48
|
},
|
|
32
49
|
"additionalProperties": true
|
package/AGENTS-consumer.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Quick reference for the 0xRay AI orchestration framework (shipped to your project on `npm install 0xray`).
|
|
6
6
|
|
|
7
|
-
**v3.5.
|
|
7
|
+
**v3.5.2** — 7 MCP servers · 45 skills · 68 codex terms · 4 platform bridges
|
|
8
8
|
|
|
9
9
|
## Postinstall (automatic)
|
|
10
10
|
|
package/AGENTS.md
CHANGED
package/README.md
CHANGED
package/SKILLS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0xray Skills
|
|
2
2
|
|
|
3
|
-
**v3.5.
|
|
3
|
+
**v3.5.2** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
|
|
4
4
|
|
|
5
5
|
xray ships 45 knowledge skills (`src/skills/<name>/SKILL.md`) across the full software lifecycle. Lead-dev operating model lives in **`orchestrator`** skill + `multi_agent_orchestration.lead_dev_mode` in features.json. Skills load on demand via the **xray-skills** MCP server (`npx -y 0xray mcp skills`), agent `@mentions`, or platform skill directories after postinstall.
|
|
6
6
|
|
package/dist/.gitignore.default
CHANGED
|
@@ -20,6 +20,13 @@ credentials.json
|
|
|
20
20
|
*.pem
|
|
21
21
|
*.key
|
|
22
22
|
|
|
23
|
+
# 0xray suit artifacts (postinstall / bridge install)
|
|
24
|
+
.grok/
|
|
25
|
+
.mcp.json
|
|
26
|
+
opencode.json
|
|
27
|
+
AGENTS.md
|
|
28
|
+
SKILLS.md
|
|
29
|
+
|
|
23
30
|
# Runtime state (xray inference workflows, session state)
|
|
24
31
|
.xray/
|
|
25
32
|
.strray/
|
|
@@ -41,6 +48,7 @@ xray/inference/
|
|
|
41
48
|
|
|
42
49
|
# Logs
|
|
43
50
|
logs/
|
|
51
|
+
logs/framework/
|
|
44
52
|
*.log
|
|
45
53
|
|
|
46
54
|
# OS files
|
package/dist/AGENTS.md
CHANGED
package/dist/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Conventional Commits](https://www.conventionalcommits.org/).
|
|
6
6
|
|
|
7
|
+
## [3.5.2] - 2026-06-19
|
|
8
|
+
|
|
9
|
+
### 🐛 Bug Fixes
|
|
10
|
+
- **Grok synthesis gate** — `pre-tool-use.js` wires `evaluatePreToolGate` SSOT (synthesis deny, gate slices, `govern-and-apply` block); fail-closed on hook errors
|
|
11
|
+
- **Consumer bridge root** — `findProjectRoot` git-root + devDependency detection; `findGitRoot` applies `normalizePath`
|
|
12
|
+
- **Consumer hooks** — stale xray pre-commit reinstall; post-commit hook installed; pre-commit fails closed when runner missing; non-xray hooks backed up before overwrite
|
|
13
|
+
- **Consumer `.gitignore`** — idempotent suit block merge on postinstall; preserves orphan tail on incomplete marker
|
|
14
|
+
- **`bridge.mjs`** — `handleGetConfig` fixes undeclared `envDir` ReferenceError
|
|
15
|
+
- **Post-commit reflection** — `load-reflection-config.mjs` mode whitelist + `--json` output (no shell `eval`); macOS/BSD `stat` support
|
|
16
|
+
|
|
17
|
+
### ✅ Tests
|
|
18
|
+
- `verify-grok-synthesis-gate.mjs` (4/4 live Grok hook); chained in `verify:grok-delegation-gate` and `verify:delegation-gate`
|
|
19
|
+
- `verify-consumer-gitignore` incomplete-marker fixture; `verify:find-project-root` 3/3
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
7
23
|
## [3.5.1] - 2026-06-19
|
|
8
24
|
|
|
9
25
|
### ✨ Features
|
package/dist/README.md
CHANGED
package/dist/SKILLS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0xray Skills
|
|
2
2
|
|
|
3
|
-
**v3.5.
|
|
3
|
+
**v3.5.2** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
|
|
4
4
|
|
|
5
5
|
xray ships 45 knowledge skills (`src/skills/<name>/SKILL.md`) across the full software lifecycle. Lead-dev operating model lives in **`orchestrator`** skill + `multi_agent_orchestration.lead_dev_mode` in features.json. Skills load on demand via the **xray-skills** MCP server (`npx -y 0xray mcp skills`), agent `@mentions`, or platform skill directories after postinstall.
|
|
6
6
|
|
package/dist/core/bridge.mjs
CHANGED
|
@@ -51,6 +51,7 @@ import { join, dirname, resolve, relative } from "path";
|
|
|
51
51
|
import { fileURLToPath } from "url";
|
|
52
52
|
import { homedir } from "os";
|
|
53
53
|
import { createServer } from "http";
|
|
54
|
+
import { findProjectRoot } from "../../scripts/helpers/find-project-root.mjs";
|
|
54
55
|
|
|
55
56
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
56
57
|
|
|
@@ -63,35 +64,11 @@ let CodexFormatterModule = null;
|
|
|
63
64
|
let frameworkReady = false;
|
|
64
65
|
let frameworkLoadAttempted = false;
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const envHome = process.env.XRAY_HOME;
|
|
72
|
-
if (envHome && existsSync(join(envHome, "package.json"))) return envHome;
|
|
73
|
-
|
|
74
|
-
const candidates = [
|
|
75
|
-
process.cwd(),
|
|
76
|
-
join(homedir(), "dev", "xray"),
|
|
77
|
-
join(dirname(__dirname), "..", ".."), // core/ -> project root
|
|
78
|
-
join(dirname(__dirname), "..", "..", ".."), // integrations/ -> project root
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
for (const dir of candidates) {
|
|
82
|
-
try {
|
|
83
|
-
const pkgPath = join(dir, "package.json");
|
|
84
|
-
if (existsSync(pkgPath)) {
|
|
85
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
86
|
-
if (pkg.name === "0xray" || pkg.dependencies?.["0xray"]) {
|
|
87
|
-
return dir;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return process.cwd();
|
|
67
|
+
function resolveBridgeProjectRoot() {
|
|
68
|
+
return findProjectRoot([
|
|
69
|
+
join(dirname(__dirname), "..", ".."),
|
|
70
|
+
join(dirname(__dirname), "..", "..", ".."),
|
|
71
|
+
]);
|
|
95
72
|
}
|
|
96
73
|
|
|
97
74
|
// ============================================================================
|
|
@@ -184,7 +161,7 @@ const BRIDGE_CODEX_FALLBACK = {
|
|
|
184
161
|
// ============================================================================
|
|
185
162
|
|
|
186
163
|
async function handleHealth(input) {
|
|
187
|
-
const projectRoot =
|
|
164
|
+
const projectRoot = resolveBridgeProjectRoot();
|
|
188
165
|
const loaded = await loadFramework(projectRoot);
|
|
189
166
|
|
|
190
167
|
const pkgPath = join(projectRoot, "package.json");
|
|
@@ -313,6 +290,7 @@ async function handleGetCodexPrompt(input, projectRoot, logDir) {
|
|
|
313
290
|
|
|
314
291
|
async function handleGetConfig(input, projectRoot, logDir) {
|
|
315
292
|
logToActivity(logDir, "get-config");
|
|
293
|
+
const envDir = process.env.XRAY_CONFIG_DIR;
|
|
316
294
|
|
|
317
295
|
const result = {
|
|
318
296
|
status: "ok",
|
|
@@ -374,7 +352,7 @@ async function handleStats() {
|
|
|
374
352
|
codexFormatterAvailable: !!CodexFormatterModule,
|
|
375
353
|
qualityGateAvailable: !!QualityGateModule,
|
|
376
354
|
nodeVersion: process.version,
|
|
377
|
-
projectRoot:
|
|
355
|
+
projectRoot: resolveBridgeProjectRoot(),
|
|
378
356
|
};
|
|
379
357
|
}
|
|
380
358
|
|
|
@@ -893,7 +871,7 @@ async function main() {
|
|
|
893
871
|
}
|
|
894
872
|
}
|
|
895
873
|
|
|
896
|
-
const projectRoot =
|
|
874
|
+
const projectRoot = resolveBridgeProjectRoot();
|
|
897
875
|
const logDir = ensureLogDir(projectRoot);
|
|
898
876
|
|
|
899
877
|
// Lazy-load framework
|
|
@@ -66,6 +66,21 @@ export interface MultiAgentOrchestrationConfig {
|
|
|
66
66
|
/** 3.5.0 — PreToolUse gate after delegate-deferred (default true when lead_dev_mode) */
|
|
67
67
|
auto_chain_delegations?: boolean;
|
|
68
68
|
}
|
|
69
|
+
/** Reflection triggers merged from legacy auto_reflection when synthesis is configured */
|
|
70
|
+
export interface SynthesisReflectionConfig {
|
|
71
|
+
mode?: AutoReflectionConfig['mode'];
|
|
72
|
+
triggers?: AutoReflectionConfig['triggers'];
|
|
73
|
+
thresholds?: AutoReflectionConfig['thresholds'];
|
|
74
|
+
}
|
|
75
|
+
/** 3.6.0 — periodic reflect & realign checkpoint (Synthesis PR1) */
|
|
76
|
+
export interface SynthesisConfig {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
every_n_gates: number;
|
|
79
|
+
every_n_turns: number;
|
|
80
|
+
every_n_todos_completed: number;
|
|
81
|
+
/** SSOT for reflection triggers when synthesis enabled; seeded from auto_reflection */
|
|
82
|
+
reflection?: SynthesisReflectionConfig;
|
|
83
|
+
}
|
|
69
84
|
export interface AutonomousReportingConfig {
|
|
70
85
|
enabled: boolean;
|
|
71
86
|
interval_minutes: number;
|
|
@@ -369,6 +384,7 @@ export interface FeaturesConfig {
|
|
|
369
384
|
auto_reflection?: AutoReflectionConfig;
|
|
370
385
|
inference?: InferenceConfig;
|
|
371
386
|
memory_routing?: MemoryRoutingConfig;
|
|
387
|
+
synthesis: SynthesisConfig;
|
|
372
388
|
kernel?: KernelConfig;
|
|
373
389
|
processors?: ProcessorsConfig;
|
|
374
390
|
enforcement?: EnforcementConfig;
|
|
@@ -278,6 +278,24 @@ export class FeaturesConfigLoader {
|
|
|
278
278
|
...defaults.caching,
|
|
279
279
|
...configData.caching,
|
|
280
280
|
},
|
|
281
|
+
synthesis: (() => {
|
|
282
|
+
const merged = {
|
|
283
|
+
...defaults.synthesis,
|
|
284
|
+
...configData.synthesis,
|
|
285
|
+
};
|
|
286
|
+
const reflection = configData.synthesis?.reflection ??
|
|
287
|
+
(configData.auto_reflection
|
|
288
|
+
? {
|
|
289
|
+
mode: configData.auto_reflection.mode,
|
|
290
|
+
triggers: configData.auto_reflection.triggers,
|
|
291
|
+
thresholds: configData.auto_reflection.thresholds,
|
|
292
|
+
}
|
|
293
|
+
: undefined);
|
|
294
|
+
if (reflection) {
|
|
295
|
+
merged.reflection = reflection;
|
|
296
|
+
}
|
|
297
|
+
return merged;
|
|
298
|
+
})(),
|
|
281
299
|
};
|
|
282
300
|
}
|
|
283
301
|
/**
|
|
@@ -391,6 +409,12 @@ export class FeaturesConfigLoader {
|
|
|
391
409
|
cache_ttl_seconds: 300,
|
|
392
410
|
max_cache_size_mb: 50,
|
|
393
411
|
},
|
|
412
|
+
synthesis: {
|
|
413
|
+
enabled: false,
|
|
414
|
+
every_n_gates: 12,
|
|
415
|
+
every_n_turns: 0,
|
|
416
|
+
every_n_todos_completed: 0,
|
|
417
|
+
},
|
|
394
418
|
};
|
|
395
419
|
}
|
|
396
420
|
/**
|
|
@@ -105,8 +105,8 @@ export function resolveCodexPath(root = workspaceRoot()) {
|
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
export function loadFeatures() {
|
|
109
|
-
const featuresPath = resolveFeaturesPath();
|
|
108
|
+
export function loadFeatures(root = workspaceRoot()) {
|
|
109
|
+
const featuresPath = resolveFeaturesPath(root);
|
|
110
110
|
if (!featuresPath) return { lead_dev_mode: true, no_new_surface: true, per_suite_triage: true };
|
|
111
111
|
try {
|
|
112
112
|
const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
|
|
@@ -36,7 +36,7 @@ async function main() {
|
|
|
36
36
|
if (isSubagentTool(toolName)) {
|
|
37
37
|
const spawnCheck = validateSpawnMatchesTodo(toolInput, eventRoot);
|
|
38
38
|
if (spawnCheck.valid && spawnCheck.expectedTodoId) {
|
|
39
|
-
updatePlanTodoStatusInPlace(spawnCheck.expectedTodoId, '
|
|
39
|
+
updatePlanTodoStatusInPlace(spawnCheck.expectedTodoId, 'completed', eventRoot);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const result = satisfyDelegationsFromToolInput(toolInput, eventRoot);
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* Contract: stdin JSON → stdout {"decision":"allow"} | {"decision":"deny","reason":"..."}
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
evaluatePreToolGate,
|
|
9
|
+
loadDelegationGateFeatures,
|
|
10
|
+
} from '../../hooks/delegation-gate-runtime.mjs';
|
|
7
11
|
import {
|
|
8
12
|
checkCodexPatterns,
|
|
9
13
|
checkFullTestSuite,
|
|
10
|
-
checkPendingDelegationGate,
|
|
11
|
-
checkSubagentGate,
|
|
12
14
|
checkSurfaceArea,
|
|
13
15
|
ensureSessionBoot,
|
|
14
16
|
isShellTool,
|
|
@@ -43,45 +45,27 @@ async function main() {
|
|
|
43
45
|
const eventRoot = event.workspaceRoot || event.cwd || root;
|
|
44
46
|
ensureSessionBoot(eventRoot, '0xray/grok-pre-tool-use-boot');
|
|
45
47
|
|
|
46
|
-
const features = loadFeatures();
|
|
48
|
+
const features = loadFeatures(eventRoot);
|
|
49
|
+
const gateFeatures = loadDelegationGateFeatures(eventRoot);
|
|
47
50
|
const ctx = extractFromEvent(event);
|
|
48
51
|
toolName = ctx.toolName;
|
|
49
52
|
const { paths, content, cmd, toolInput } = ctx;
|
|
50
53
|
const sessionId = resolveSessionId(event);
|
|
51
54
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
toolInput,
|
|
55
|
-
features,
|
|
56
|
-
eventRoot,
|
|
57
|
-
sessionId,
|
|
58
|
-
);
|
|
59
|
-
if (delegationBlock) {
|
|
60
|
-
finish(
|
|
61
|
-
eventRoot,
|
|
62
|
-
'deny',
|
|
63
|
-
delegationBlock.reason,
|
|
64
|
-
delegationBlock.hint,
|
|
65
|
-
toolName,
|
|
66
|
-
{ gate: delegationBlock.gate },
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const subagentBlock = checkSubagentGate(
|
|
71
|
-
toolName,
|
|
72
|
-
features,
|
|
73
|
-
eventRoot,
|
|
55
|
+
const gateBlock = evaluatePreToolGate(toolName, toolInput, {
|
|
56
|
+
projectRoot: eventRoot,
|
|
74
57
|
sessionId,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
58
|
+
features: gateFeatures,
|
|
59
|
+
host: 'grok',
|
|
60
|
+
});
|
|
61
|
+
if (!gateBlock.allow) {
|
|
78
62
|
finish(
|
|
79
63
|
eventRoot,
|
|
80
64
|
'deny',
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
gateBlock.reason,
|
|
66
|
+
gateBlock.hint,
|
|
83
67
|
toolName,
|
|
84
|
-
{ gate:
|
|
68
|
+
{ gate: gateBlock.gate },
|
|
85
69
|
);
|
|
86
70
|
}
|
|
87
71
|
|
|
@@ -110,7 +94,14 @@ async function main() {
|
|
|
110
94
|
tool: toolName,
|
|
111
95
|
error: err.message,
|
|
112
96
|
});
|
|
113
|
-
finish(
|
|
97
|
+
finish(
|
|
98
|
+
root,
|
|
99
|
+
'deny',
|
|
100
|
+
`PreToolUse hook error — blocked for safety: ${err.message}`,
|
|
101
|
+
null,
|
|
102
|
+
toolName,
|
|
103
|
+
{ gate: 'hook-error' },
|
|
104
|
+
);
|
|
114
105
|
}
|
|
115
106
|
}
|
|
116
107
|
|
|
@@ -14,15 +14,34 @@ import {
|
|
|
14
14
|
writeSessionBoot,
|
|
15
15
|
} from './grok-hook-utils.js';
|
|
16
16
|
import { appendHookActivity } from './grok-hook-activity.js';
|
|
17
|
+
import { recordSynthesisTurnSlice } from '../../hooks/synthesis-hook-runtime.mjs';
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
function resolveHookEvent(event) {
|
|
20
|
+
if (process.env.GROK_HOOK_EVENT) return process.env.GROK_HOOK_EVENT;
|
|
21
|
+
const flag = process.argv.find((a) => a.startsWith('--hook-event='));
|
|
22
|
+
if (flag) return flag.slice('--hook-event='.length);
|
|
23
|
+
if (
|
|
24
|
+
event.prompt != null ||
|
|
25
|
+
event.userMessage != null ||
|
|
26
|
+
event.user_prompt != null ||
|
|
27
|
+
event.hook === 'UserPromptSubmit'
|
|
28
|
+
) {
|
|
29
|
+
return 'user_prompt_submit';
|
|
30
|
+
}
|
|
31
|
+
return 'session_start';
|
|
32
|
+
}
|
|
19
33
|
|
|
20
34
|
async function main() {
|
|
21
35
|
const root = workspaceRoot();
|
|
36
|
+
let HOOK_EVENT = 'session_start';
|
|
22
37
|
try {
|
|
23
38
|
const event = await readStdinJson();
|
|
39
|
+
HOOK_EVENT = resolveHookEvent(event);
|
|
24
40
|
const eventRoot = event.workspaceRoot || event.cwd || root;
|
|
25
41
|
const sessionId = resolveSessionId(event);
|
|
42
|
+
if (HOOK_EVENT === 'user_prompt_submit' && sessionId) {
|
|
43
|
+
recordSynthesisTurnSlice(eventRoot, sessionId);
|
|
44
|
+
}
|
|
26
45
|
if (clearPendingDelegationsForSessionChange(sessionId, eventRoot)) {
|
|
27
46
|
appendHookActivity(eventRoot, 'grok-session-start', 'stale-pending-cleared', 'info', {
|
|
28
47
|
sessionId,
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"type": "command",
|
|
37
37
|
"command": "node",
|
|
38
38
|
"args": [
|
|
39
|
-
"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js"
|
|
39
|
+
"${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js",
|
|
40
|
+
"--hook-event=user_prompt_submit"
|
|
40
41
|
],
|
|
41
42
|
"timeout": 15000
|
|
42
43
|
}
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
import { join, dirname, relative } from "path";
|
|
40
40
|
import { fileURLToPath } from "url";
|
|
41
41
|
import { homedir } from "os";
|
|
42
|
+
import { findProjectRoot } from "../../../scripts/helpers/find-project-root.mjs";
|
|
42
43
|
|
|
43
44
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
44
45
|
|
|
@@ -52,62 +53,19 @@ let frameworkReady = false;
|
|
|
52
53
|
let frameworkLoadAttempted = false;
|
|
53
54
|
|
|
54
55
|
// ── Project root detection ───────────────────────────────────
|
|
55
|
-
function
|
|
56
|
-
return (
|
|
57
|
-
existsSync(join(dir, ".xray", "features.json")) ||
|
|
58
|
-
existsSync(join(dir, "node_modules", "0xray", "package.json"))
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function findProjectRoot() {
|
|
63
|
-
const envHome = process.env.XRAY_HOME || process.env.XRAY_ROOT;
|
|
64
|
-
if (envHome) {
|
|
65
|
-
const resolved = join(envHome);
|
|
66
|
-
if (existsSync(join(resolved, "package.json")) || isConsumerWorkspace(resolved)) {
|
|
67
|
-
return resolved;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
56
|
+
function resolveBridgeProjectRoot() {
|
|
71
57
|
const consumerMarker = join(__dirname, "xray-consumer-root.txt");
|
|
72
58
|
if (existsSync(consumerMarker)) {
|
|
73
59
|
try {
|
|
74
60
|
const marked = readFileSync(consumerMarker, "utf-8").trim();
|
|
75
|
-
if (marked &&
|
|
61
|
+
if (marked && existsSync(join(marked, "package.json"))) {
|
|
76
62
|
return marked;
|
|
77
63
|
}
|
|
78
64
|
} catch {
|
|
79
|
-
// fall through
|
|
65
|
+
// fall through to shared SSOT
|
|
80
66
|
}
|
|
81
67
|
}
|
|
82
|
-
|
|
83
|
-
// Prefer consumer workspace (--cwd / process.chdir) over framework package root
|
|
84
|
-
let dir = process.cwd();
|
|
85
|
-
for (let i = 0; i < 12; i++) {
|
|
86
|
-
if (isConsumerWorkspace(dir)) return dir;
|
|
87
|
-
const parent = dirname(dir);
|
|
88
|
-
if (parent === dir) break;
|
|
89
|
-
dir = parent;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const candidates = [
|
|
93
|
-
join(homedir(), "dev", "xray"),
|
|
94
|
-
join(dirname(__dirname), "..", "..", "..", ".."), // plugin dir -> package root
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
for (const candidate of candidates) {
|
|
98
|
-
try {
|
|
99
|
-
const pkgPath = join(candidate, "package.json");
|
|
100
|
-
if (existsSync(pkgPath)) {
|
|
101
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
102
|
-
if (pkg.name === "0xray" || pkg.dependencies?.["0xray"]) {
|
|
103
|
-
return candidate;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
} catch {
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return process.cwd();
|
|
68
|
+
return findProjectRoot([join(dirname(__dirname), "..", "..", "..", "..")]);
|
|
111
69
|
}
|
|
112
70
|
|
|
113
71
|
// ── Log directory ────────────────────────────────────────────
|
|
@@ -403,7 +361,7 @@ async function runProcessors(tool, args, phase, projectRoot, logDir) {
|
|
|
403
361
|
// ── Command handlers ─────────────────────────────────────────
|
|
404
362
|
|
|
405
363
|
async function handleHealth(input) {
|
|
406
|
-
const projectRoot =
|
|
364
|
+
const projectRoot = resolveBridgeProjectRoot();
|
|
407
365
|
const loaded = await loadFramework(projectRoot);
|
|
408
366
|
|
|
409
367
|
const components = {
|
|
@@ -760,7 +718,7 @@ function handleStats() {
|
|
|
760
718
|
qualityGateAvailable: !!runQualityGateWithLogging,
|
|
761
719
|
processorsAvailable: !!ProcessorManager,
|
|
762
720
|
nodeVersion: process.version,
|
|
763
|
-
projectRoot:
|
|
721
|
+
projectRoot: resolveBridgeProjectRoot(),
|
|
764
722
|
};
|
|
765
723
|
}
|
|
766
724
|
|
|
@@ -811,6 +769,16 @@ const KNOWN_COMMANDS = new Set([
|
|
|
811
769
|
"skill-install", "skill-registry", "delegation-gate",
|
|
812
770
|
]);
|
|
813
771
|
|
|
772
|
+
async function recordHermesSynthesisTurn(projectRoot, sessionId) {
|
|
773
|
+
if (!sessionId) return;
|
|
774
|
+
try {
|
|
775
|
+
const { recordSynthesisTurnSlice } = await import("../hooks/synthesis-hook-runtime.mjs");
|
|
776
|
+
recordSynthesisTurnSlice(projectRoot, sessionId);
|
|
777
|
+
} catch {
|
|
778
|
+
/* optional */
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
814
782
|
async function handleDelegationGate(command, projectRoot, logDir) {
|
|
815
783
|
try {
|
|
816
784
|
const gate = await import("../hooks/delegation-gate-runtime.mjs");
|
|
@@ -834,6 +802,10 @@ async function handleDelegationGate(command, projectRoot, logDir) {
|
|
|
834
802
|
return { allow: true, phase: "post", ...result };
|
|
835
803
|
}
|
|
836
804
|
|
|
805
|
+
if (command.recordTurn === true || command.event === 'user_prompt_submit') {
|
|
806
|
+
await recordHermesSynthesisTurn(projectRoot, sessionId);
|
|
807
|
+
}
|
|
808
|
+
|
|
837
809
|
const features = gate.loadDelegationGateFeatures(projectRoot);
|
|
838
810
|
const outcome = gate.evaluatePreToolGate(tool, args, {
|
|
839
811
|
projectRoot,
|
|
@@ -935,7 +907,7 @@ async function main() {
|
|
|
935
907
|
}
|
|
936
908
|
}
|
|
937
909
|
|
|
938
|
-
const projectRoot =
|
|
910
|
+
const projectRoot = resolveBridgeProjectRoot();
|
|
939
911
|
const logDir = ensureLogDir(projectRoot);
|
|
940
912
|
|
|
941
913
|
// Log session start for test verification
|