0xray 3.4.9 → 3.5.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 (36) hide show
  1. package/.xray/features.json +2 -1
  2. package/AGENTS-consumer.md +1 -1
  3. package/AGENTS.md +1 -1
  4. package/README.md +1 -1
  5. package/SKILLS.md +1 -1
  6. package/dist/AGENTS.md +1 -1
  7. package/dist/CHANGELOG.md +32 -0
  8. package/dist/README.md +1 -1
  9. package/dist/SKILLS.md +1 -1
  10. package/dist/core/features-config.d.ts +2 -0
  11. package/dist/integrations/grok/hooks/grok-hook-utils.js +390 -16
  12. package/dist/integrations/grok/hooks/post-tool-use.js +62 -0
  13. package/dist/integrations/grok/hooks/pre-tool-use.js +43 -5
  14. package/dist/integrations/grok/hooks/session-start.js +8 -0
  15. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +15 -0
  16. package/dist/mcps/orchestrator/config/agent-capabilities.d.ts +1 -1
  17. package/dist/mcps/orchestrator/config/agent-capabilities.js +23 -1
  18. package/dist/mcps/orchestrator/execution/execution-planner.js +2 -2
  19. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +1 -0
  20. package/dist/mcps/orchestrator/handlers/complexity-handler.js +8 -1
  21. package/dist/mcps/orchestrator/handlers/task-handler.js +20 -3
  22. package/dist/nucleus/autonomy-kernel.d.ts +1 -1
  23. package/dist/nucleus/autonomy-kernel.js +5 -2
  24. package/dist/nucleus/lead-dev-plan-persistence.d.ts +43 -0
  25. package/dist/nucleus/lead-dev-plan-persistence.js +155 -0
  26. package/dist/nucleus/pending-delegations.d.ts +71 -0
  27. package/dist/nucleus/pending-delegations.js +285 -0
  28. package/package.json +6 -2
  29. package/scripts/mjs/verify-grok-delegation-gate.mjs +173 -0
  30. package/scripts/mjs/verify-orchestrator-behavior.mjs +168 -0
  31. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +15 -0
  32. package/src/mcps/orchestrator/config/agent-capabilities.ts +26 -0
  33. package/src/mcps/orchestrator/execution/execution-planner.ts +2 -0
  34. package/src/mcps/orchestrator/handlers/complexity-handler.ts +9 -1
  35. package/src/mcps/orchestrator/handlers/task-handler.ts +53 -3
  36. package/xray/features.json +2 -1
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Pending delegations SSOT — written by task-handler on delegate-deferred.
3
+ * Grok hooks read the same file; PostToolUse never re-parses MCP output.
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import { featuresConfigLoader } from '../core/features-config.js';
8
+ import { updatePlanTodoStatus } from './lead-dev-plan-persistence.js';
9
+ export const DEFAULT_PENDING_TTL_MS = 4 * 60 * 60 * 1000;
10
+ const AGENT_ALIASES = {
11
+ 'bug-triage-specialist': 'bug-triage',
12
+ 'code-reviewer': 'code-review',
13
+ 'security-auditor': 'security-audit',
14
+ };
15
+ const TASK_TYPE_BY_AGENT = {
16
+ 'backend-engineer': 'backend-engineer',
17
+ 'frontend-engineer': 'frontend-engineer',
18
+ 'bug-triage': 'bug-triage',
19
+ 'bug-triage-specialist': 'bug-triage',
20
+ strategist: 'strategist',
21
+ researcher: 'generalPurpose',
22
+ 'architect-tools': 'generalPurpose',
23
+ 'code-review': 'code-review',
24
+ 'code-reviewer': 'code-review',
25
+ };
26
+ export function pendingDelegationsPath(projectRoot = process.cwd()) {
27
+ return path.join(projectRoot, '.xray', 'state', 'pending-delegations.json');
28
+ }
29
+ export function leadDevPlanPath(projectRoot = process.cwd()) {
30
+ return path.join(projectRoot, '.xray', 'state', 'lead-dev-plan.json');
31
+ }
32
+ export function isAutoChainDelegationsEnabled(projectRoot = process.cwd()) {
33
+ try {
34
+ const localPath = path.join(projectRoot, '.xray', 'features.json');
35
+ if (fs.existsSync(localPath)) {
36
+ const data = JSON.parse(fs.readFileSync(localPath, 'utf8'));
37
+ const cfg = data.multi_agent_orchestration ?? {};
38
+ if (cfg.enabled === false || cfg.lead_dev_mode === false)
39
+ return false;
40
+ return cfg.auto_chain_delegations !== false;
41
+ }
42
+ const cfg = featuresConfigLoader.loadConfig().multi_agent_orchestration;
43
+ if (!cfg.enabled || cfg.lead_dev_mode === false)
44
+ return false;
45
+ return cfg.auto_chain_delegations !== false;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ function normalizeAgent(agent) {
52
+ const key = agent.toLowerCase().trim();
53
+ return AGENT_ALIASES[key] ?? key;
54
+ }
55
+ function loadLeadDevPlan(projectRoot) {
56
+ const planPath = leadDevPlanPath(projectRoot);
57
+ if (!fs.existsSync(planPath))
58
+ return null;
59
+ try {
60
+ return JSON.parse(fs.readFileSync(planPath, 'utf8'));
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ function allPlanTodos(plan) {
67
+ return plan.phases.flatMap((p) => p.todos);
68
+ }
69
+ export function matchPlanTodo(agent, taskDescription, taskId, plan) {
70
+ if (!plan) {
71
+ return { todo: null, method: 'none', confidence: 0 };
72
+ }
73
+ const normalizedAgent = normalizeAgent(agent);
74
+ const pendingTodos = allPlanTodos(plan).filter((t) => t.status === 'pending');
75
+ const byTaskId = pendingTodos.find((t) => t.id === taskId || t.task.includes(taskId));
76
+ if (byTaskId) {
77
+ return { todo: byTaskId, method: 'taskId', confidence: 0.95 };
78
+ }
79
+ const desc = taskDescription.toLowerCase().trim();
80
+ if (desc) {
81
+ const byDesc = pendingTodos.find((t) => {
82
+ const todoText = t.task.toLowerCase();
83
+ return todoText.includes(desc) || desc.includes(todoText.slice(0, 40));
84
+ });
85
+ if (byDesc) {
86
+ return { todo: byDesc, method: 'description', confidence: 0.75 };
87
+ }
88
+ }
89
+ const byAgent = pendingTodos.find((t) => normalizeAgent(t.subagent) === normalizedAgent);
90
+ if (byAgent) {
91
+ return { todo: byAgent, method: 'agent-only', confidence: 0.5 };
92
+ }
93
+ return { todo: null, method: 'none', confidence: 0 };
94
+ }
95
+ export function mapSubagentToTaskType(agent) {
96
+ const key = agent.toLowerCase().trim();
97
+ return TASK_TYPE_BY_AGENT[key] ?? TASK_TYPE_BY_AGENT[normalizeAgent(key)] ?? 'generalPurpose';
98
+ }
99
+ export function buildSpawnHint(delegation) {
100
+ const todoLine = delegation.planTodoTask
101
+ ? `[plan todo ${delegation.planTodoId}] ${delegation.planTodoTask}`
102
+ : delegation.taskDescription;
103
+ return {
104
+ tool: 'Task',
105
+ subagent_type: mapSubagentToTaskType(delegation.agent),
106
+ description: `Lead-dev implementation delegation. ${todoLine}. ` +
107
+ `Complete this todo per .xray/state/lead-dev-plan.json; run per-suite tests; report results.`,
108
+ planTodoId: delegation.planTodoId,
109
+ delegationId: delegation.id,
110
+ };
111
+ }
112
+ export function loadPendingDelegationsState(projectRoot = process.cwd()) {
113
+ const filePath = pendingDelegationsPath(projectRoot);
114
+ if (!fs.existsSync(filePath))
115
+ return null;
116
+ try {
117
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ }
123
+ export function savePendingDelegationsState(state, projectRoot = process.cwd()) {
124
+ const filePath = pendingDelegationsPath(projectRoot);
125
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
126
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2));
127
+ return filePath;
128
+ }
129
+ function newDelegationId() {
130
+ return `del-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
131
+ }
132
+ export function recordDeferredDelegations(sessionId, deferrals, projectRoot = process.cwd()) {
133
+ if (!isAutoChainDelegationsEnabled(projectRoot) || deferrals.length === 0) {
134
+ return [];
135
+ }
136
+ const plan = loadLeadDevPlan(projectRoot);
137
+ const existing = loadPendingDelegationsState(projectRoot);
138
+ const now = new Date().toISOString();
139
+ const base = existing && existing.sessionId === sessionId
140
+ ? existing
141
+ : {
142
+ sessionId,
143
+ createdAt: now,
144
+ ttlMs: DEFAULT_PENDING_TTL_MS,
145
+ delegations: [],
146
+ };
147
+ const created = [];
148
+ for (const d of deferrals) {
149
+ const { todo, method, confidence } = matchPlanTodo(d.agent, d.description, d.taskId, plan);
150
+ const partial = {
151
+ id: newDelegationId(),
152
+ taskId: d.taskId,
153
+ agent: d.agent,
154
+ taskDescription: d.description,
155
+ taskType: d.type ?? 'implement',
156
+ sessionId,
157
+ planTodoId: todo?.id ?? null,
158
+ planTodoTask: todo?.task ?? null,
159
+ matchMethod: method,
160
+ matchConfidence: confidence,
161
+ status: 'pending',
162
+ createdAt: now,
163
+ satisfiedAt: null,
164
+ };
165
+ created.push({
166
+ ...partial,
167
+ spawnHint: buildSpawnHint(partial),
168
+ });
169
+ }
170
+ base.delegations = [
171
+ ...base.delegations.filter((d) => d.status === 'pending'),
172
+ ...created,
173
+ ];
174
+ if (base.sessionId !== sessionId) {
175
+ base.sessionId = sessionId;
176
+ base.createdAt = now;
177
+ base.delegations = created;
178
+ }
179
+ savePendingDelegationsState(base, projectRoot);
180
+ return created;
181
+ }
182
+ export function isPendingStateActive(state, sessionId) {
183
+ if (!state)
184
+ return false;
185
+ if (!sessionId || state.sessionId !== sessionId)
186
+ return false;
187
+ const age = Date.now() - new Date(state.createdAt).getTime();
188
+ if (age > state.ttlMs)
189
+ return false;
190
+ return state.delegations.some((d) => d.status === 'pending');
191
+ }
192
+ export function getActivePendingDelegations(sessionId, projectRoot = process.cwd()) {
193
+ const state = loadPendingDelegationsState(projectRoot);
194
+ if (!isPendingStateActive(state, sessionId))
195
+ return [];
196
+ return state.delegations.filter((d) => d.status === 'pending');
197
+ }
198
+ export function satisfyDelegation(input, projectRoot = process.cwd()) {
199
+ const state = loadPendingDelegationsState(projectRoot);
200
+ if (!state)
201
+ return { satisfied: [], clearedAll: false };
202
+ const pending = state.delegations.filter((d) => d.status === 'pending');
203
+ if (pending.length === 0)
204
+ return { satisfied: [], clearedAll: false };
205
+ const prompt = (input.toolPrompt ?? '').toLowerCase();
206
+ let matches = [];
207
+ if (input.delegationId) {
208
+ matches = pending.filter((d) => d.id === input.delegationId);
209
+ }
210
+ else if (input.planTodoId) {
211
+ matches = pending.filter((d) => d.planTodoId === input.planTodoId || prompt.includes(input.planTodoId.toLowerCase()));
212
+ }
213
+ else if (input.taskId) {
214
+ matches = pending.filter((d) => d.taskId === input.taskId || prompt.includes(input.taskId.toLowerCase()));
215
+ }
216
+ else if (input.agent) {
217
+ const norm = normalizeAgent(input.agent);
218
+ matches = pending.filter((d) => normalizeAgent(d.agent) === norm);
219
+ }
220
+ if (matches.length === 0 && prompt) {
221
+ for (const d of pending) {
222
+ if (d.planTodoId && prompt.includes(d.planTodoId.toLowerCase())) {
223
+ matches.push(d);
224
+ break;
225
+ }
226
+ if (prompt.includes(d.taskId.toLowerCase())) {
227
+ matches.push(d);
228
+ break;
229
+ }
230
+ }
231
+ }
232
+ if (matches.length === 0 && pending.length === 1) {
233
+ const only = pending[0];
234
+ const hint = only.spawnHint.description.toLowerCase();
235
+ if (prompt && hint && prompt.includes(hint.slice(0, 40))) {
236
+ matches = [only];
237
+ }
238
+ }
239
+ if (matches.length === 0) {
240
+ return { satisfied: [], clearedAll: false };
241
+ }
242
+ const now = new Date().toISOString();
243
+ const matchIds = new Set(matches.map((m) => m.id));
244
+ for (const d of state.delegations) {
245
+ if (matchIds.has(d.id) && d.status === 'pending') {
246
+ d.status = 'satisfied';
247
+ d.satisfiedAt = now;
248
+ }
249
+ }
250
+ savePendingDelegationsState(state, projectRoot);
251
+ for (const m of matches) {
252
+ if (m.planTodoId) {
253
+ updatePlanTodoStatus(m.planTodoId, 'in_progress', projectRoot);
254
+ }
255
+ }
256
+ const stillPending = state.delegations.some((d) => d.status === 'pending');
257
+ return { satisfied: matches, clearedAll: !stillPending };
258
+ }
259
+ export function clearPendingDelegations(projectRoot = process.cwd()) {
260
+ const filePath = pendingDelegationsPath(projectRoot);
261
+ if (!fs.existsSync(filePath))
262
+ return false;
263
+ fs.unlinkSync(filePath);
264
+ return true;
265
+ }
266
+ export function clearPendingDelegationsForSessionChange(newSessionId, projectRoot = process.cwd()) {
267
+ const state = loadPendingDelegationsState(projectRoot);
268
+ if (!state)
269
+ return false;
270
+ if (newSessionId && state.sessionId === newSessionId)
271
+ return false;
272
+ fs.unlinkSync(pendingDelegationsPath(projectRoot));
273
+ return true;
274
+ }
275
+ export function delegationSummaryForResponse(delegations) {
276
+ return delegations.map((d) => ({
277
+ id: d.id,
278
+ taskId: d.taskId,
279
+ agent: d.agent,
280
+ planTodoId: d.planTodoId,
281
+ matchMethod: d.matchMethod,
282
+ matchConfidence: d.matchConfidence,
283
+ spawnHint: d.spawnHint,
284
+ }));
285
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "3.4.9",
3
+ "version": "3.5.0",
4
4
  "description": "Multi-agent orchestration and Codex governance for OpenCode, Hermes, Grok Build, and OpenClaw",
5
5
  "readme": "README.md",
6
6
  "license": "MIT",
@@ -125,7 +125,9 @@
125
125
  "pre-publish-guard": "node scripts/node/pre-publish-guard.js",
126
126
  "safe-publish": "npm run prepare-consumer && npm run build",
127
127
  "publish": "npm run release:gate && npm run safe-publish && npm publish --access public",
128
- "release:patch": "node scripts/node/release.mjs patch"
128
+ "release:patch": "node scripts/node/release.mjs patch",
129
+ "verify:orchestrator-behavior": "npm run build && node scripts/mjs/verify-orchestrator-behavior.mjs",
130
+ "verify:grok-delegation-gate": "npm run build && node scripts/mjs/verify-grok-delegation-gate.mjs"
129
131
  },
130
132
  "files": [
131
133
  "dist/",
@@ -137,6 +139,8 @@
137
139
  "scripts/mjs/test-mcp-functionality.mjs",
138
140
  "scripts/mjs/validate-mcp-connectivity.cjs",
139
141
  "scripts/mjs/validate-postinstall-config.mjs",
142
+ "scripts/mjs/verify-orchestrator-behavior.mjs",
143
+ "scripts/mjs/verify-grok-delegation-gate.mjs",
140
144
  "src/opencode/",
141
145
  "src/opencode/codex.codex",
142
146
  "src/opencode/commands/",
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hook fixture verify — PreToolUse delegation gate with seeded pending-delegations.json.
4
+ * Run: node scripts/mjs/verify-grok-delegation-gate.mjs
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
8
+ import { tmpdir } from 'node:os';
9
+ import { join, dirname } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const packageRoot = join(__dirname, '../..');
14
+ const preToolHook = join(packageRoot, 'dist/integrations/grok/hooks/pre-tool-use.js');
15
+
16
+ let failed = 0;
17
+ function pass(n) {
18
+ console.log(`✅ ${n}`);
19
+ }
20
+ function fail(n, d = '') {
21
+ failed++;
22
+ console.error(`❌ ${n}${d ? ` — ${d}` : ''}`);
23
+ }
24
+
25
+ function runHook(fixture, env = {}) {
26
+ const payload = JSON.stringify(fixture);
27
+ const out = execSync(`printf '%s' '${payload.replace(/'/g, "'\\''")}' | node "${preToolHook}"`, {
28
+ encoding: 'utf8',
29
+ env: { ...process.env, ...env },
30
+ });
31
+ return JSON.parse(out.trim());
32
+ }
33
+
34
+ const tmp = mkdtempSync(join(tmpdir(), 'xray-gate-verify-'));
35
+ mkdirSync(join(tmp, '.xray', 'state'), { recursive: true });
36
+ writeFileSync(
37
+ join(tmp, '.xray', 'features.json'),
38
+ JSON.stringify({
39
+ multi_agent_orchestration: {
40
+ enabled: true,
41
+ lead_dev_mode: true,
42
+ auto_chain_delegations: true,
43
+ },
44
+ }),
45
+ );
46
+ writeFileSync(
47
+ join(tmp, '.xray', 'state', 'lead-dev-plan.json'),
48
+ JSON.stringify({ active: true }),
49
+ );
50
+ writeFileSync(
51
+ join(tmp, '.xray', 'state', 'pending-delegations.json'),
52
+ JSON.stringify({
53
+ sessionId: 'verify-gate',
54
+ createdAt: new Date().toISOString(),
55
+ ttlMs: 14400000,
56
+ delegations: [
57
+ {
58
+ id: 'del-verify',
59
+ taskId: 'impl-1',
60
+ agent: 'backend-engineer',
61
+ taskDescription: 'implement',
62
+ taskType: 'implement',
63
+ sessionId: 'verify-gate',
64
+ planTodoId: '2.1',
65
+ planTodoTask: 'swap deps',
66
+ matchMethod: 'agent-only',
67
+ matchConfidence: 0.5,
68
+ status: 'pending',
69
+ createdAt: new Date().toISOString(),
70
+ satisfiedAt: null,
71
+ spawnHint: {
72
+ tool: 'Task',
73
+ subagent_type: 'backend-engineer',
74
+ description: 'Lead-dev delegation 2.1',
75
+ planTodoId: '2.1',
76
+ delegationId: 'del-verify',
77
+ },
78
+ },
79
+ ],
80
+ }),
81
+ );
82
+
83
+ const baseEnv = {
84
+ GROK_WORKSPACE_ROOT: tmp,
85
+ GROK_SESSION_ID: 'verify-gate',
86
+ };
87
+
88
+ try {
89
+ const deny = runHook(
90
+ {
91
+ toolName: 'search_replace',
92
+ workspaceRoot: tmp,
93
+ sessionId: 'verify-gate',
94
+ toolInput: { path: 'src/a.ts', new_string: 'x' },
95
+ },
96
+ baseEnv,
97
+ );
98
+ if (deny.decision === 'deny' && deny.gate === 'auto-chain-pending') {
99
+ pass('PreToolUse denies write while pending');
100
+ } else {
101
+ fail('write deny', JSON.stringify(deny));
102
+ }
103
+
104
+ const allowRead = runHook(
105
+ {
106
+ toolName: 'read_file',
107
+ workspaceRoot: tmp,
108
+ sessionId: 'verify-gate',
109
+ toolInput: { path: 'src/a.ts' },
110
+ },
111
+ baseEnv,
112
+ );
113
+ if (allowRead.decision === 'allow') pass('PreToolUse allows read while pending');
114
+ else fail('read allow', JSON.stringify(allowRead));
115
+
116
+ const allowTask = runHook(
117
+ {
118
+ toolName: 'Task',
119
+ workspaceRoot: tmp,
120
+ sessionId: 'verify-gate',
121
+ toolInput: { prompt: 'plan todo 2.1 swap deps', subagent_type: 'backend-engineer' },
122
+ },
123
+ baseEnv,
124
+ );
125
+ if (allowTask.decision === 'allow') pass('PreToolUse allows Task while pending');
126
+ else fail('Task allow', JSON.stringify(allowTask));
127
+
128
+ writeFileSync(
129
+ join(tmp, '.xray', 'state', 'lead-dev-plan.json'),
130
+ JSON.stringify({
131
+ active: true,
132
+ persistedAt: new Date().toISOString(),
133
+ phases: [
134
+ {
135
+ id: 'phase-2',
136
+ todos: [
137
+ {
138
+ id: '2.1',
139
+ task: 'swap deps',
140
+ subagent: 'backend-engineer',
141
+ status: 'pending',
142
+ },
143
+ ],
144
+ },
145
+ ],
146
+ }),
147
+ );
148
+
149
+ const denyWrongTodo = runHook(
150
+ {
151
+ toolName: 'Task',
152
+ workspaceRoot: tmp,
153
+ sessionId: 'verify-gate',
154
+ toolInput: { prompt: 'explore only', subagent_type: 'explore' },
155
+ },
156
+ baseEnv,
157
+ );
158
+ if (denyWrongTodo.decision === 'deny' && denyWrongTodo.gate === 'spawn-todo-persistence') {
159
+ pass('PreToolUse denies spawn skipping required plan todo');
160
+ } else {
161
+ fail('spawn todo persistence', JSON.stringify(denyWrongTodo));
162
+ }
163
+ } finally {
164
+ rmSync(tmp, { recursive: true, force: true });
165
+ }
166
+
167
+ console.log(
168
+ '\n' +
169
+ (failed === 0
170
+ ? '🎉 Grok delegation gate verify passed (4/4).'
171
+ : `⚠️ ${failed} gate check(s) failed.`),
172
+ );
173
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Consumer behavior verify — orchestrator honesty (ships in 0xray tarball).
4
+ * Run: node node_modules/0xray/scripts/mjs/verify-orchestrator-behavior.mjs
5
+ */
6
+ import { existsSync, readFileSync, unlinkSync } from 'fs';
7
+ import { dirname, join } from 'path';
8
+ import { fileURLToPath, pathToFileURL } from 'url';
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const packageRoot = join(__dirname, '../..');
12
+ const dist = (rel) => pathToFileURL(join(packageRoot, 'dist', rel)).href;
13
+
14
+ let failed = 0;
15
+
16
+ function pass(name) {
17
+ console.log(`✅ ${name}`);
18
+ }
19
+
20
+ function fail(name, detail = '') {
21
+ failed++;
22
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
23
+ }
24
+
25
+ const { dependencyCount, getExecutionPlanner } = await import(
26
+ dist('mcps/orchestrator/execution/execution-planner.js')
27
+ );
28
+ const { buildLeadDevPlan } = await import(dist('nucleus/autonomy-kernel.js'));
29
+ const { TaskHandler } = await import(
30
+ dist('mcps/orchestrator/handlers/task-handler.js')
31
+ );
32
+
33
+ const planner = getExecutionPlanner();
34
+
35
+ // 1. numeric deps ≠ NaN
36
+ const analysis = await planner.analyzeTaskComplexity([
37
+ {
38
+ id: 't1',
39
+ description: 'Jelly migration',
40
+ type: 'implement',
41
+ dependencies: 8,
42
+ },
43
+ ]);
44
+ if (Number.isFinite(analysis.overallComplexity) && analysis.overallComplexity > 0) {
45
+ pass('numeric dependencyCount produces finite MCP complexity');
46
+ } else {
47
+ fail('NaN guard', `overallComplexity=${analysis.overallComplexity}`);
48
+ }
49
+
50
+ // 2. multi-task → ≥3 todos
51
+ const multiPlan = buildLeadDevPlan(
52
+ 'G0 J1 J2',
53
+ ['implement'],
54
+ [
55
+ { description: 'Publish 0xray', type: 'release' },
56
+ { description: 'Jelly swap', type: 'migration' },
57
+ { description: 'Wire tests', type: 'test' },
58
+ ],
59
+ 70,
60
+ );
61
+ const todoCount =
62
+ multiPlan?.phases.reduce((n, p) => n + p.todos.length, 0) ?? 0;
63
+ if (multiPlan?.requiresPhasedPlan && todoCount >= 3) {
64
+ pass(`multi-task phased plan (${todoCount} todos)`);
65
+ } else {
66
+ fail('multi-task plan', `requiresPhasedPlan=${multiPlan?.requiresPhasedPlan} todos=${todoCount}`);
67
+ }
68
+
69
+ // 3. implement → backend-engineer
70
+ const implAnalysis = await planner.analyzeTaskComplexity([
71
+ { id: 'impl-1', description: 'swap deps', type: 'implement' },
72
+ ]);
73
+ const implAgent = implAnalysis.agentAssignments[0]?.agent;
74
+ if (implAgent === 'backend-engineer') {
75
+ pass('implement routes to backend-engineer');
76
+ } else {
77
+ fail('implement routing', `got ${implAgent}`);
78
+ }
79
+
80
+ // 4. MCP complexity drives phased plan (single task, high MCP score)
81
+ const singleHigh = buildLeadDevPlan(
82
+ 'single complex migration',
83
+ ['implement'],
84
+ [{ description: 'single complex migration', type: 'implement' }],
85
+ 70,
86
+ );
87
+ if (singleHigh?.requiresPhasedPlan && (singleHigh.phases.length ?? 0) > 1) {
88
+ pass('MCP overallComplexity 70 forces phased plan');
89
+ } else {
90
+ fail('MCP phased plan', `requiresPhasedPlan=${singleHigh?.requiresPhasedPlan}`);
91
+ }
92
+
93
+ // 5. orchestrate-task defers implement (not fake-complete)
94
+ const handler = new TaskHandler();
95
+ const orch = await handler.handleOrchestrateTask(
96
+ {
97
+ description: 'implement feature',
98
+ sessionId: 'verify-behavior',
99
+ tasks: [
100
+ {
101
+ id: 'be-1',
102
+ description: 'implementation work',
103
+ type: 'implement',
104
+ estimatedComplexity: 40,
105
+ },
106
+ ],
107
+ },
108
+ { taskHistory: [], activeTasks: new Map() },
109
+ );
110
+ const text = orch.content[0]?.text ?? '';
111
+ if (text.includes('Deferred') && text.includes('❌ FAILED')) {
112
+ pass('orchestrate-task defers implement (success: false)');
113
+ } else {
114
+ fail('defer honesty', text.slice(0, 200));
115
+ }
116
+
117
+ // 6. delegations[] block in response
118
+ if (text.includes('delegations') && text.includes('spawnHint')) {
119
+ pass('orchestrate-task includes delegations[] JSON block');
120
+ } else {
121
+ fail('delegations block', 'missing JSON delegations in response');
122
+ }
123
+
124
+ // 7. pending-delegations.json written on defer (consumer cwd, not package root)
125
+ const pendingPath = join(process.cwd(), '.xray', 'state', 'pending-delegations.json');
126
+ if (existsSync(pendingPath)) {
127
+ try {
128
+ const pending = JSON.parse(readFileSync(pendingPath, 'utf8'));
129
+ if (pending.delegations?.some((d) => d.status === 'pending' && d.agent === 'backend-engineer')) {
130
+ pass('pending-delegations.json recorded on defer');
131
+ } else {
132
+ fail('pending file content', 'no pending backend-engineer delegation');
133
+ }
134
+ } catch (e) {
135
+ fail('pending file parse', e.message);
136
+ }
137
+ } else {
138
+ fail('pending-delegations.json', 'file missing after defer');
139
+ }
140
+
141
+ // 8. spawnHint includes Task tool
142
+ try {
143
+ const match = text.match(/```json\n([\s\S]*?)\n```/);
144
+ const block = match ? JSON.parse(match[1]) : null;
145
+ const hint = block?.delegations?.[0]?.spawnHint;
146
+ if (hint?.tool === 'Task' && hint?.subagent_type) {
147
+ pass('delegations spawnHint routes to host Task');
148
+ } else {
149
+ fail('spawnHint', JSON.stringify(hint));
150
+ }
151
+ } catch (e) {
152
+ fail('spawnHint parse', e.message);
153
+ }
154
+
155
+ try {
156
+ if (existsSync(pendingPath)) unlinkSync(pendingPath);
157
+ } catch {
158
+ /* cleanup best-effort */
159
+ }
160
+
161
+ const total = 8;
162
+ console.log(
163
+ '\n' +
164
+ (failed === 0
165
+ ? `🎉 Orchestrator behavior verify passed (${total}/${total}).`
166
+ : `⚠️ ${failed} behavior check(s) failed.`),
167
+ );
168
+ process.exit(failed === 0 ? 0 : 1);
@@ -42,6 +42,21 @@
42
42
  }
43
43
  ]
44
44
  }
45
+ ],
46
+ "PostToolUse": [
47
+ {
48
+ "matcher": ".*",
49
+ "hooks": [
50
+ {
51
+ "type": "command",
52
+ "command": "node",
53
+ "args": [
54
+ "${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/post-tool-use.js"
55
+ ],
56
+ "timeout": 15000
57
+ }
58
+ ]
59
+ }
45
60
  ]
46
61
  }
47
62
  }