0xray 3.5.0 → 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 +32 -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 +32 -385
- 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/__init__.py +50 -3
- package/dist/integrations/hermes-agent/bridge.mjs +71 -30
- package/dist/integrations/hooks/delegation-gate-runtime.mjs +75 -0
- 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 +88 -0
- package/dist/nucleus/delegation-gate.js +347 -0
- package/dist/nucleus/lead-dev-plan-persistence.d.ts +3 -0
- package/dist/nucleus/lead-dev-plan-persistence.js +47 -4
- 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 +50 -0
- package/package.json +11 -2
- 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 +121 -0
- package/scripts/mjs/verify-delegation-gate-core.mjs +99 -0
- package/scripts/mjs/verify-grok-delegation-gate.mjs +9 -69
- package/scripts/mjs/verify-hermes-delegation-gate.mjs +89 -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/scripts/node/universal-version-manager.js +1 -1
- 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
|
@@ -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,40 +53,19 @@ let frameworkReady = false;
|
|
|
52
53
|
let frameworkLoadAttempted = false;
|
|
53
54
|
|
|
54
55
|
// ── Project root detection ───────────────────────────────────
|
|
55
|
-
function
|
|
56
|
-
const envHome = process.env.XRAY_HOME || process.env.XRAY_ROOT;
|
|
57
|
-
if (envHome && existsSync(join(envHome, "package.json"))) return envHome;
|
|
58
|
-
|
|
56
|
+
function resolveBridgeProjectRoot() {
|
|
59
57
|
const consumerMarker = join(__dirname, "xray-consumer-root.txt");
|
|
60
58
|
if (existsSync(consumerMarker)) {
|
|
61
59
|
try {
|
|
62
60
|
const marked = readFileSync(consumerMarker, "utf-8").trim();
|
|
63
|
-
if (marked && existsSync(join(marked, "package.json")))
|
|
64
|
-
|
|
65
|
-
// fall through
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const candidates = [
|
|
70
|
-
process.cwd(),
|
|
71
|
-
join(homedir(), "dev", "xray"),
|
|
72
|
-
join(dirname(__dirname), "..", "..", "..", ".."), // plugin dir -> project root
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
for (const dir of candidates) {
|
|
76
|
-
try {
|
|
77
|
-
const pkgPath = join(dir, "package.json");
|
|
78
|
-
if (existsSync(pkgPath)) {
|
|
79
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
80
|
-
if (pkg.name === "0xray" || pkg.dependencies?.["0xray"] || pkg.name === "0xray" || pkg.dependencies?.["0xray"]) {
|
|
81
|
-
return dir;
|
|
82
|
-
}
|
|
61
|
+
if (marked && existsSync(join(marked, "package.json"))) {
|
|
62
|
+
return marked;
|
|
83
63
|
}
|
|
84
64
|
} catch {
|
|
85
|
-
|
|
65
|
+
// fall through to shared SSOT
|
|
86
66
|
}
|
|
87
67
|
}
|
|
88
|
-
return
|
|
68
|
+
return findProjectRoot([join(dirname(__dirname), "..", "..", "..", "..")]);
|
|
89
69
|
}
|
|
90
70
|
|
|
91
71
|
// ── Log directory ────────────────────────────────────────────
|
|
@@ -381,7 +361,7 @@ async function runProcessors(tool, args, phase, projectRoot, logDir) {
|
|
|
381
361
|
// ── Command handlers ─────────────────────────────────────────
|
|
382
362
|
|
|
383
363
|
async function handleHealth(input) {
|
|
384
|
-
const projectRoot =
|
|
364
|
+
const projectRoot = resolveBridgeProjectRoot();
|
|
385
365
|
const loaded = await loadFramework(projectRoot);
|
|
386
366
|
|
|
387
367
|
const components = {
|
|
@@ -738,7 +718,7 @@ function handleStats() {
|
|
|
738
718
|
qualityGateAvailable: !!runQualityGateWithLogging,
|
|
739
719
|
processorsAvailable: !!ProcessorManager,
|
|
740
720
|
nodeVersion: process.version,
|
|
741
|
-
projectRoot:
|
|
721
|
+
projectRoot: resolveBridgeProjectRoot(),
|
|
742
722
|
};
|
|
743
723
|
}
|
|
744
724
|
|
|
@@ -786,9 +766,67 @@ async function handleApply(input, projectRoot, logDir) {
|
|
|
786
766
|
// ── Known commands for positional-arg mode ──────────────────
|
|
787
767
|
const KNOWN_COMMANDS = new Set([
|
|
788
768
|
"health", "stats", "pre-process", "post-process", "validate", "codex-check", "hooks", "govern", "apply",
|
|
789
|
-
"skill-install", "skill-registry",
|
|
769
|
+
"skill-install", "skill-registry", "delegation-gate",
|
|
790
770
|
]);
|
|
791
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
|
+
|
|
782
|
+
async function handleDelegationGate(command, projectRoot, logDir) {
|
|
783
|
+
try {
|
|
784
|
+
const gate = await import("../hooks/delegation-gate-runtime.mjs");
|
|
785
|
+
const phase = command.phase || "pre";
|
|
786
|
+
const tool = command.tool || command.toolName || "unknown";
|
|
787
|
+
const args = command.args || command.toolInput || {};
|
|
788
|
+
const sessionId = command.sessionId || command.taskId || null;
|
|
789
|
+
const host = command.host || "hermes";
|
|
790
|
+
|
|
791
|
+
if (phase === "post") {
|
|
792
|
+
if (!gate.isSubagentTool(tool)) {
|
|
793
|
+
return { allow: true, phase: "post", skipped: true };
|
|
794
|
+
}
|
|
795
|
+
const result = gate.evaluatePostToolSpawn(tool, args, projectRoot);
|
|
796
|
+
if (result.satisfied.length > 0) {
|
|
797
|
+
logToActivity(
|
|
798
|
+
logDir,
|
|
799
|
+
`[delegation-gate-post] satisfied=${result.satisfied.map((d) => d.id).join(",")} clearedAll=${result.clearedAll}`,
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
return { allow: true, phase: "post", ...result };
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (command.recordTurn === true || command.event === 'user_prompt_submit') {
|
|
806
|
+
await recordHermesSynthesisTurn(projectRoot, sessionId);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const features = gate.loadDelegationGateFeatures(projectRoot);
|
|
810
|
+
const outcome = gate.evaluatePreToolGate(tool, args, {
|
|
811
|
+
projectRoot,
|
|
812
|
+
sessionId,
|
|
813
|
+
features,
|
|
814
|
+
host,
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
if (!outcome.allow) {
|
|
818
|
+
logToActivity(
|
|
819
|
+
logDir,
|
|
820
|
+
`[delegation-gate] deny tool=${tool} gate=${outcome.gate} reason=${outcome.reason}`,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return { phase: "pre", ...outcome };
|
|
825
|
+
} catch (error) {
|
|
826
|
+
return { error: `delegation-gate failed: ${error.message || error}` };
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
792
830
|
// ── Skill management ──────────────────────────────────────────
|
|
793
831
|
|
|
794
832
|
async function handleSkillInstall(command, projectRoot, logDir) {
|
|
@@ -869,7 +907,7 @@ async function main() {
|
|
|
869
907
|
}
|
|
870
908
|
}
|
|
871
909
|
|
|
872
|
-
const projectRoot =
|
|
910
|
+
const projectRoot = resolveBridgeProjectRoot();
|
|
873
911
|
const logDir = ensureLogDir(projectRoot);
|
|
874
912
|
|
|
875
913
|
// Log session start for test verification
|
|
@@ -918,6 +956,9 @@ async function main() {
|
|
|
918
956
|
case "skill-registry":
|
|
919
957
|
response = await handleSkillRegistry(command, projectRoot, logDir);
|
|
920
958
|
break;
|
|
959
|
+
case "delegation-gate":
|
|
960
|
+
response = await handleDelegationGate(command, projectRoot, logDir);
|
|
961
|
+
break;
|
|
921
962
|
default:
|
|
922
963
|
response = { error: `Unknown command: ${cmd}` };
|
|
923
964
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Load compiled delegation-gate SSOT from consumer or package dist.
|
|
4
|
+
* Used by Grok hooks, Hermes bridge, and verify fixtures.
|
|
5
|
+
*/
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { dirname, join, resolve } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
/** 0xray package root — never use consumer workspace env (GROK_WORKSPACE_ROOT / XRAY_ROOT). */
|
|
14
|
+
function resolvePackageRoot() {
|
|
15
|
+
let dir = __dirname;
|
|
16
|
+
for (let i = 0; i < 10; i++) {
|
|
17
|
+
const pkg = join(dir, 'package.json');
|
|
18
|
+
if (existsSync(pkg)) {
|
|
19
|
+
try {
|
|
20
|
+
const require = createRequire(join(dir, 'package.json'));
|
|
21
|
+
const name = require('./package.json').name;
|
|
22
|
+
if (name === '0xray') return dir;
|
|
23
|
+
} catch {
|
|
24
|
+
/* continue */
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const nm = join(dir, 'node_modules', '0xray', 'package.json');
|
|
28
|
+
if (existsSync(nm)) {
|
|
29
|
+
return dirname(nm);
|
|
30
|
+
}
|
|
31
|
+
const parent = dirname(dir);
|
|
32
|
+
if (parent === dir) break;
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
return resolve(__dirname, '../../..');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function loadDelegationGate() {
|
|
39
|
+
const root = resolvePackageRoot();
|
|
40
|
+
const candidates = [
|
|
41
|
+
join(__dirname, '../../nucleus/delegation-gate.js'),
|
|
42
|
+
join(root, 'dist/nucleus/delegation-gate.js'),
|
|
43
|
+
join(process.cwd(), 'node_modules/0xray/dist/nucleus/delegation-gate.js'),
|
|
44
|
+
];
|
|
45
|
+
const found = candidates.find((p) => existsSync(p));
|
|
46
|
+
if (!found) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
'delegation-gate.js missing — run npm run build in 0xray or npm install 0xray@^3.5.1',
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return createRequire(import.meta.url)(found);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const gate = loadDelegationGate();
|
|
55
|
+
|
|
56
|
+
export const {
|
|
57
|
+
loadDelegationGateFeatures,
|
|
58
|
+
normalizeHostToolInput,
|
|
59
|
+
evaluatePreToolGate,
|
|
60
|
+
evaluatePendingDelegationGate,
|
|
61
|
+
evaluateSpawnPlanGate,
|
|
62
|
+
evaluatePostToolSpawn,
|
|
63
|
+
checkPendingDelegationGate,
|
|
64
|
+
checkSubagentGate,
|
|
65
|
+
satisfyDelegationsFromToolInput,
|
|
66
|
+
isSubagentTool,
|
|
67
|
+
isOrchestrateToolEvent,
|
|
68
|
+
isReadOnlyTool,
|
|
69
|
+
isWriteTool,
|
|
70
|
+
isShellTool,
|
|
71
|
+
getActivePendingDelegations,
|
|
72
|
+
validateSpawnMatchesTodo,
|
|
73
|
+
updatePlanTodoStatus,
|
|
74
|
+
updatePlanTodoStatusInPlace,
|
|
75
|
+
} = gate;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Load compiled synthesis SSOT for Grok/Hermes hooks.
|
|
4
|
+
*/
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { dirname, join, resolve } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
function resolvePackageRoot() {
|
|
13
|
+
let dir = __dirname;
|
|
14
|
+
for (let i = 0; i < 10; i++) {
|
|
15
|
+
const pkg = join(dir, 'package.json');
|
|
16
|
+
if (existsSync(pkg)) {
|
|
17
|
+
try {
|
|
18
|
+
const require = createRequire(join(dir, 'package.json'));
|
|
19
|
+
if (require('./package.json').name === '0xray') return dir;
|
|
20
|
+
} catch {
|
|
21
|
+
/* continue */
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const nm = join(dir, 'node_modules', '0xray', 'package.json');
|
|
25
|
+
if (existsSync(nm)) return dirname(nm);
|
|
26
|
+
const parent = dirname(dir);
|
|
27
|
+
if (parent === dir) break;
|
|
28
|
+
dir = parent;
|
|
29
|
+
}
|
|
30
|
+
return resolve(__dirname, '../../..');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadSynthesis() {
|
|
34
|
+
const root = resolvePackageRoot();
|
|
35
|
+
const candidates = [
|
|
36
|
+
join(__dirname, '../../nucleus/synthesis.js'),
|
|
37
|
+
join(root, 'dist/nucleus/synthesis.js'),
|
|
38
|
+
join(process.cwd(), 'node_modules/0xray/dist/nucleus/synthesis.js'),
|
|
39
|
+
];
|
|
40
|
+
const found = candidates.find((p) => existsSync(p));
|
|
41
|
+
if (!found) return null;
|
|
42
|
+
return createRequire(import.meta.url)(found);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function recordSynthesisTurnSlice(projectRoot, sessionId) {
|
|
46
|
+
if (!sessionId) return null;
|
|
47
|
+
const mod = loadSynthesis();
|
|
48
|
+
if (!mod?.recordExecutionSlice) return null;
|
|
49
|
+
return mod.recordExecutionSlice('turn', { projectRoot, sessionId });
|
|
50
|
+
}
|
|
@@ -27,11 +27,14 @@ export declare class ComplexityHandler {
|
|
|
27
27
|
handleAnalyzeComplexity(args: {
|
|
28
28
|
tasks: AnalyzeComplexityInput[];
|
|
29
29
|
sessionId?: string;
|
|
30
|
+
synthesisCheckpoint?: boolean;
|
|
31
|
+
synthesisDueReason?: string | null;
|
|
30
32
|
}, asideId?: string): Promise<{
|
|
31
33
|
content: Array<{
|
|
32
34
|
type: string;
|
|
33
35
|
text: string;
|
|
34
36
|
}>;
|
|
37
|
+
ok: boolean;
|
|
35
38
|
}>;
|
|
36
39
|
/**
|
|
37
40
|
* Generate recommendations based on complexity analysis
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles complexity analysis requests
|
|
5
5
|
*/
|
|
6
6
|
import { frameworkLogger } from '../../../core/framework-logger.js';
|
|
7
|
-
import { buildLeadDevPlan, isLeadDevModeActive, persistLeadDevPlan, } from '../../../nucleus/autonomy-kernel.js';
|
|
7
|
+
import { buildLeadDevPlan, buildSynthesisCheckpointPlan, isLeadDevModeActive, persistLeadDevPlan, } from '../../../nucleus/autonomy-kernel.js';
|
|
8
8
|
import { bindPlanToSession } from '../../../nucleus/lead-dev-plan-persistence.js';
|
|
9
9
|
import { clearPendingDelegations } from '../../../nucleus/pending-delegations.js';
|
|
10
10
|
import { getExecutionPlanner } from '../execution/execution-planner.js';
|
|
@@ -60,38 +60,50 @@ export class ComplexityHandler {
|
|
|
60
60
|
const planTaskInputs = tasks
|
|
61
61
|
.filter((t) => t.description?.trim())
|
|
62
62
|
.map((t) => ({ description: t.description, type: t.type }));
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const synthesisCheckpoint = args.synthesisCheckpoint === true;
|
|
64
|
+
const leadDevPlan = synthesisCheckpoint
|
|
65
|
+
? buildSynthesisCheckpointPlan(args.synthesisDueReason ?? null)
|
|
66
|
+
: isLeadDevModeActive() && (primaryDescription || planTaskInputs.length > 0)
|
|
67
|
+
? buildLeadDevPlan(primaryDescription, taskTypes.length ? taskTypes : ['implement'], planTaskInputs, analysis.overallComplexity)
|
|
68
|
+
: null;
|
|
69
|
+
let planPersisted = synthesisCheckpoint ? leadDevPlan !== null : !leadDevPlan;
|
|
66
70
|
if (leadDevPlan) {
|
|
67
71
|
try {
|
|
68
72
|
const sessionId = args.sessionId ?? `analyze-${Date.now()}`;
|
|
69
|
-
|
|
73
|
+
if (!synthesisCheckpoint) {
|
|
74
|
+
clearPendingDelegations();
|
|
75
|
+
}
|
|
70
76
|
const planPath = persistLeadDevPlan(leadDevPlan);
|
|
71
77
|
const bound = bindPlanToSession(sessionId);
|
|
72
|
-
|
|
78
|
+
planPersisted = true;
|
|
79
|
+
await frameworkLogger.log('orchestrator.server', synthesisCheckpoint ? 'synthesis-plan-persisted' : 'lead-dev-plan-persisted', 'info', {
|
|
73
80
|
planPath,
|
|
74
81
|
sessionId,
|
|
75
82
|
planGeneration: bound?.planGeneration,
|
|
76
83
|
phaseCount: leadDevPlan.phases.length,
|
|
77
84
|
todoCount: leadDevPlan.phases.reduce((n, p) => n + p.todos.length, 0),
|
|
85
|
+
synthesisCheckpoint,
|
|
78
86
|
});
|
|
79
87
|
}
|
|
80
88
|
catch (err) {
|
|
81
|
-
|
|
89
|
+
planPersisted = false;
|
|
90
|
+
await frameworkLogger.log('orchestrator.server', synthesisCheckpoint ? 'synthesis-plan-persist-failed' : 'lead-dev-plan-persist-failed', 'warning', { error: err instanceof Error ? err.message : String(err) });
|
|
82
91
|
}
|
|
83
92
|
}
|
|
93
|
+
const ok = planPersisted;
|
|
84
94
|
return {
|
|
95
|
+
ok,
|
|
85
96
|
content: [
|
|
86
97
|
{
|
|
87
98
|
type: 'text',
|
|
88
|
-
text: this.formatComplexityResponse(analysis, recommendations, tasks.length, leadDevPlan),
|
|
99
|
+
text: this.formatComplexityResponse(analysis, recommendations, tasks.length, leadDevPlan, synthesisCheckpoint),
|
|
89
100
|
},
|
|
90
101
|
],
|
|
91
102
|
};
|
|
92
103
|
}
|
|
93
104
|
catch (error) {
|
|
94
105
|
return {
|
|
106
|
+
ok: false,
|
|
95
107
|
content: [
|
|
96
108
|
{
|
|
97
109
|
type: 'text',
|
|
@@ -129,7 +141,7 @@ export class ComplexityHandler {
|
|
|
129
141
|
/**
|
|
130
142
|
* Format complexity response
|
|
131
143
|
*/
|
|
132
|
-
formatComplexityResponse(analysis, recommendations, taskCount, leadDevPlan = null) {
|
|
144
|
+
formatComplexityResponse(analysis, recommendations, taskCount, leadDevPlan = null, synthesisCheckpoint = false) {
|
|
133
145
|
const leadDevSection = leadDevPlan
|
|
134
146
|
? `
|
|
135
147
|
|
|
@@ -146,7 +158,10 @@ ${leadDevPlan.phases
|
|
|
146
158
|
${JSON.stringify(leadDevPlan, null, 2)}
|
|
147
159
|
\`\`\``
|
|
148
160
|
: '';
|
|
149
|
-
|
|
161
|
+
const header = synthesisCheckpoint
|
|
162
|
+
? '🔄 Synthesis Checkpoint — Reflect & Realign'
|
|
163
|
+
: '🔍 Complexity Analysis Results';
|
|
164
|
+
return `${header}
|
|
150
165
|
|
|
151
166
|
**Tasks Analyzed:** ${taskCount}
|
|
152
167
|
**Overall Complexity:** ${analysis.overallComplexity}/100
|
|
@@ -105,10 +105,14 @@ export class OrchestratorServer {
|
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
name: 'analyze-complexity',
|
|
108
|
-
description: 'Analyze task complexity and recommend orchestration strategy. When lead_dev_mode is on (features.json), includes phased plan + todos + subagent routes.',
|
|
108
|
+
description: 'Analyze task complexity and recommend orchestration strategy. When lead_dev_mode is on (features.json), includes phased plan + todos + subagent routes. During synthesis checkpoint, returns collocated context and mandatory-consult realignment plan — pass sessionId from the active session.',
|
|
109
109
|
inputSchema: {
|
|
110
110
|
type: 'object',
|
|
111
111
|
properties: {
|
|
112
|
+
sessionId: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Active session id (required to clear synthesis checkpoint; inferred from checkpoint state when omitted)',
|
|
115
|
+
},
|
|
112
116
|
tasks: {
|
|
113
117
|
type: 'array',
|
|
114
118
|
items: {
|
|
@@ -229,11 +233,97 @@ export class OrchestratorServer {
|
|
|
229
233
|
}
|
|
230
234
|
case 'analyze-complexity': {
|
|
231
235
|
const complexityArgs = args;
|
|
236
|
+
const projectRoot = process.cwd();
|
|
237
|
+
const { isSynthesisCheckpointDue, getSynthesisDueReason, getSynthesisCheckpointSessionId, } = await import('../../nucleus/synthesis.js');
|
|
238
|
+
const { appendSynthesisContextToResponse, prepareSynthesisCollocatedContext } = await import('../../nucleus/synthesis-context.js');
|
|
239
|
+
const { loadPersistedLeadDevPlan, isSynthesisRealignmentPlan, areSynthesisConsultTodosComplete, getSynthesisConsultTodos, getNextRequiredTodo, } = await import('../../nucleus/lead-dev-plan-persistence.js');
|
|
240
|
+
const checkpointSession = getSynthesisCheckpointSessionId(projectRoot);
|
|
241
|
+
if (checkpointSession &&
|
|
242
|
+
complexityArgs.sessionId &&
|
|
243
|
+
complexityArgs.sessionId !== checkpointSession) {
|
|
244
|
+
return {
|
|
245
|
+
content: [
|
|
246
|
+
{
|
|
247
|
+
type: 'text',
|
|
248
|
+
text: `❌ Synthesis sessionId mismatch — expected \`${checkpointSession}\` ` +
|
|
249
|
+
`(from synthesis-checkpoint.json), got \`${complexityArgs.sessionId}\`.`,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
const sessionId = checkpointSession ?? complexityArgs.sessionId ?? null;
|
|
255
|
+
const synthesisDue = checkpointSession
|
|
256
|
+
? isSynthesisCheckpointDue(projectRoot, checkpointSession)
|
|
257
|
+
: false;
|
|
258
|
+
const dueReason = synthesisDue
|
|
259
|
+
? getSynthesisDueReason(projectRoot, sessionId)
|
|
260
|
+
: null;
|
|
261
|
+
if (synthesisDue && !sessionId) {
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: 'text',
|
|
266
|
+
text: '❌ Synthesis checkpoint due but sessionId could not be resolved. ' +
|
|
267
|
+
'Pass sessionId matching .xray/state/synthesis-checkpoint.json.',
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const existingPlan = loadPersistedLeadDevPlan(projectRoot);
|
|
273
|
+
const realignmentInProgress = synthesisDue &&
|
|
274
|
+
existingPlan &&
|
|
275
|
+
isSynthesisRealignmentPlan(existingPlan) &&
|
|
276
|
+
!areSynthesisConsultTodosComplete(existingPlan);
|
|
277
|
+
if (realignmentInProgress) {
|
|
278
|
+
const collocated = await prepareSynthesisCollocatedContext(projectRoot, dueReason);
|
|
279
|
+
const nextTodo = getNextRequiredTodo(existingPlan);
|
|
280
|
+
const consultTodos = getSynthesisConsultTodos(existingPlan);
|
|
281
|
+
const todoList = consultTodos
|
|
282
|
+
.map((t) => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.id} (${t.subagent}): ${t.task}`)
|
|
283
|
+
.join('\n');
|
|
284
|
+
let text = '🔄 Synthesis realignment in progress — consult todos preserved (analyze-complexity no-op).\n\n' +
|
|
285
|
+
`**Next required todo:** ${nextTodo?.id ?? 'none'} (${nextTodo?.subagent ?? ''})\n\n` +
|
|
286
|
+
`${todoList}\n\n` +
|
|
287
|
+
'Complete mandatory consult todos via Task spawns before other gated work.';
|
|
288
|
+
if (collocated.collatedText) {
|
|
289
|
+
text = appendSynthesisContextToResponse([{ type: 'text', text }], collocated.collatedText)[0].text;
|
|
290
|
+
}
|
|
291
|
+
return { content: [{ type: 'text', text }] };
|
|
292
|
+
}
|
|
293
|
+
const collocated = synthesisDue
|
|
294
|
+
? await prepareSynthesisCollocatedContext(projectRoot, dueReason)
|
|
295
|
+
: null;
|
|
296
|
+
const inheritedContext = synthesisDue && collocated
|
|
297
|
+
? { synthesis: true, ...collocated }
|
|
298
|
+
: { taskCount: complexityArgs.tasks.length };
|
|
232
299
|
const aside = await spawnAside({
|
|
233
|
-
description:
|
|
300
|
+
description: synthesisDue
|
|
301
|
+
? 'Synthesis checkpoint: reflect & realign'
|
|
302
|
+
: `Complexity analysis: ${complexityArgs.tasks.length} tasks`,
|
|
303
|
+
...(sessionId ? { sessionId } : {}),
|
|
304
|
+
inheritedContext,
|
|
234
305
|
});
|
|
235
306
|
try {
|
|
236
|
-
|
|
307
|
+
const result = await this.complexityHandler.handleAnalyzeComplexity({
|
|
308
|
+
...complexityArgs,
|
|
309
|
+
...(sessionId ? { sessionId } : {}),
|
|
310
|
+
...(synthesisDue
|
|
311
|
+
? { synthesisCheckpoint: true, synthesisDueReason: dueReason }
|
|
312
|
+
: {}),
|
|
313
|
+
}, aside.asideId);
|
|
314
|
+
let content = result.content;
|
|
315
|
+
if (synthesisDue && collocated?.collatedText) {
|
|
316
|
+
content = appendSynthesisContextToResponse(content, collocated.collatedText);
|
|
317
|
+
}
|
|
318
|
+
if (synthesisDue && result.ok) {
|
|
319
|
+
content = content.map((block, index) => index === 0
|
|
320
|
+
? {
|
|
321
|
+
...block,
|
|
322
|
+
text: `${block.text}\n\n**Synthesis checkpoint remains active** until mandatory consult todos (s.1–s.3) are completed via Task spawns.`,
|
|
323
|
+
}
|
|
324
|
+
: block);
|
|
325
|
+
}
|
|
326
|
+
return { content };
|
|
237
327
|
}
|
|
238
328
|
finally {
|
|
239
329
|
closeAside(aside.asideId);
|
|
@@ -281,12 +371,38 @@ export class OrchestratorServer {
|
|
|
281
371
|
}
|
|
282
372
|
async handleGovernAndApply(args) {
|
|
283
373
|
try {
|
|
374
|
+
const projectRoot = process.cwd();
|
|
375
|
+
const { isSynthesisCheckpointDue, getSynthesisDueReason, getSynthesisCheckpointSessionId, recordExecutionSlice, } = await import('../../nucleus/synthesis.js');
|
|
376
|
+
const { loadPersistedLeadDevPlan, isSynthesisRealignmentPlan, areSynthesisConsultTodosComplete, } = await import('../../nucleus/lead-dev-plan-persistence.js');
|
|
377
|
+
const checkpointSession = getSynthesisCheckpointSessionId(projectRoot);
|
|
378
|
+
const sessionId = checkpointSession ?? null;
|
|
379
|
+
if (sessionId && isSynthesisCheckpointDue(projectRoot, sessionId)) {
|
|
380
|
+
const dueReason = getSynthesisDueReason(projectRoot, sessionId);
|
|
381
|
+
const plan = loadPersistedLeadDevPlan(projectRoot);
|
|
382
|
+
const realignmentPending = plan && isSynthesisRealignmentPlan(plan) && !areSynthesisConsultTodosComplete(plan);
|
|
383
|
+
return {
|
|
384
|
+
content: [{
|
|
385
|
+
type: 'text',
|
|
386
|
+
text: JSON.stringify({
|
|
387
|
+
blocked: true,
|
|
388
|
+
gate: 'synthesis-checkpoint',
|
|
389
|
+
dueReason,
|
|
390
|
+
primitive: 'synthesis',
|
|
391
|
+
realignmentPending,
|
|
392
|
+
hint: realignmentPending
|
|
393
|
+
? 'Complete mandatory consult todos (s.1–s.3) before govern-and-apply'
|
|
394
|
+
: 'Run analyze-complexity to reflect and realign before govern-and-apply',
|
|
395
|
+
}, null, 2),
|
|
396
|
+
}],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
284
399
|
const { InferenceCycle } = await import('../../inference/inference-cycle.js');
|
|
285
|
-
const cycle = InferenceCycle.getInstance(
|
|
400
|
+
const cycle = InferenceCycle.getInstance(projectRoot, {
|
|
286
401
|
skipApply: args.skipApply ?? false,
|
|
287
402
|
});
|
|
288
403
|
const aside = await spawnAside({
|
|
289
404
|
description: `Governance cycle: ${args.proposals.length} proposals`,
|
|
405
|
+
...(sessionId ? { sessionId } : {}),
|
|
290
406
|
priorVerdictContext: { proposalCount: args.proposals.length },
|
|
291
407
|
});
|
|
292
408
|
let result;
|
|
@@ -299,12 +415,16 @@ export class OrchestratorServer {
|
|
|
299
415
|
}
|
|
300
416
|
const approved = result.votes.filter((v) => v.decision === 'approve').length;
|
|
301
417
|
const rejected = result.votes.length - approved;
|
|
418
|
+
if (sessionId) {
|
|
419
|
+
recordExecutionSlice('gate', { projectRoot, sessionId });
|
|
420
|
+
}
|
|
302
421
|
return {
|
|
303
422
|
content: [{
|
|
304
423
|
type: 'text',
|
|
305
424
|
text: JSON.stringify({
|
|
306
425
|
cycleId: result.cycleId,
|
|
307
426
|
asideId: aside.asideId,
|
|
427
|
+
synthesisBridged: Boolean(sessionId),
|
|
308
428
|
approved,
|
|
309
429
|
rejected,
|
|
310
430
|
votes: result.votes,
|
|
@@ -95,6 +95,13 @@ export interface MemoryRoutingProvider {
|
|
|
95
95
|
resolveThinDispatch(baseAgent: string, operation: string, complexityScore: number): MemoryThinDispatchResult;
|
|
96
96
|
getTaskConfidence?(task: MemoryOrchestrationTask): MemoryTaskConfidence;
|
|
97
97
|
ingestFeedback?(entry: OrchestratorFeedbackEntry): void;
|
|
98
|
+
buildSynthesisContext?(opts: {
|
|
99
|
+
projectRoot: string;
|
|
100
|
+
dueReason?: string | null;
|
|
101
|
+
}): Record<string, unknown> | null;
|
|
102
|
+
refreshMetaInference?(): Promise<{
|
|
103
|
+
refreshed: boolean;
|
|
104
|
+
}>;
|
|
98
105
|
}
|
|
99
106
|
export interface MemoryRoutingConfig {
|
|
100
107
|
enabled: boolean;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Config: features.json → multi_agent_orchestration (lead_dev_mode), NOT a separate surface.
|
|
4
4
|
*/
|
|
5
5
|
export declare const LEAD_DEV_RULES: readonly ["Phased plan + detailed todos; assign best subagent; monitor output; iterate fully", "Lead dev takes helm — loop test fix until complete; no permission pings", "Per-suite test triage after major changes; full suite only when individuals pass", "Lead stays main thread; subagents execute; update todos continuously", "Read all console and test output; triage fix rerun", "Never defer errors as pre-existing — add todo and resolve", "Resolve all errors before phase completion"];
|
|
6
|
+
export declare const SYNTHESIS_REALIGNMENT_PHASE_ID = "phase-synthesis";
|
|
6
7
|
export declare const MANDATORY_MAJOR_CONSULTS: readonly ["researcher", "architect-tools", "code-review"];
|
|
7
8
|
export type SubagentRoute = 'strategist' | 'architect-tools' | 'researcher' | 'backend-engineer' | 'frontend-engineer' | 'bug-triage' | 'code-review' | 'security-audit' | 'enforcer';
|
|
8
9
|
export interface LeadDevTodo {
|
|
@@ -41,6 +42,8 @@ export interface LeadDevPlan {
|
|
|
41
42
|
export declare function isLeadDevModeActive(): boolean;
|
|
42
43
|
export declare function routeSubagent(taskType: string): SubagentRoute;
|
|
43
44
|
export declare function shouldFlagFullTestSuite(command: string): boolean;
|
|
45
|
+
/** Mandatory-consult plan for synthesis checkpoint realignment (no implementation phase). */
|
|
46
|
+
export declare function buildSynthesisCheckpointPlan(dueReason: string | null): LeadDevPlan | null;
|
|
44
47
|
export declare function buildLeadDevPlan(description: string, taskTypes?: string[], taskInputs?: LeadDevPlanTaskInput[], mcpOverallComplexity?: number): LeadDevPlan | null;
|
|
45
48
|
export declare function buildSessionBootContext(): Record<string, unknown>;
|
|
46
49
|
/** Persist plan for PreToolUse spawn_subagent gate */
|
|
@@ -15,6 +15,7 @@ export const LEAD_DEV_RULES = [
|
|
|
15
15
|
'Never defer errors as pre-existing — add todo and resolve',
|
|
16
16
|
'Resolve all errors before phase completion',
|
|
17
17
|
];
|
|
18
|
+
export const SYNTHESIS_REALIGNMENT_PHASE_ID = 'phase-synthesis';
|
|
18
19
|
export const MANDATORY_MAJOR_CONSULTS = [
|
|
19
20
|
'researcher',
|
|
20
21
|
'architect-tools',
|
|
@@ -73,6 +74,45 @@ function buildImplementationTodos(description, taskTypes, taskInputs, idPrefix)
|
|
|
73
74
|
status: 'pending',
|
|
74
75
|
}));
|
|
75
76
|
}
|
|
77
|
+
/** Mandatory-consult plan for synthesis checkpoint realignment (no implementation phase). */
|
|
78
|
+
export function buildSynthesisCheckpointPlan(dueReason) {
|
|
79
|
+
if (!isLeadDevModeActive())
|
|
80
|
+
return null;
|
|
81
|
+
const cfg = orchestrationConfig();
|
|
82
|
+
const mandatoryConsults = cfg.auto_consult_major_work !== false ? [...MANDATORY_MAJOR_CONSULTS] : [];
|
|
83
|
+
if (mandatoryConsults.length === 0)
|
|
84
|
+
return null;
|
|
85
|
+
const reasonSuffix = dueReason ? ` (${dueReason})` : '';
|
|
86
|
+
return {
|
|
87
|
+
active: true,
|
|
88
|
+
rules: LEAD_DEV_RULES,
|
|
89
|
+
codexTerms: [59, 67, 68, 69],
|
|
90
|
+
description: `Synthesis checkpoint: reflect and realign${reasonSuffix}`,
|
|
91
|
+
complexity: 30,
|
|
92
|
+
requiresPhasedPlan: true,
|
|
93
|
+
recommendedStrategy: 'sequential',
|
|
94
|
+
mandatoryConsults,
|
|
95
|
+
phases: [
|
|
96
|
+
{
|
|
97
|
+
id: SYNTHESIS_REALIGNMENT_PHASE_ID,
|
|
98
|
+
name: 'Reflect & realign',
|
|
99
|
+
goal: 'Review collocated context and mandatory consults before resuming execution',
|
|
100
|
+
definitionOfDone: 'researcher + architect-tools + code-review consulted; todos realigned',
|
|
101
|
+
todos: mandatoryConsults.map((subagent, i) => ({
|
|
102
|
+
id: `s.${i + 1}`,
|
|
103
|
+
task: `Synthesis consult ${subagent}: review checkpoint context and realign plan`,
|
|
104
|
+
subagent: subagent,
|
|
105
|
+
status: 'pending',
|
|
106
|
+
})),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
testProtocol: {
|
|
110
|
+
perSuiteFirst: cfg.per_suite_test_triage !== false,
|
|
111
|
+
fullSuiteGate: false,
|
|
112
|
+
hint: 'Synthesis checkpoint — consult mandatory agents before resuming gated work',
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
76
116
|
export function buildLeadDevPlan(description, taskTypes = ['implement'], taskInputs = [], mcpOverallComplexity) {
|
|
77
117
|
if (!isLeadDevModeActive())
|
|
78
118
|
return null;
|