0xray 3.4.10 → 3.5.1

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/.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 +36 -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 +53 -32
  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/integrations/hermes-agent/__init__.py +50 -3
  17. package/dist/integrations/hermes-agent/bridge.mjs +78 -9
  18. package/dist/integrations/hooks/delegation-gate-runtime.mjs +75 -0
  19. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +1 -0
  20. package/dist/mcps/orchestrator/handlers/complexity-handler.js +7 -0
  21. package/dist/mcps/orchestrator/handlers/task-handler.js +20 -3
  22. package/dist/nucleus/delegation-gate.d.ts +86 -0
  23. package/dist/nucleus/delegation-gate.js +275 -0
  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/dist/plugin/xray-codex-injection.js +33 -0
  29. package/package.json +9 -2
  30. package/scripts/mjs/delegation-gate-fixture.mjs +84 -0
  31. package/scripts/mjs/verify-delegation-gate-core.mjs +99 -0
  32. package/scripts/mjs/verify-grok-delegation-gate.mjs +113 -0
  33. package/scripts/mjs/verify-hermes-delegation-gate.mjs +89 -0
  34. package/scripts/mjs/verify-orchestrator-behavior.mjs +47 -1
  35. package/scripts/node/universal-version-manager.js +1 -1
  36. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +15 -0
  37. package/src/mcps/orchestrator/handlers/complexity-handler.ts +8 -1
  38. package/src/mcps/orchestrator/handlers/task-handler.ts +53 -3
  39. package/xray/features.json +2 -1
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shared 4/4 delegation gate fixture against nucleus SSOT (any host adapter).
4
+ * Usage: node scripts/mjs/verify-delegation-gate-core.mjs [--host=grok|hermes|opencode]
5
+ */
6
+ import { mkdtempSync, rmSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import {
11
+ FIXTURE_SESSION_ID,
12
+ seedDelegationGateFixture,
13
+ seedSpawnTodoPlan,
14
+ } from './delegation-gate-fixture.mjs';
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const packageRoot = join(__dirname, '../..');
18
+
19
+ const hostArg = process.argv.find((a) => a.startsWith('--host='));
20
+ const host = hostArg?.split('=')[1] || 'generic';
21
+
22
+ const { evaluatePreToolGate, loadDelegationGateFeatures } = await import(
23
+ join(packageRoot, 'dist/integrations/hooks/delegation-gate-runtime.mjs')
24
+ );
25
+
26
+ const WRITE_TOOL =
27
+ host === 'hermes' ? 'write_file' : host === 'opencode' ? 'write' : 'search_replace';
28
+ const READ_TOOL = host === 'hermes' ? 'read_file' : 'read_file';
29
+ const SPAWN_TOOL =
30
+ host === 'hermes' ? 'delegate_task' : host === 'opencode' ? 'task' : 'Task';
31
+
32
+ let failed = 0;
33
+ function pass(n) {
34
+ console.log(`✅ ${n}`);
35
+ }
36
+ function fail(n, d = '') {
37
+ failed++;
38
+ console.error(`❌ ${n}${d ? ` — ${d}` : ''}`);
39
+ }
40
+
41
+ const tmp = mkdtempSync(join(tmpdir(), 'xray-gate-core-'));
42
+ seedDelegationGateFixture(tmp);
43
+
44
+ const ctx = () => ({
45
+ projectRoot: tmp,
46
+ sessionId: FIXTURE_SESSION_ID,
47
+ features: loadDelegationGateFeatures(tmp),
48
+ host,
49
+ });
50
+
51
+ try {
52
+ const deny = evaluatePreToolGate(
53
+ WRITE_TOOL,
54
+ host === 'opencode'
55
+ ? { filePath: 'src/a.ts', content: 'x' }
56
+ : { path: 'src/a.ts', new_string: 'x', content: 'x' },
57
+ ctx(),
58
+ );
59
+ if (!deny.allow && deny.gate === 'auto-chain-pending') {
60
+ pass('denies write while pending');
61
+ } else {
62
+ fail('write deny', JSON.stringify(deny));
63
+ }
64
+
65
+ const allowRead = evaluatePreToolGate(READ_TOOL, { path: 'src/a.ts' }, ctx());
66
+ if (allowRead.allow) pass('allows read while pending');
67
+ else fail('read allow', JSON.stringify(allowRead));
68
+
69
+ const allowSpawn = evaluatePreToolGate(
70
+ SPAWN_TOOL,
71
+ { prompt: 'plan todo 2.1 swap deps', subagent_type: 'backend-engineer' },
72
+ ctx(),
73
+ );
74
+ if (allowSpawn.allow) pass('allows spawn while pending');
75
+ else fail('spawn allow', JSON.stringify(allowSpawn));
76
+
77
+ seedSpawnTodoPlan(tmp);
78
+ const denyWrongTodo = evaluatePreToolGate(
79
+ SPAWN_TOOL,
80
+ { prompt: 'explore only', subagent_type: 'explore' },
81
+ ctx(),
82
+ );
83
+ if (!denyWrongTodo.allow && denyWrongTodo.gate === 'spawn-todo-persistence') {
84
+ pass('denies spawn skipping required plan todo');
85
+ } else {
86
+ fail('spawn todo persistence', JSON.stringify(denyWrongTodo));
87
+ }
88
+ } finally {
89
+ rmSync(tmp, { recursive: true, force: true });
90
+ }
91
+
92
+ const label = host === 'generic' ? 'core' : host;
93
+ console.log(
94
+ '\n' +
95
+ (failed === 0
96
+ ? `🎉 Delegation gate verify passed (4/4) [${label}].`
97
+ : `⚠️ ${failed} gate check(s) failed [${label}].`),
98
+ );
99
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,113 @@
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, rmSync } from 'node:fs';
8
+ import { tmpdir } from 'node:os';
9
+ import { join, dirname } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import {
12
+ FIXTURE_SESSION_ID,
13
+ seedDelegationGateFixture,
14
+ seedSpawnTodoPlan,
15
+ } from './delegation-gate-fixture.mjs';
16
+
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const packageRoot = join(__dirname, '../..');
19
+ const preToolHook = join(packageRoot, 'dist/integrations/grok/hooks/pre-tool-use.js');
20
+
21
+ let failed = 0;
22
+ function pass(n) {
23
+ console.log(`✅ ${n}`);
24
+ }
25
+ function fail(n, d = '') {
26
+ failed++;
27
+ console.error(`❌ ${n}${d ? ` — ${d}` : ''}`);
28
+ }
29
+
30
+ function runHook(fixture, env = {}) {
31
+ const payload = JSON.stringify(fixture);
32
+ const out = execSync(`printf '%s' '${payload.replace(/'/g, "'\\''")}' | node "${preToolHook}"`, {
33
+ encoding: 'utf8',
34
+ env: { ...process.env, ...env },
35
+ });
36
+ return JSON.parse(out.trim());
37
+ }
38
+
39
+ const tmp = mkdtempSync(join(tmpdir(), 'xray-gate-verify-'));
40
+ seedDelegationGateFixture(tmp);
41
+
42
+ const baseEnv = {
43
+ GROK_WORKSPACE_ROOT: tmp,
44
+ GROK_SESSION_ID: FIXTURE_SESSION_ID,
45
+ };
46
+
47
+ try {
48
+ const deny = runHook(
49
+ {
50
+ toolName: 'search_replace',
51
+ workspaceRoot: tmp,
52
+ sessionId: 'verify-gate',
53
+ toolInput: { path: 'src/a.ts', new_string: 'x' },
54
+ },
55
+ baseEnv,
56
+ );
57
+ if (deny.decision === 'deny' && deny.gate === 'auto-chain-pending') {
58
+ pass('PreToolUse denies write while pending');
59
+ } else {
60
+ fail('write deny', JSON.stringify(deny));
61
+ }
62
+
63
+ const allowRead = runHook(
64
+ {
65
+ toolName: 'read_file',
66
+ workspaceRoot: tmp,
67
+ sessionId: 'verify-gate',
68
+ toolInput: { path: 'src/a.ts' },
69
+ },
70
+ baseEnv,
71
+ );
72
+ if (allowRead.decision === 'allow') pass('PreToolUse allows read while pending');
73
+ else fail('read allow', JSON.stringify(allowRead));
74
+
75
+ const allowTask = runHook(
76
+ {
77
+ toolName: 'Task',
78
+ workspaceRoot: tmp,
79
+ sessionId: 'verify-gate',
80
+ toolInput: { prompt: 'plan todo 2.1 swap deps', subagent_type: 'backend-engineer' },
81
+ },
82
+ baseEnv,
83
+ );
84
+ if (allowTask.decision === 'allow') pass('PreToolUse allows Task while pending');
85
+ else fail('Task allow', JSON.stringify(allowTask));
86
+
87
+ seedSpawnTodoPlan(tmp);
88
+
89
+ const denyWrongTodo = runHook(
90
+ {
91
+ toolName: 'Task',
92
+ workspaceRoot: tmp,
93
+ sessionId: 'verify-gate',
94
+ toolInput: { prompt: 'explore only', subagent_type: 'explore' },
95
+ },
96
+ baseEnv,
97
+ );
98
+ if (denyWrongTodo.decision === 'deny' && denyWrongTodo.gate === 'spawn-todo-persistence') {
99
+ pass('PreToolUse denies spawn skipping required plan todo');
100
+ } else {
101
+ fail('spawn todo persistence', JSON.stringify(denyWrongTodo));
102
+ }
103
+ } finally {
104
+ rmSync(tmp, { recursive: true, force: true });
105
+ }
106
+
107
+ console.log(
108
+ '\n' +
109
+ (failed === 0
110
+ ? '🎉 Grok delegation gate verify passed (4/4).'
111
+ : `⚠️ ${failed} gate check(s) failed.`),
112
+ );
113
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hermes bridge delegation gate fixture (4/4) — uses bridge delegation-gate command.
4
+ */
5
+ import { execSync } from 'node:child_process';
6
+ import { mkdtempSync, rmSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import {
11
+ FIXTURE_SESSION_ID,
12
+ seedDelegationGateFixture,
13
+ seedSpawnTodoPlan,
14
+ } from './delegation-gate-fixture.mjs';
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const packageRoot = join(__dirname, '../..');
18
+ const bridge = join(packageRoot, 'dist/integrations/hermes-agent/bridge.mjs');
19
+
20
+ let failed = 0;
21
+ function pass(n) {
22
+ console.log(`✅ ${n}`);
23
+ }
24
+ function fail(n, d = '') {
25
+ failed++;
26
+ console.error(`❌ ${n}${d ? ` — ${d}` : ''}`);
27
+ }
28
+
29
+ function runBridge(payload, cwd) {
30
+ const json = JSON.stringify(payload).replace(/'/g, "'\\''");
31
+ const out = execSync(`node "${bridge}" delegation-gate --cwd "${cwd}" '${json}'`, {
32
+ cwd,
33
+ encoding: 'utf8',
34
+ env: { ...process.env, XRAY_ROOT: cwd },
35
+ });
36
+ return JSON.parse(out.trim());
37
+ }
38
+
39
+ const tmp = mkdtempSync(join(tmpdir(), 'xray-hermes-gate-'));
40
+ seedDelegationGateFixture(tmp);
41
+
42
+ const base = {
43
+ command: 'delegation-gate',
44
+ phase: 'pre',
45
+ sessionId: FIXTURE_SESSION_ID,
46
+ host: 'hermes',
47
+ };
48
+
49
+ try {
50
+ const deny = runBridge({ ...base, tool: 'write_file', args: { path: 'src/a.ts', content: 'x' } }, tmp);
51
+ if (deny.allow === false && deny.gate === 'auto-chain-pending') pass('bridge denies write while pending');
52
+ else fail('write deny', JSON.stringify(deny));
53
+
54
+ const allowRead = runBridge({ ...base, tool: 'read_file', args: { path: 'src/a.ts' } }, tmp);
55
+ if (allowRead.allow === true) pass('bridge allows read while pending');
56
+ else fail('read allow', JSON.stringify(allowRead));
57
+
58
+ const allowSpawn = runBridge(
59
+ {
60
+ ...base,
61
+ tool: 'delegate_task',
62
+ args: { prompt: 'plan todo 2.1 swap deps', subagent_type: 'backend-engineer' },
63
+ },
64
+ tmp,
65
+ );
66
+ if (allowSpawn.allow === true) pass('bridge allows delegate_task while pending');
67
+ else fail('spawn allow', JSON.stringify(allowSpawn));
68
+
69
+ seedSpawnTodoPlan(tmp);
70
+ const denyTodo = runBridge(
71
+ { ...base, tool: 'delegate_task', args: { prompt: 'explore only', subagent_type: 'explore' } },
72
+ tmp,
73
+ );
74
+ if (denyTodo.allow === false && denyTodo.gate === 'spawn-todo-persistence') {
75
+ pass('bridge denies spawn skipping required plan todo');
76
+ } else {
77
+ fail('spawn todo persistence', JSON.stringify(denyTodo));
78
+ }
79
+ } finally {
80
+ rmSync(tmp, { recursive: true, force: true });
81
+ }
82
+
83
+ console.log(
84
+ '\n' +
85
+ (failed === 0
86
+ ? '🎉 Hermes delegation gate verify passed (4/4).'
87
+ : `⚠️ ${failed} Hermes gate check(s) failed.`),
88
+ );
89
+ process.exit(failed === 0 ? 0 : 1);
@@ -3,6 +3,7 @@
3
3
  * Consumer behavior verify — orchestrator honesty (ships in 0xray tarball).
4
4
  * Run: node node_modules/0xray/scripts/mjs/verify-orchestrator-behavior.mjs
5
5
  */
6
+ import { existsSync, readFileSync, unlinkSync } from 'fs';
6
7
  import { dirname, join } from 'path';
7
8
  import { fileURLToPath, pathToFileURL } from 'url';
8
9
 
@@ -113,10 +114,55 @@ if (text.includes('Deferred') && text.includes('❌ FAILED')) {
113
114
  fail('defer honesty', text.slice(0, 200));
114
115
  }
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;
116
162
  console.log(
117
163
  '\n' +
118
164
  (failed === 0
119
- ? '🎉 Orchestrator behavior verify passed (5/5).'
165
+ ? `🎉 Orchestrator behavior verify passed (${total}/${total}).`
120
166
  : `⚠️ ${failed} behavior check(s) failed.`),
121
167
  );
122
168
  process.exit(failed === 0 ? 0 : 1);
@@ -152,7 +152,7 @@ const OFFICIAL_VERSIONS = {
152
152
  framework: {
153
153
  version: PACKAGE_VERSION,
154
154
  displayName: "xray: Self-Healing AI Governance OS",
155
- lastUpdated: "2026-06-17",
155
+ lastUpdated: "2026-06-19",
156
156
  // Counts (auto-calculated, but can be overridden)
157
157
  ...CALCULATED_COUNTS,
158
158
  },
@@ -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
  }
@@ -10,6 +10,8 @@ import {
10
10
  isLeadDevModeActive,
11
11
  persistLeadDevPlan,
12
12
  } from '../../../nucleus/autonomy-kernel.js';
13
+ import { bindPlanToSession } from '../../../nucleus/lead-dev-plan-persistence.js';
14
+ import { clearPendingDelegations } from '../../../nucleus/pending-delegations.js';
13
15
  import { getExecutionPlanner } from '../execution/execution-planner.js';
14
16
  import { addObservations, extractComplexityObservations } from '../aside-context.js';
15
17
  import type { OrchestrationTask, ComplexityAnalysis } from '../types.js';
@@ -65,7 +67,7 @@ export class ComplexityHandler {
65
67
  * Handle analyze-complexity request
66
68
  */
67
69
  async handleAnalyzeComplexity(
68
- args: { tasks: AnalyzeComplexityInput[] },
70
+ args: { tasks: AnalyzeComplexityInput[]; sessionId?: string },
69
71
  asideId?: string,
70
72
  ): Promise<{ content: Array<{ type: string; text: string }> }> {
71
73
  const tasks = normalizeAnalyzeTasks(args.tasks ?? []);
@@ -102,13 +104,18 @@ export class ComplexityHandler {
102
104
 
103
105
  if (leadDevPlan) {
104
106
  try {
107
+ const sessionId = args.sessionId ?? `analyze-${Date.now()}`;
108
+ clearPendingDelegations();
105
109
  const planPath = persistLeadDevPlan(leadDevPlan);
110
+ const bound = bindPlanToSession(sessionId);
106
111
  await frameworkLogger.log(
107
112
  'orchestrator.server',
108
113
  'lead-dev-plan-persisted',
109
114
  'info',
110
115
  {
111
116
  planPath,
117
+ sessionId,
118
+ planGeneration: bound?.planGeneration,
112
119
  phaseCount: leadDevPlan.phases.length,
113
120
  todoCount: leadDevPlan.phases.reduce((n, p) => n + p.todos.length, 0),
114
121
  },
@@ -9,6 +9,11 @@ import { getExecutionPlanner } from '../execution/execution-planner.js';
9
9
  import { mcpClientManager } from '../../../mcps/mcp-client.js';
10
10
  import { addObservations, extractOrchestrationObservations, spawnAside } from '../aside-context.js';
11
11
  import { getProvider } from '../config/memory-routing-bridge.js';
12
+ import {
13
+ delegationSummaryForResponse,
14
+ recordDeferredDelegations,
15
+ type PendingDelegation,
16
+ } from '../../../nucleus/pending-delegations.js';
12
17
  import type { OrchestrationResult, OrchestrationTask } from '../types.js';
13
18
 
14
19
  export interface TaskHandlerDeps {
@@ -66,6 +71,7 @@ export class TaskHandler {
66
71
  };
67
72
 
68
73
  const startTime = Date.now();
74
+ let pendingDelegations: PendingDelegation[] = [];
69
75
 
70
76
  try {
71
77
  // Validate tasks
@@ -90,6 +96,16 @@ export class TaskHandler {
90
96
 
91
97
  const results = await this.executePlan(executionPlan, sessionId, timeout);
92
98
 
99
+ if (results.deferredRecords.length > 0) {
100
+ pendingDelegations = recordDeferredDelegations(sessionId, results.deferredRecords);
101
+ await frameworkLogger.log(
102
+ 'orchestrator.task-handler',
103
+ 'pending-delegations-recorded',
104
+ 'info',
105
+ { count: pendingDelegations.length, sessionId },
106
+ );
107
+ }
108
+
93
109
  // Feed per-task outcomes back to memory provider (not aggregate session stats)
94
110
  if (memoryProvider.ingestFeedback) {
95
111
  const outcomeByKey = new Map(
@@ -150,7 +166,12 @@ export class TaskHandler {
150
166
 
151
167
  orchestrationResult.duration = Date.now() - startTime;
152
168
 
153
- const response = this.formatOrchestrationResponse(description, orchestrationResult, executionMode);
169
+ const response = this.formatOrchestrationResponse(
170
+ description,
171
+ orchestrationResult,
172
+ executionMode,
173
+ pendingDelegations,
174
+ );
154
175
 
155
176
  return { content: [{ type: 'text', text: response }] };
156
177
  }
@@ -173,6 +194,12 @@ export class TaskHandler {
173
194
  bottlenecks: string[];
174
195
  recommendations: string[];
175
196
  agentOutputs?: Record<string, string>;
197
+ deferredRecords: Array<{
198
+ taskId: string;
199
+ agent: string;
200
+ description: string;
201
+ type?: string;
202
+ }>;
176
203
  taskOutcomes: Array<{
177
204
  taskId: string;
178
205
  agent: string;
@@ -185,6 +212,12 @@ export class TaskHandler {
185
212
  let failedTasks = 0;
186
213
  let deferredTasks = 0;
187
214
  const agentOutputs: Record<string, string> = {};
215
+ const deferredRecords: Array<{
216
+ taskId: string;
217
+ agent: string;
218
+ description: string;
219
+ type?: string;
220
+ }> = [];
188
221
  const taskOutcomes: Array<{
189
222
  taskId: string;
190
223
  agent: string;
@@ -245,6 +278,12 @@ export class TaskHandler {
245
278
  } else {
246
279
  for (const task of tasks) {
247
280
  deferredTasks++;
281
+ deferredRecords.push({
282
+ taskId: task.id,
283
+ agent,
284
+ description: task.description,
285
+ type: task.type,
286
+ });
248
287
  taskOutcomes.push({
249
288
  taskId: task.id,
250
289
  agent,
@@ -288,6 +327,7 @@ export class TaskHandler {
288
327
  bottlenecks,
289
328
  recommendations,
290
329
  agentOutputs,
330
+ deferredRecords,
291
331
  taskOutcomes,
292
332
  };
293
333
  }
@@ -329,8 +369,18 @@ export class TaskHandler {
329
369
  private formatOrchestrationResponse(
330
370
  description: string,
331
371
  result: OrchestrationResult,
332
- executionMode: string
372
+ executionMode: string,
373
+ pendingDelegations: PendingDelegation[] = [],
333
374
  ): string {
375
+ const delegationsBlock =
376
+ pendingDelegations.length > 0
377
+ ? `\n**Delegations (host Task required):**\n\`\`\`json\n${JSON.stringify(
378
+ { delegations: delegationSummaryForResponse(pendingDelegations) },
379
+ null,
380
+ 2,
381
+ )}\n\`\`\`\n`
382
+ : '';
383
+
334
384
  return `🎯 Orchestration Complete: "${description}"
335
385
 
336
386
  **Session ID:** ${result.sessionId}
@@ -355,6 +405,6 @@ ${result.recommendations.length > 0 ? result.recommendations.map((r) => `•
355
405
 
356
406
  ${result.agentOutputs && Object.keys(result.agentOutputs).length > 0
357
407
  ? `**Agent Outputs (real MCP responses):**\n${Object.entries(result.agentOutputs).map(([k, v]) => `• ${k}: ${v.substring(0, 200)}${v.length > 200 ? '...' : ''}`).join('\n')}\n`
358
- : ''}`;
408
+ : ''}${delegationsBlock}`;
359
409
  }
360
410
  }
@@ -35,7 +35,8 @@
35
35
  "per_suite_test_triage": true,
36
36
  "phased_plan_threshold": 25,
37
37
  "auto_consult_major_work": true,
38
- "no_new_surface": true
38
+ "no_new_surface": true,
39
+ "auto_chain_delegations": true
39
40
  },
40
41
  "autonomous_reporting": {
41
42
  "enabled": true,