0xray 3.5.4 → 3.5.5

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 (73) hide show
  1. package/.xray/features.json +5 -1
  2. package/.xray/features.schema.json +56 -0
  3. package/AGENTS-consumer.md +1 -1
  4. package/AGENTS.md +11 -1
  5. package/README.md +1 -1
  6. package/SKILLS.md +1 -1
  7. package/dist/AGENTS.md +11 -1
  8. package/dist/CHANGELOG.md +44 -0
  9. package/dist/README.md +1 -1
  10. package/dist/SKILLS.md +1 -1
  11. package/dist/cli/commands/hermes-install.js +3 -1
  12. package/dist/cli/index.js +0 -0
  13. package/dist/core/features-config.d.ts +15 -0
  14. package/dist/governance/governance-service.d.ts +1 -0
  15. package/dist/governance/governance-service.js +81 -6
  16. package/dist/integrations/enforcement-gate.js +15 -5
  17. package/dist/integrations/grok/grok-cli.js +1 -10
  18. package/dist/integrations/grok/hooks/grok-hook-utils.js +46 -2
  19. package/dist/integrations/grok/hooks/post-tool-use.js +21 -0
  20. package/dist/integrations/grok/hooks/pre-tool-use.js +20 -0
  21. package/dist/integrations/grok/hooks/session-start.js +28 -0
  22. package/dist/integrations/hermes-agent/__init__.py +11 -1
  23. package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
  25. package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
  26. package/dist/integrations/hermes-agent/bridge.mjs +73 -10
  27. package/dist/integrations/hooks/confer-hook-runtime.mjs +49 -0
  28. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +211 -0
  29. package/dist/integrations/hooks/plan-hook-runtime.mjs +68 -0
  30. package/dist/integrations/hooks/user-aside-hook-runtime.mjs +49 -0
  31. package/dist/mcps/architect-tools.server.js +6 -1
  32. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +7 -0
  33. package/dist/mcps/orchestrator/handlers/complexity-handler.js +106 -7
  34. package/dist/mcps/orchestrator/handlers/task-handler.d.ts +6 -0
  35. package/dist/mcps/orchestrator/handlers/task-handler.js +75 -0
  36. package/dist/mcps/orchestrator/server.js +54 -5
  37. package/dist/memory-routing/provider-loader.js +30 -2
  38. package/dist/nucleus/aside-worktree.d.ts +41 -0
  39. package/dist/nucleus/aside-worktree.js +164 -0
  40. package/dist/nucleus/confer.d.ts +57 -0
  41. package/dist/nucleus/confer.js +308 -0
  42. package/dist/nucleus/delegation-gate.d.ts +4 -0
  43. package/dist/nucleus/delegation-gate.js +90 -11
  44. package/dist/nucleus/lead-dev-plan-persistence.d.ts +27 -3
  45. package/dist/nucleus/lead-dev-plan-persistence.js +201 -19
  46. package/dist/nucleus/spawn-plan-resolution.d.ts +20 -0
  47. package/dist/nucleus/spawn-plan-resolution.js +42 -0
  48. package/dist/nucleus/synthesis-consult-receipt.js +18 -2
  49. package/dist/nucleus/synthesis-context.js +1 -1
  50. package/dist/nucleus/user-aside.d.ts +70 -0
  51. package/dist/nucleus/user-aside.js +393 -0
  52. package/dist/postprocessor/PostProcessor.d.ts +1 -0
  53. package/dist/postprocessor/PostProcessor.js +11 -0
  54. package/dist/processors/implementations/session-summary-processor.d.ts +1 -1
  55. package/dist/processors/implementations/session-summary-processor.js +9 -6
  56. package/package.json +14 -5
  57. package/scripts/hooks/install-hooks.cjs +3 -3
  58. package/scripts/hooks/run-hook.js +171 -86
  59. package/scripts/mjs/verify-confer-core.mjs +103 -0
  60. package/scripts/mjs/verify-delegation-gate-core.mjs +48 -2
  61. package/scripts/mjs/verify-hermes-session-start.mjs +137 -0
  62. package/scripts/mjs/verify-pre-push-diff-core.mjs +87 -0
  63. package/scripts/mjs/verify-user-aside-core.mjs +137 -0
  64. package/scripts/node/bridge-mcp-wiring.cjs +10 -0
  65. package/scripts/node/install-bridges.cjs +3 -11
  66. package/scripts/node/load-reflection-config.mjs +2 -2
  67. package/scripts/node/release-gate.mjs +8 -4
  68. package/src/mcps/architect-tools.server.ts +6 -1
  69. package/src/mcps/orchestrator/handlers/complexity-handler.ts +178 -20
  70. package/src/mcps/orchestrator/handlers/task-handler.ts +99 -0
  71. package/src/mcps/orchestrator/server.ts +56 -5
  72. package/xray/features.json +5 -1
  73. package/xray/features.schema.json +56 -0
@@ -0,0 +1,393 @@
1
+ /**
2
+ * User aside — parallel work tracks (worktree-like) declared by the user.
3
+ * SSOT: `.xray/state/asides/{id}.json` + `_active.json`.
4
+ * Todo ids: `{asideId}.a.{phase}.{todo}` — globally unique per aside.
5
+ */
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { buildLeadDevPlan, isLeadDevModeActive, } from './autonomy-kernel.js';
9
+ import { allPlanTodos, getNextRequiredTodo, getOutstandingTodos, } from './lead-dev-plan-persistence.js';
10
+ export const USER_ASIDE_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
11
+ /** Namespaced aside todo: `suit-nft.a.1.1` */
12
+ export const USER_ASIDE_TODO_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}\.a\.\d+\.\d+$/;
13
+ export const USER_ASIDE_STATE_VERSION = 1;
14
+ export class UserAsideValidationError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'UserAsideValidationError';
18
+ }
19
+ }
20
+ export function assertSafeAsideId(asideId) {
21
+ const trimmed = asideId.trim();
22
+ if (!trimmed || !USER_ASIDE_ID_PATTERN.test(trimmed)) {
23
+ throw new UserAsideValidationError(`Invalid aside id "${asideId}" — use [a-zA-Z0-9][a-zA-Z0-9._-]{0,63}`);
24
+ }
25
+ if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
26
+ throw new UserAsideValidationError(`Invalid aside id "${asideId}" — path characters forbidden`);
27
+ }
28
+ return trimmed;
29
+ }
30
+ export function asideTodoId(asideId, phaseIndex, todoIndex) {
31
+ return `${assertSafeAsideId(asideId)}.a.${phaseIndex + 1}.${todoIndex + 1}`;
32
+ }
33
+ export function isUserAsideTodoId(todoId) {
34
+ return USER_ASIDE_TODO_PATTERN.test(todoId);
35
+ }
36
+ export function parseAsideIdFromTodoId(todoId) {
37
+ if (!isUserAsideTodoId(todoId))
38
+ return null;
39
+ const dotA = todoId.indexOf('.a.');
40
+ if (dotA <= 0)
41
+ return null;
42
+ const asideId = todoId.slice(0, dotA);
43
+ try {
44
+ return assertSafeAsideId(asideId);
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ export function userAsidesDir(projectRoot = process.cwd()) {
51
+ return path.join(projectRoot, '.xray', 'state', 'asides');
52
+ }
53
+ export function userAsidePath(asideId, projectRoot = process.cwd()) {
54
+ const safe = assertSafeAsideId(asideId);
55
+ const resolved = path.resolve(userAsidesDir(projectRoot), `${safe}.json`);
56
+ const base = path.resolve(userAsidesDir(projectRoot));
57
+ if (!resolved.startsWith(base + path.sep) && resolved !== base) {
58
+ throw new UserAsideValidationError(`Aside path escapes asides directory: ${asideId}`);
59
+ }
60
+ return resolved;
61
+ }
62
+ export function activeAsidePointerPath(projectRoot = process.cwd()) {
63
+ return path.join(userAsidesDir(projectRoot), '_active.json');
64
+ }
65
+ export function isUserAsidesEnabled(projectRoot = process.cwd()) {
66
+ const featuresPath = path.join(projectRoot, '.xray', 'features.json');
67
+ if (!fs.existsSync(featuresPath))
68
+ return isLeadDevModeActive();
69
+ try {
70
+ const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
71
+ const raw = data.multi_agent_orchestration?.user_asides;
72
+ if (raw?.enabled === false)
73
+ return false;
74
+ return isLeadDevModeActive();
75
+ }
76
+ catch {
77
+ return isLeadDevModeActive();
78
+ }
79
+ }
80
+ function validateLoadedAside(parsed, expectedId) {
81
+ if (!parsed || typeof parsed !== 'object')
82
+ return null;
83
+ const row = parsed;
84
+ if (row.version !== USER_ASIDE_STATE_VERSION)
85
+ return null;
86
+ if (!row.id || !row.title || !row.description || !row.plan)
87
+ return null;
88
+ if (!Array.isArray(row.plan.phases))
89
+ return null;
90
+ try {
91
+ assertSafeAsideId(row.id);
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ if (expectedId && row.id !== expectedId)
97
+ return null;
98
+ for (const phase of row.plan.phases) {
99
+ if (!phase?.todos || !Array.isArray(phase.todos))
100
+ return null;
101
+ }
102
+ return row;
103
+ }
104
+ function remapPhasesToAsidePrefix(asideId, phases) {
105
+ const safeId = assertSafeAsideId(asideId);
106
+ return phases.map((phase, phaseIndex) => ({
107
+ ...phase,
108
+ id: phase.id.startsWith('phase-') ? `aside-phase-${phaseIndex + 1}` : phase.id,
109
+ todos: phase.todos.map((todo, todoIndex) => ({
110
+ ...todo,
111
+ id: asideTodoId(safeId, phaseIndex, todoIndex),
112
+ status: todo.status ?? 'pending',
113
+ })),
114
+ }));
115
+ }
116
+ /** Build a phased aside plan from task intake (mirrors lead-dev plan, namespaced todo ids). */
117
+ export function buildUserAsidePlan(asideId, title, description, taskInputs = [], mcpOverallComplexity, projectRoot = process.cwd()) {
118
+ if (!isUserAsidesEnabled(projectRoot))
119
+ return null;
120
+ const safeId = assertSafeAsideId(asideId);
121
+ const taskTypes = taskInputs.map((t) => t.type ?? 'implement');
122
+ const basePlan = buildLeadDevPlan(description, taskTypes, taskInputs, mcpOverallComplexity);
123
+ if (!basePlan)
124
+ return null;
125
+ return {
126
+ version: USER_ASIDE_STATE_VERSION,
127
+ id: safeId,
128
+ title,
129
+ status: 'active',
130
+ description,
131
+ persistedAt: new Date().toISOString(),
132
+ plan: {
133
+ ...basePlan,
134
+ description: `[Aside ${safeId}] ${description}`,
135
+ phases: remapPhasesToAsidePrefix(safeId, basePlan.phases),
136
+ },
137
+ };
138
+ }
139
+ /** Merge new task intake into an existing aside (re-intake). */
140
+ export function mergeUserAsideIntake(existing, description, taskInputs, mcpOverallComplexity, projectRoot = process.cwd()) {
141
+ const fresh = buildUserAsidePlan(existing.id, existing.title, description || existing.description, taskInputs.length > 0 ? taskInputs : [{ description: existing.description, type: 'implement' }], mcpOverallComplexity, projectRoot);
142
+ if (!fresh)
143
+ return existing;
144
+ const mergedPhases = [...existing.plan.phases];
145
+ for (const phase of fresh.plan.phases) {
146
+ const match = mergedPhases.find((p) => p.id === phase.id);
147
+ if (match) {
148
+ const existingIds = new Set(match.todos.map((t) => t.id));
149
+ for (const todo of phase.todos) {
150
+ if (!existingIds.has(todo.id))
151
+ match.todos.push(todo);
152
+ }
153
+ }
154
+ else {
155
+ mergedPhases.push(phase);
156
+ }
157
+ }
158
+ return {
159
+ ...existing,
160
+ description: description || existing.description,
161
+ status: existing.status === 'completed' ? 'active' : existing.status,
162
+ plan: { ...existing.plan, phases: mergedPhases },
163
+ };
164
+ }
165
+ export function loadUserAside(asideId, projectRoot = process.cwd()) {
166
+ try {
167
+ const filePath = userAsidePath(asideId, projectRoot);
168
+ if (!fs.existsSync(filePath))
169
+ return null;
170
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
171
+ return validateLoadedAside(parsed, assertSafeAsideId(asideId));
172
+ }
173
+ catch {
174
+ return null;
175
+ }
176
+ }
177
+ export function saveUserAside(aside, projectRoot = process.cwd()) {
178
+ assertSafeAsideId(aside.id);
179
+ const dir = userAsidesDir(projectRoot);
180
+ fs.mkdirSync(dir, { recursive: true });
181
+ const filePath = userAsidePath(aside.id, projectRoot);
182
+ const payload = {
183
+ ...aside,
184
+ version: USER_ASIDE_STATE_VERSION,
185
+ persistedAt: new Date().toISOString(),
186
+ };
187
+ fs.writeFileSync(filePath, JSON.stringify(payload, null, 2));
188
+ return filePath;
189
+ }
190
+ export function listUserAsideIds(projectRoot = process.cwd()) {
191
+ const dir = userAsidesDir(projectRoot);
192
+ if (!fs.existsSync(dir))
193
+ return [];
194
+ return fs
195
+ .readdirSync(dir)
196
+ .filter((f) => f.endsWith('.json') && f !== '_active.json')
197
+ .map((f) => f.replace(/\.json$/, ''))
198
+ .filter((id) => USER_ASIDE_ID_PATTERN.test(id));
199
+ }
200
+ export function loadActiveAsidePointer(projectRoot = process.cwd()) {
201
+ const pointerPath = activeAsidePointerPath(projectRoot);
202
+ if (!fs.existsSync(pointerPath))
203
+ return null;
204
+ try {
205
+ const parsed = JSON.parse(fs.readFileSync(pointerPath, 'utf8'));
206
+ if (!parsed?.asideId)
207
+ return null;
208
+ assertSafeAsideId(parsed.asideId);
209
+ return parsed;
210
+ }
211
+ catch {
212
+ return null;
213
+ }
214
+ }
215
+ export function getActiveAsideId(projectRoot = process.cwd(), sessionId) {
216
+ if (!isUserAsidesEnabled(projectRoot))
217
+ return null;
218
+ const pointer = loadActiveAsidePointer(projectRoot);
219
+ if (!pointer)
220
+ return null;
221
+ if (pointer.sessionId) {
222
+ if (!sessionId || pointer.sessionId !== sessionId)
223
+ return null;
224
+ }
225
+ const aside = loadUserAside(pointer.asideId, projectRoot);
226
+ if (!aside || aside.status !== 'active')
227
+ return null;
228
+ return pointer.asideId;
229
+ }
230
+ export function setActiveAsideId(asideId, projectRoot = process.cwd(), sessionId) {
231
+ const safeId = assertSafeAsideId(asideId);
232
+ const aside = loadUserAside(safeId, projectRoot);
233
+ if (!aside) {
234
+ throw new UserAsideValidationError(`Cannot activate aside "${safeId}" — file not found at .xray/state/asides/${safeId}.json`);
235
+ }
236
+ if (aside.status !== 'active' && aside.status !== 'paused') {
237
+ throw new UserAsideValidationError(`Cannot activate aside "${safeId}" — status is ${aside.status}`);
238
+ }
239
+ if (aside.status === 'paused') {
240
+ aside.status = 'active';
241
+ saveUserAside(aside, projectRoot);
242
+ }
243
+ const dir = userAsidesDir(projectRoot);
244
+ fs.mkdirSync(dir, { recursive: true });
245
+ const pointer = {
246
+ asideId: safeId,
247
+ activatedAt: new Date().toISOString(),
248
+ ...(sessionId ? { sessionId } : {}),
249
+ };
250
+ fs.writeFileSync(activeAsidePointerPath(projectRoot), JSON.stringify(pointer, null, 2));
251
+ return pointer;
252
+ }
253
+ export function clearActiveAside(projectRoot = process.cwd()) {
254
+ const pointerPath = activeAsidePointerPath(projectRoot);
255
+ if (!fs.existsSync(pointerPath))
256
+ return false;
257
+ fs.unlinkSync(pointerPath);
258
+ return true;
259
+ }
260
+ export function loadActiveUserAside(projectRoot = process.cwd(), sessionId) {
261
+ const id = getActiveAsideId(projectRoot, sessionId);
262
+ if (!id) {
263
+ const pointer = loadActiveAsidePointer(projectRoot);
264
+ if (pointer) {
265
+ const aside = loadUserAside(pointer.asideId, projectRoot);
266
+ if (aside && aside.status !== 'active')
267
+ clearActiveAside(projectRoot);
268
+ }
269
+ return null;
270
+ }
271
+ const aside = loadUserAside(id, projectRoot);
272
+ if (!aside || aside.status !== 'active') {
273
+ clearActiveAside(projectRoot);
274
+ return null;
275
+ }
276
+ return aside;
277
+ }
278
+ /** PersistedLeadDevPlan view of an aside for spawn-gate reuse. */
279
+ export function asideToSpawnPlan(aside) {
280
+ const plan = { ...aside.plan };
281
+ if (aside.persistedAt)
282
+ plan.persistedAt = aside.persistedAt;
283
+ if (aside.sessionId)
284
+ plan.sessionId = aside.sessionId;
285
+ return plan;
286
+ }
287
+ /** Aside routable when active for session, or pointer is unscoped (accepted exception). */
288
+ export function isAsideActiveForSession(asideId, projectRoot = process.cwd(), sessionId) {
289
+ const activeId = getActiveAsideId(projectRoot, sessionId);
290
+ if (activeId === asideId)
291
+ return true;
292
+ const pointer = loadActiveAsidePointer(projectRoot);
293
+ if (!pointer || pointer.asideId !== asideId)
294
+ return false;
295
+ if (!pointer.sessionId)
296
+ return true;
297
+ return Boolean(sessionId && pointer.sessionId === sessionId);
298
+ }
299
+ export function findAsideContainingTodo(todoId, projectRoot = process.cwd(), preferredAsideId) {
300
+ if (!isUserAsideTodoId(todoId))
301
+ return null;
302
+ if (preferredAsideId) {
303
+ const direct = loadUserAside(preferredAsideId, projectRoot);
304
+ if (direct) {
305
+ const hit = allPlanTodos(asideToSpawnPlan(direct)).some((t) => t.id === todoId);
306
+ if (hit)
307
+ return direct;
308
+ }
309
+ }
310
+ const parsedId = parseAsideIdFromTodoId(todoId);
311
+ if (parsedId) {
312
+ const aside = loadUserAside(parsedId, projectRoot);
313
+ if (aside) {
314
+ const hit = allPlanTodos(asideToSpawnPlan(aside)).some((t) => t.id === todoId);
315
+ if (hit)
316
+ return aside;
317
+ }
318
+ }
319
+ return null;
320
+ }
321
+ export function updateUserAsideTodoStatus(todoId, status, projectRoot = process.cwd(), sessionId) {
322
+ const parsedId = parseAsideIdFromTodoId(todoId);
323
+ let aside = parsedId
324
+ ? findAsideContainingTodo(todoId, projectRoot, parsedId)
325
+ : null;
326
+ if (aside && parsedId && !isAsideActiveForSession(parsedId, projectRoot, sessionId)) {
327
+ aside = null;
328
+ }
329
+ if (!aside)
330
+ aside = loadActiveUserAside(projectRoot, sessionId);
331
+ if (!aside)
332
+ return false;
333
+ let updated = false;
334
+ for (const phase of aside.plan.phases) {
335
+ for (const todo of phase.todos) {
336
+ if (todo.id === todoId) {
337
+ todo.status = status;
338
+ updated = true;
339
+ }
340
+ }
341
+ }
342
+ if (!updated)
343
+ return false;
344
+ const outstanding = getOutstandingTodos(asideToSpawnPlan(aside));
345
+ if (outstanding.length === 0) {
346
+ aside.status = 'completed';
347
+ clearActiveAside(projectRoot);
348
+ }
349
+ saveUserAside(aside, projectRoot);
350
+ return true;
351
+ }
352
+ export function getNextAsideTodo(aside) {
353
+ return getNextRequiredTodo(asideToSpawnPlan(aside));
354
+ }
355
+ export function formatUserAsideSummary(aside) {
356
+ const todos = allPlanTodos(asideToSpawnPlan(aside));
357
+ const done = todos.filter((t) => t.status === 'completed').length;
358
+ const lines = [
359
+ `**Aside \`${aside.id}\`** — ${aside.title}`,
360
+ `Status: ${aside.status} | Todos: ${done}/${todos.length}`,
361
+ aside.worktree ? `Worktree: \`${aside.worktree}\`` : null,
362
+ aside.branch ? `Branch: \`${aside.branch}\`` : null,
363
+ '',
364
+ ...todos.map((t) => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.id} (${t.subagent}): ${t.task}`),
365
+ ].filter(Boolean);
366
+ return lines.join('\n');
367
+ }
368
+ export function getUserAsideBootHints(projectRoot = process.cwd(), sessionId) {
369
+ if (!isUserAsidesEnabled(projectRoot))
370
+ return null;
371
+ const aside = loadActiveUserAside(projectRoot, sessionId);
372
+ if (!aside)
373
+ return null;
374
+ const hints = {
375
+ activeAside: aside.id,
376
+ asideTitle: aside.title,
377
+ asideStatus: aside.status,
378
+ asideTodoPrefix: `${aside.id}.a.*`,
379
+ asideHint: 'Spawns route to active aside todos; orchestrate-task clearActiveAside to resume main',
380
+ };
381
+ if (aside.worktree)
382
+ hints.asideWorktree = aside.worktree;
383
+ if (aside.branch)
384
+ hints.asideBranch = aside.branch;
385
+ if (aside.governance?.trapSignals?.length) {
386
+ hints.asideTrapSignals = aside.governance.trapSignals;
387
+ }
388
+ if (aside.governance?.conferOnPhaseStart) {
389
+ hints.asideConferRecommended = true;
390
+ hints.asideConferTrigger = 'orchestrate-task { confer: true } or synthesis analyze-complexity';
391
+ }
392
+ return hints;
393
+ }
@@ -23,6 +23,7 @@ export declare class PostProcessor {
23
23
  private escalationEngine;
24
24
  private successHandler;
25
25
  private triggers;
26
+ private metamorphosisEngines;
26
27
  constructor(stateManager: XrayStateManager, sessionMonitor?: SessionMonitor | null, config?: Partial<PostProcessorConfig>);
27
28
  /**
28
29
  * Generate automated framework report if conditions are met
@@ -25,6 +25,7 @@ import { defaultConfig } from "./config.js";
25
25
  import { frameworkReportingSystem } from "../reporting/framework-reporting-system.js";
26
26
  import { ReportContentValidator } from "../validation/report-content-validator.js";
27
27
  import { RegressionAnalysisService } from "./services/RegressionAnalysisService.js";
28
+ import { SelfProposalEngine } from "./metamorphosis/SelfProposalEngine.js";
28
29
  export class PostProcessor {
29
30
  stateManager;
30
31
  sessionMonitor;
@@ -39,6 +40,7 @@ export class PostProcessor {
39
40
  escalationEngine;
40
41
  successHandler;
41
42
  triggers;
43
+ metamorphosisEngines;
42
44
  constructor(stateManager, sessionMonitor = null, config = {}) {
43
45
  this.stateManager = stateManager;
44
46
  this.sessionMonitor = sessionMonitor;
@@ -56,6 +58,7 @@ export class PostProcessor {
56
58
  // Initialize escalation and success handlers
57
59
  this.escalationEngine = new EscalationEngine(this.config.escalation);
58
60
  this.successHandler = new SuccessHandler(this.config.success);
61
+ this.metamorphosisEngines = [new SelfProposalEngine({ projectRoot: process.cwd() })];
59
62
  // Initialize trigger mechanisms
60
63
  this.triggers = {
61
64
  gitHook: new GitHookTrigger(this),
@@ -736,6 +739,14 @@ All path violations will be automatically detected and blocked.
736
739
  };
737
740
  // Handle successful completion
738
741
  await this.successHandler.handleSuccess(context, result, monitoringResults);
742
+ for (const engine of this.metamorphosisEngines) {
743
+ try {
744
+ await engine.onPhase('post-process-complete', { context, result, monitoringResults });
745
+ }
746
+ catch (metaErr) {
747
+ await frameworkLogger.log('-post-processor', 'self-proposal-phase-error', 'warning', { error: String(metaErr) });
748
+ }
749
+ }
739
750
  // AGENTS.md auto-update with smart triggers
740
751
  // Only updates when agent-related files have changed
741
752
  const agentChangePatterns = [
@@ -2,7 +2,7 @@
2
2
  * Session Summary Post-Processor
3
3
  *
4
4
  * Generates emoji-formatted session summaries after tool execution.
5
- * Uses features.json -> auto_reporting config for control.
5
+ * Uses features.json -> autonomous_reporting config for control.
6
6
  *
7
7
  * @since 2026-04-04
8
8
  */
@@ -2,7 +2,7 @@
2
2
  * Session Summary Post-Processor
3
3
  *
4
4
  * Generates emoji-formatted session summaries after tool execution.
5
- * Uses features.json -> auto_reporting config for control.
5
+ * Uses features.json -> autonomous_reporting config for control.
6
6
  *
7
7
  * @since 2026-04-04
8
8
  */
@@ -29,12 +29,15 @@ export class SessionSummaryProcessor extends PostProcessor {
29
29
  loadConfig() {
30
30
  try {
31
31
  const configData = featuresConfigLoader.loadConfig();
32
- const autoReporting = configData.auto_reporting;
32
+ const autoReporting = configData.autonomous_reporting ?? configData.auto_reporting;
33
33
  if (autoReporting) {
34
- this.config.enabled = autoReporting.mode !== "off";
35
- this.config.include_emojis = autoReporting.display?.indicators?.emojis !== false;
36
- this.config.include_recommendations = autoReporting.report_types?.session_summary?.include_recommendations !== false;
37
- this.config.include_agent_activities = autoReporting.report_types?.session_summary?.include_agent_activities !== false;
34
+ this.config.enabled = autoReporting.enabled !== false;
35
+ this.config.include_emojis =
36
+ autoReporting.include_agent_activities !== false;
37
+ this.config.include_recommendations =
38
+ autoReporting.include_recommendations !== false;
39
+ this.config.include_agent_activities =
40
+ autoReporting.include_agent_activities !== false;
38
41
  }
39
42
  }
40
43
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "3.5.4",
3
+ "version": "3.5.5",
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",
@@ -64,7 +64,7 @@
64
64
  "version:bump": "node scripts/node/version-manager.mjs",
65
65
  "version": "node scripts/node/version-manager.mjs",
66
66
  "prebuild": "rm -rf dist tsconfig.tsbuildinfo tsconfig.*.tsbuildinfo",
67
- "build": "tsc && mkdir -p dist/public dist/scripts && cp -r public/* dist/public/ && cp scripts/hooks/pre-command dist/scripts/ && cp scripts/hooks/pre-command.mjs dist/scripts/ && cp README.md AGENTS.md SKILLS.md CHANGELOG.md LICENSE .gitignore.default dist/ && find src -name '*.mjs' ! -path '*/__tests__/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done && for dir in skills integrations mcps; do find src/$dir -type f ! -name '*.ts' ! -path '*/.pytest_cache/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done; done && mkdir -p dist/plugin && (find dist/plugin -name '*codex-injection*.js' -exec cp {} dist/plugin/xray-codex-injection.js \\; 2>/dev/null || true) && cp -r src/opencode/ .opencode/ && mkdir -p .xray && for f in codex.json features.json features.schema.json config.json; do cp xray/$f .xray/$f 2>/dev/null || true; done",
67
+ "build": "tsc && chmod +x dist/cli/index.js && mkdir -p dist/public dist/scripts && cp -r public/* dist/public/ && cp scripts/hooks/pre-command dist/scripts/ && cp scripts/hooks/pre-command.mjs dist/scripts/ && cp README.md AGENTS.md SKILLS.md CHANGELOG.md LICENSE .gitignore.default dist/ && find src -name '*.mjs' ! -path '*/__tests__/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done && for dir in skills integrations mcps; do find src/$dir -type f ! -name '*.ts' ! -path '*/.pytest_cache/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done; done && mkdir -p dist/plugin && (find dist/plugin -name '*codex-injection*.js' -exec cp {} dist/plugin/xray-codex-injection.js \\; 2>/dev/null || true) && cp -r src/opencode/ .opencode/ && mkdir -p .xray && for f in codex.json features.json features.schema.json config.json; do cp xray/$f .xray/$f 2>/dev/null || true; done",
68
68
  "build:all": "npm run build",
69
69
  "ci-install": "npm ci",
70
70
  "clean": "rm -rf dist tsconfig.tsbuildinfo tsconfig.*.tsbuildinfo",
@@ -129,11 +129,16 @@
129
129
  "verify:orchestrator-behavior": "npm run build && node scripts/mjs/verify-orchestrator-behavior.mjs",
130
130
  "verify:grok-delegation-gate": "npm run build && node scripts/mjs/verify-grok-delegation-gate.mjs && node scripts/mjs/verify-grok-synthesis-gate.mjs",
131
131
  "verify:grok-synthesis-gate": "npm run build && node scripts/mjs/verify-grok-synthesis-gate.mjs",
132
- "verify:hermes-delegation-gate": "npm run build && node scripts/mjs/verify-hermes-delegation-gate.mjs",
132
+ "verify:hermes-delegation-gate": "npm run build && node scripts/mjs/verify-hermes-delegation-gate.mjs && node scripts/mjs/verify-hermes-session-start.mjs",
133
133
  "verify:delegation-gate": "npm run build && node scripts/mjs/verify-delegation-gate-core.mjs --host=grok && node scripts/mjs/verify-delegation-gate-core.mjs --host=hermes && node scripts/mjs/verify-delegation-gate-core.mjs --host=opencode && node scripts/mjs/verify-grok-delegation-gate.mjs && node scripts/mjs/verify-grok-synthesis-gate.mjs && node scripts/mjs/verify-hermes-delegation-gate.mjs",
134
- "verify:synthesis": "npm run build && node scripts/mjs/verify-synthesis-core.mjs && node scripts/mjs/verify-synthesis-gate-core.mjs && node scripts/mjs/verify-synthesis-outcome-core.mjs && node scripts/mjs/verify-synthesis-p2-core.mjs && node scripts/mjs/verify-synthesis-e2e-core.mjs",
134
+ "verify:synthesis": "npm run build && node scripts/mjs/verify-synthesis-core.mjs && node scripts/mjs/verify-synthesis-gate-core.mjs && node scripts/mjs/verify-synthesis-outcome-core.mjs && node scripts/mjs/verify-synthesis-p2-core.mjs && node scripts/mjs/verify-synthesis-e2e-core.mjs && node scripts/mjs/verify-confer-core.mjs",
135
+ "verify:confer": "npm run build && node scripts/mjs/verify-confer-core.mjs",
136
+ "verify:user-aside": "npm run build && node scripts/mjs/verify-user-aside-core.mjs",
137
+ "verify:pipeline-facets": "npm run build && node scripts/mjs/verify-pipeline-facets.mjs",
135
138
  "verify:find-project-root": "node scripts/mjs/verify-find-project-root-core.mjs",
136
- "verify:consumer-gitignore": "node scripts/mjs/verify-consumer-gitignore-core.mjs"
139
+ "verify:consumer-gitignore": "node scripts/mjs/verify-consumer-gitignore-core.mjs",
140
+ "verify:pre-commit-diff": "node scripts/mjs/verify-pre-commit-diff-core.mjs",
141
+ "verify:pre-push-diff": "node scripts/mjs/verify-pre-push-diff-core.mjs"
137
142
  },
138
143
  "files": [
139
144
  "dist/",
@@ -149,6 +154,10 @@
149
154
  "scripts/mjs/verify-grok-delegation-gate.mjs",
150
155
  "scripts/mjs/verify-grok-synthesis-gate.mjs",
151
156
  "scripts/mjs/verify-hermes-delegation-gate.mjs",
157
+ "scripts/mjs/verify-hermes-session-start.mjs",
158
+ "scripts/mjs/verify-pre-push-diff-core.mjs",
159
+ "scripts/mjs/verify-confer-core.mjs",
160
+ "scripts/mjs/verify-user-aside-core.mjs",
152
161
  "scripts/mjs/verify-delegation-gate-core.mjs",
153
162
  "scripts/mjs/delegation-gate-fixture.mjs",
154
163
  "src/opencode/",
@@ -108,9 +108,9 @@ BRANCH=$(git rev-parse --abbrev-ref HEAD)
108
108
  ) &
109
109
 
110
110
  MODE="minimal"
111
- COMMIT_THRESHOLD=25
111
+ COMMIT_THRESHOLD=50
112
112
  DAYS_THRESHOLD=14
113
- AUTO_GENERATE=true
113
+ AUTO_GENERATE=false
114
114
  PROMPT_USER=true
115
115
 
116
116
  if command -v node >/dev/null 2>&1; then
@@ -119,7 +119,7 @@ if command -v node >/dev/null 2>&1; then
119
119
  export PROJECT_ROOT="$PROJECT_ROOT"
120
120
  REFLECTION_JSON=$(node "$LOAD_SCRIPT" --json 2>/dev/null || echo '{}')
121
121
  MODE=$(node -e 'const c=JSON.parse(process.argv[1]);process.stdout.write(String(c.mode||"minimal"))' "$REFLECTION_JSON")
122
- COMMIT_THRESHOLD=$(node -e 'const c=JSON.parse(process.argv[1]);process.stdout.write(String(c.commitThreshold??25))' "$REFLECTION_JSON")
122
+ COMMIT_THRESHOLD=$(node -e 'const c=JSON.parse(process.argv[1]);process.stdout.write(String(c.commitThreshold??50))' "$REFLECTION_JSON")
123
123
  DAYS_THRESHOLD=$(node -e 'const c=JSON.parse(process.argv[1]);process.stdout.write(String(c.daysThreshold??14))' "$REFLECTION_JSON")
124
124
  AUTO_GENERATE=$(node -e 'const c=JSON.parse(process.argv[1]);process.stdout.write(c.autoGenerate===false?"false":"true")' "$REFLECTION_JSON")
125
125
  PROMPT_USER=$(node -e 'const c=JSON.parse(process.argv[1]);process.stdout.write(c.promptUser===false?"false":"true")' "$REFLECTION_JSON")