0xray 4.0.41 → 4.0.42

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 (115) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.opencode/skills/orchestrator/SKILL.md +12 -0
  3. package/.xray/features.json +1 -1
  4. package/.xray/features.schema.json +1 -1
  5. package/AGENTS-consumer.md +2 -0
  6. package/AGENTS.md +2 -0
  7. package/README.md +52 -12
  8. package/SKILLS.md +2 -0
  9. package/dist/AGENTS.md +2 -0
  10. package/dist/CHANGELOG.md +13 -0
  11. package/dist/README.md +52 -12
  12. package/dist/SKILLS.md +2 -0
  13. package/dist/cli/commands/openclaw-install.js +1 -1
  14. package/dist/cli/index.js +12 -0
  15. package/dist/enforcement/index.d.ts +4 -0
  16. package/dist/enforcement/index.js +4 -0
  17. package/dist/enforcement/validators/code-quality-validators.js +6 -4
  18. package/dist/governance/llm-governance-provider.d.ts +7 -1
  19. package/dist/governance/llm-governance-provider.js +72 -14
  20. package/dist/governance/local-confer.d.ts +4 -1
  21. package/dist/governance/local-confer.js +22 -1
  22. package/dist/integrations/grok/grok-cli.js +10 -37
  23. package/dist/integrations/grok/hooks/grok-hook-utils.js +8 -1
  24. package/dist/integrations/hooks/confer-hook-runtime.mjs +23 -1
  25. package/dist/integrations/hooks/goggles-planes.json +4 -1
  26. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  27. package/dist/mcps/architect-tools.server.d.ts +6 -0
  28. package/dist/mcps/architect-tools.server.js +19 -2
  29. package/dist/mcps/config/server-config-registry.js +8 -6
  30. package/dist/mcps/governance.server.d.ts +1 -0
  31. package/dist/mcps/governance.server.js +46 -0
  32. package/dist/mcps/knowledge-skills/code-review.server.js +7 -0
  33. package/dist/mcps/knowledge-skills/security-audit.server.js +5 -3
  34. package/dist/mcps/mcp-client.js +1 -1
  35. package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
  36. package/dist/mcps/researcher.server.js +12 -3
  37. package/dist/nucleus/autonomy-kernel.d.ts +2 -0
  38. package/dist/nucleus/autonomy-kernel.js +17 -13
  39. package/dist/nucleus/confer-host.d.ts +35 -0
  40. package/dist/nucleus/confer-host.js +55 -0
  41. package/dist/nucleus/confer.d.ts +19 -3
  42. package/dist/nucleus/confer.js +111 -55
  43. package/dist/nucleus/lead-dev-plan-persistence.js +4 -2
  44. package/dist/nucleus/suit-temperament.d.ts +1 -1
  45. package/dist/nucleus/suit-temperament.js +2 -2
  46. package/dist/nucleus/synthesis-consult-receipt.d.ts +7 -1
  47. package/dist/nucleus/synthesis-consult-receipt.js +86 -35
  48. package/dist/reporting/framework-reporting-system.js +2 -5
  49. package/dist/reporting/log-parser.d.ts +2 -0
  50. package/dist/reporting/log-parser.js +7 -6
  51. package/dist/skills/orchestrator/SKILL.md +12 -0
  52. package/docs-site/docs/plates/activity-log.md +1 -1
  53. package/docs-site/docs/plates/boot.md +1 -1
  54. package/docs-site/docs/plates/centrifuge.md +1 -1
  55. package/docs-site/docs/plates/cursor-compact.md +1 -1
  56. package/docs-site/docs/plates/glossary.md +1 -1
  57. package/docs-site/docs/plates/goggles.md +1 -1
  58. package/docs-site/docs/plates/governance.md +1 -1
  59. package/docs-site/docs/plates/grok-compact.md +1 -1
  60. package/docs-site/docs/plates/grokbot.md +1 -1
  61. package/docs-site/docs/plates/host-pack.md +1 -1
  62. package/docs-site/docs/plates/house.md +1 -1
  63. package/docs-site/docs/plates/index.md +1 -1
  64. package/docs-site/docs/plates/inference-files.md +1 -1
  65. package/docs-site/docs/plates/kept-line.md +1 -1
  66. package/docs-site/docs/plates/kits.md +1 -1
  67. package/docs-site/docs/plates/lens-gate.md +1 -1
  68. package/docs-site/docs/plates/lens-page.md +1 -1
  69. package/docs-site/docs/plates/live-mesh.md +1 -1
  70. package/docs-site/docs/plates/memory-recall.md +1 -1
  71. package/docs-site/docs/plates/notes-page.md +1 -1
  72. package/docs-site/docs/plates/orchestration.md +1 -1
  73. package/docs-site/docs/plates/package-face.md +1 -1
  74. package/docs-site/docs/plates/payload-heat.md +1 -1
  75. package/docs-site/docs/plates/pickup-stamp.md +1 -1
  76. package/docs-site/docs/plates/pre-tool.md +1 -1
  77. package/docs-site/docs/plates/processor.md +1 -1
  78. package/docs-site/docs/plates/record-map.md +1 -1
  79. package/docs-site/docs/plates/reflection-page.md +1 -1
  80. package/docs-site/docs/plates/reporting.md +1 -1
  81. package/docs-site/docs/plates/review.md +1 -1
  82. package/docs-site/docs/plates/routing.md +1 -1
  83. package/docs-site/docs/plates/session-capture.md +1 -1
  84. package/docs-site/docs/plates/site-manual.md +1 -1
  85. package/docs-site/docs/plates/stamp-plate.md +1 -1
  86. package/docs-site/docs/plates/station-card.md +1 -1
  87. package/docs-site/docs/plates/station-heat.md +1 -1
  88. package/docs-site/docs/plates/suit-organs.md +1 -1
  89. package/docs-site/docs/plates/suit-settings.md +1 -1
  90. package/docs-site/docs/plates/suit-wear.md +1 -1
  91. package/docs-site/docs/plates/suit.md +1 -1
  92. package/docs-site/docs/plates/trail-state.md +1 -1
  93. package/docs-site/docs/plates/work-fresh.md +1 -1
  94. package/docs-site/docs/plates/write-home.md +1 -1
  95. package/llms.txt +2 -0
  96. package/package.json +2 -2
  97. package/scripts/mjs/verify-confer-core.mjs +153 -29
  98. package/scripts/mjs/verify-user-aside-core.mjs +3 -4
  99. package/scripts/node/bridge-mcp-wiring.cjs +236 -5
  100. package/scripts/node/confer-switch.cjs +66 -0
  101. package/scripts/node/install-bridges.cjs +16 -41
  102. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  103. package/src/mcps/architect-tools.server.ts +26 -2
  104. package/src/mcps/config/__tests__/server-config-registry.test.ts +15 -6
  105. package/src/mcps/config/server-config-registry.ts +9 -6
  106. package/src/mcps/governance.server.ts +49 -0
  107. package/src/mcps/knowledge-skills/code-review.server.ts +8 -0
  108. package/src/mcps/knowledge-skills/security-audit.server.ts +11 -12
  109. package/src/mcps/mcp-client.ts +1 -1
  110. package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
  111. package/src/mcps/researcher.server.ts +19 -10
  112. package/src/skills/orchestrator/SKILL.md +12 -0
  113. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +5 -51
  114. package/xray/features.json +1 -1
  115. package/xray/features.schema.json +1 -1
@@ -1,10 +1,13 @@
1
- import type { GovernanceRole, GovernanceVote } from "./llm-governance-provider.js";
1
+ import type { GovernanceMiss, GovernanceRole, GovernanceVote } from "./llm-governance-provider.js";
2
2
  export type LocalConferInput = {
3
3
  role: GovernanceRole;
4
4
  highConfidenceTrapPresent?: boolean | undefined;
5
5
  recommendedAgent?: string | null | undefined;
6
6
  llmConfigured?: boolean | undefined;
7
+ miss?: GovernanceMiss | undefined;
7
8
  };
8
9
  /** Receipt when nested LLM is absent or returned no vote. Never approve — analyze_proposal feeds mergeVotes. */
9
10
  export declare function localConferVote(input: LocalConferInput): GovernanceVote;
11
+ /** Stop a model from planting a second DECISION or Verdict line inside the reasoning body. */
12
+ export declare function sanitizeGovernanceReasoning(reasoning: string): string;
10
13
  export declare function formatGovernanceVoteText(vote: GovernanceVote, extra?: string): string;
@@ -1,6 +1,13 @@
1
1
  /** Receipt when nested LLM is absent or returned no vote. Never approve — analyze_proposal feeds mergeVotes. */
2
2
  export function localConferVote(input) {
3
3
  const role = input.role;
4
+ if (input.miss && input.miss !== "not-configured") {
5
+ return {
6
+ decision: "abstain",
7
+ confidence: 0.5,
8
+ reasoning: `Local confer (${role}); nested LLM did not produce a vote (${input.miss}).`,
9
+ };
10
+ }
4
11
  if (input.llmConfigured) {
5
12
  return {
6
13
  decision: "abstain",
@@ -22,6 +29,20 @@ export function localConferVote(input) {
22
29
  reasoning: `Local confer (${role}); nested LLM not configured.`,
23
30
  };
24
31
  }
32
+ function abstainedWithoutUsableVote(vote) {
33
+ if (vote.decision !== "abstain")
34
+ return false;
35
+ return /nested LLM not configured|did not produce a vote|returned no vote/i.test(vote.reasoning);
36
+ }
37
+ /** Stop a model from planting a second DECISION or Verdict line inside the reasoning body. */
38
+ export function sanitizeGovernanceReasoning(reasoning) {
39
+ return reasoning
40
+ .split("\n")
41
+ .map((line) => /^\s*(?:[-*•]\s*)?(?:Verdict:|DECISION:)/i.test(line) ? `> ${line.trim()}` : line)
42
+ .join("\n");
43
+ }
25
44
  export function formatGovernanceVoteText(vote, extra = "") {
26
- return `DECISION: ${vote.decision}\nCONFIDENCE: ${vote.confidence.toFixed(2)}\nREASONING: ${vote.reasoning}${extra}`;
45
+ const reviewLine = abstainedWithoutUsableVote(vote) ? "\nVerdict: UNREVIEWED" : "";
46
+ const reasoning = sanitizeGovernanceReasoning(vote.reasoning);
47
+ return `DECISION: ${vote.decision}\nCONFIDENCE: ${vote.confidence.toFixed(2)}\nREASONING: ${reasoning}${reviewLine}${extra}`;
27
48
  }
@@ -17,7 +17,6 @@
17
17
  import { frameworkLogger } from '../../core/framework-logger.js';
18
18
  import fs from 'fs';
19
19
  import path from 'path';
20
- import { execSync } from 'child_process';
21
20
  import { createRequire } from 'module';
22
21
  import { fileURLToPath } from 'node:url';
23
22
  import { syncBuiltinSkills } from '../../cli/commands/skill-install.js';
@@ -27,27 +26,13 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
27
26
  const packageRoot = path.resolve(__dirname, '..', '..', '..');
28
27
  const requireCjs = createRequire(import.meta.url);
29
28
  const { resolveConsumerTargetDir, patchGrokHooks } = requireCjs(path.join(packageRoot, 'scripts/node/install-bridges.cjs'));
30
- const { XRAY_MCP_SERVERS, resolveRepertoireMcp, resolveGogglesMcp } = requireCjs(path.join(packageRoot, 'scripts/node/bridge-mcp-wiring.cjs'));
31
- function registerGrokMcpServers(targetDir) {
32
- try {
33
- execSync('which grok', { stdio: 'ignore' });
34
- }
35
- catch {
36
- console.log('[Grok] grok CLI not on PATH — plugin .mcp.json still configured');
37
- return;
38
- }
39
- for (const s of XRAY_MCP_SERVERS) {
40
- try {
41
- const envEntries = { ...s.env, XRAY_ROOT: targetDir };
42
- const envFlags = Object.entries(envEntries)
43
- .map(([k, v]) => `--env "${k}=${v}"`)
44
- .join(' ');
45
- execSync(`grok mcp add ${s.name} --command npx --args "-y" "0xray" "mcp" "${s.mcpCmd}" ${envFlags}`, { stdio: 'pipe' });
46
- }
47
- catch {
48
- // already registered or grok config conflict — non-blocking
49
- }
50
- }
29
+ const { resolveRepertoireMcp, resolveGogglesMcp } = requireCjs(path.join(packageRoot, 'scripts/node/bridge-mcp-wiring.cjs'));
30
+ function registerGrokMcpServers(targetDir, env) {
31
+ const wiring = requireCjs(path.join(packageRoot, 'scripts/node/bridge-mcp-wiring.cjs'));
32
+ const tomlPath = wiring.writeProjectSuitMcp(targetDir);
33
+ const trustPath = wiring.trustGrokFolder(targetDir, env);
34
+ process.stdout.write(`Project Grok MCP written to ${tomlPath}\n`);
35
+ process.stdout.write(`Grok folder trust written to ${trustPath}\n`);
51
36
  }
52
37
  export async function installForGrokCLI(options = {}) {
53
38
  frameworkLogger.log('grok-integration', 'install-start', 'info', {
@@ -115,21 +100,9 @@ export async function installForGrokCLI(options = {}) {
115
100
  writeProjectRepertoireMcp(targetDir);
116
101
  writeProjectGogglesMcp(targetDir);
117
102
  mintAfterWear(targetDir);
118
- const primary = dests[0];
119
- try {
120
- execSync(`grok plugins trust "${primary}"`, { stdio: 'ignore' });
121
- console.log('\x1b[32m✓ Auto-trusted the 0xray plugin with Grok CLI\x1b[0m');
122
- }
123
- catch {
124
- console.log('\nPlease run this command to fully trust the plugin:');
125
- console.log(` grok plugins trust "${primary}"`);
126
- }
127
- if (isolated) {
128
- registerGrokMcpServers(targetDir);
129
- console.log('\x1b[32m✓ Registered 7 xray MCP servers with Grok CLI (npx)\x1b[0m');
130
- }
131
- else {
132
- console.log('\x1b[32m✓ Project-scoped Grok plugin worn (machine ~/.grok/plugins/0xray not written)\x1b[0m');
103
+ registerGrokMcpServers(targetDir, env);
104
+ if (!isolated) {
105
+ process.stdout.write('Project-scoped Grok plugin worn (machine ~/.grok/plugins/0xray not written)\n');
133
106
  }
134
107
  console.log('\n✅ 0xRay is now installed as a first-class Grok CLI plugin!');
135
108
  console.log('Restart Grok or run `grok` to load the new hooks and MCP servers.');
@@ -19,7 +19,7 @@ import {
19
19
  updatePlanTodoStatusInPlace,
20
20
  loadDelegationGateFeatures,
21
21
  } from '../../hooks/delegation-gate-runtime.mjs';
22
- import { isConferPendingForSession } from '../../hooks/confer-hook-runtime.mjs';
22
+ import { isConferPendingForSession, readConferReviewHint } from '../../hooks/confer-hook-runtime.mjs';
23
23
  import { getActiveUserAsideBoot } from '../../hooks/user-aside-hook-runtime.mjs';
24
24
  import {
25
25
  applyStationHeat,
@@ -308,6 +308,7 @@ export function buildSessionBootPayload(root, source = '0xray/grok-session-start
308
308
  const sessionId =
309
309
  extra.sessionId || process.env.GROK_SESSION_ID || process.env.GROK_SESSION || null;
310
310
  const conferPending = loadConferPending(root, sessionId);
311
+ const conferReview = readConferReviewHint(root);
311
312
  const userAsideBoot = getActiveUserAsideBoot(root, sessionId);
312
313
  const gateFeatures = loadDelegationGateFeatures(root, 'grok');
313
314
  const frontier = gateFeatures.ceremony === 'lite';
@@ -332,6 +333,12 @@ export function buildSessionBootPayload(root, source = '0xray/grok-session-start
332
333
  ...heat,
333
334
  ...(siblingRoots.length > 0 ? { siblingWorkspaceRoots: siblingRoots } : {}),
334
335
  ...(conferPending ? { conferPending: true, conferTrigger: 'analyze-complexity at synthesis checkpoint' } : {}),
336
+ ...(conferReview
337
+ ? {
338
+ conferReview,
339
+ conferReviewHint: 'UNREVIEWED — abstain without a governance LLM blocks the consult todo',
340
+ }
341
+ : {}),
335
342
  ...(userAsideBoot ?? {}),
336
343
  sessionId,
337
344
  ...extra,
@@ -3,7 +3,7 @@
3
3
  * Lazy-load confer SSOT for Grok session-boot hints.
4
4
  */
5
5
  import { createRequire } from 'node:module';
6
- import { existsSync } from 'node:fs';
6
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
7
7
  import { dirname, join, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
 
@@ -46,4 +46,26 @@ export function isConferPendingForSession(projectRoot, sessionId = null) {
46
46
  const mod = loadConfer();
47
47
  if (!mod?.isConferPending) return false;
48
48
  return mod.isConferPending(projectRoot, sessionId);
49
+ }
50
+
51
+ /** 'UNREVIEWED' when a consult receipt recorded abstain-without-model. */
52
+ export function readConferReviewHint(projectRoot) {
53
+ const stateDir = join(projectRoot, '.xray', 'state');
54
+ if (!existsSync(stateDir)) return null;
55
+ let names;
56
+ try {
57
+ names = readdirSync(stateDir);
58
+ } catch {
59
+ return null;
60
+ }
61
+ for (const name of names) {
62
+ if (!name.startsWith('synthesis-consult-') || !name.endsWith('.json')) continue;
63
+ try {
64
+ const receipt = JSON.parse(readFileSync(join(stateDir, name), 'utf8'));
65
+ if (receipt && receipt.verdict === 'UNREVIEWED') return 'UNREVIEWED';
66
+ } catch {
67
+ /* unreadable receipt is not a hint */
68
+ }
69
+ }
70
+ return null;
49
71
  }
@@ -38,7 +38,10 @@
38
38
  "plates": ["suit-wear"]
39
39
  },
40
40
  "governance": {
41
- "files": ["src/governance/governance-service.ts"],
41
+ "files": [
42
+ "src/governance/governance-service.ts",
43
+ "src/governance/llm-governance-provider.ts"
44
+ ],
42
45
  "skills": ".agents/skills/governance/SKILL.md"
43
46
  },
44
47
  "memory-recall": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xray-pre-tool",
3
- "version": "4.0.41",
3
+ "version": "4.0.42",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "0xRay constitution + temperament PreToolUse for OpenClaw",
@@ -5,6 +5,12 @@
5
5
  * Provides contextual analysis capabilities via MCP protocol
6
6
  */
7
7
  import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
8
+ import { type GovernanceVote } from "../governance/llm-governance-provider.js";
9
+ import { type ArchitectureAssessment } from "../architect/architect-tools.js";
10
+ export type ConferModelVerdict = "PASS" | "CONDITIONAL" | "FAIL" | "UNREVIEWED";
11
+ /** A confer verdict counts only when a model vote exists. Metrics alone are UNREVIEWED. */
12
+ export declare function conferVerdictFromModelVote(vote: Pick<GovernanceVote, "decision"> | null): ConferModelVerdict;
13
+ export declare function formatArchitectureAssessmentConferText(conferPrompt: string, assessment: ArchitectureAssessment, verdict?: ConferModelVerdict): string;
8
14
  export declare function renderArchitectureAssessment(args: Record<string, unknown> | undefined): Promise<{
9
15
  content: Array<{
10
16
  type: "text";
@@ -8,7 +8,24 @@ import { CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
8
8
  import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
9
9
  import * as fs from "fs";
10
10
  import { frameworkLogger } from "../core/framework-logger.js";
11
- import { contextAnalysis as architectContextAnalysis, codebaseStructure as architectCodebaseStructure, dependencyAnalysis as architectDependencyAnalysis, architectureAssessment as architectArchitectureAssessment, formatConferArchitectureText, } from "../architect/architect-tools.js";
11
+ import { contextAnalysis as architectContextAnalysis, codebaseStructure as architectCodebaseStructure, dependencyAnalysis as architectDependencyAnalysis, architectureAssessment as architectArchitectureAssessment, } from "../architect/architect-tools.js";
12
+ /** A confer verdict counts only when a model vote exists. Metrics alone are UNREVIEWED. */
13
+ export function conferVerdictFromModelVote(vote) {
14
+ if (!vote)
15
+ return "UNREVIEWED";
16
+ if (vote.decision === "reject")
17
+ return "FAIL";
18
+ if (vote.decision === "approve")
19
+ return "PASS";
20
+ return "CONDITIONAL";
21
+ }
22
+ export function formatArchitectureAssessmentConferText(conferPrompt, assessment, verdict = "UNREVIEWED") {
23
+ const assessmentJson = JSON.stringify(assessment, null, 2);
24
+ const trimmed = conferPrompt.trim();
25
+ if (!trimmed)
26
+ return assessmentJson;
27
+ return `${trimmed}\n\n## Architecture assessment\n${assessmentJson}\n\nVerdict: ${verdict}\nTop risks: review assessment metrics above\nHardening: address high-complexity or coupling findings before resuming`;
28
+ }
12
29
  export async function renderArchitectureAssessment(args) {
13
30
  const projectRoot = typeof args?.projectRoot === "string" ? args.projectRoot : "";
14
31
  const assessmentType = args?.assessmentType === "quick" || args?.assessmentType === "comprehensive"
@@ -21,7 +38,7 @@ export async function renderArchitectureAssessment(args) {
21
38
  content: [
22
39
  {
23
40
  type: "text",
24
- text: formatConferArchitectureText(conferPrompt, result),
41
+ text: formatArchitectureAssessmentConferText(conferPrompt, result, "UNREVIEWED"),
25
42
  },
26
43
  ],
27
44
  };
@@ -10,6 +10,8 @@ import { frameworkLogger } from '../../core/framework-logger.js';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { dirname, join } from 'path';
12
12
  import fs from 'fs';
13
+ /** Must outlast the 120s governance model call so a real approve can return. */
14
+ const GOVERNANCE_MODEL_TIMEOUT_MS = 150_000;
13
15
  /**
14
16
  * Registry for managing MCP server configurations
15
17
  */
@@ -85,14 +87,14 @@ export class ServerConfigRegistry {
85
87
  serverName: 'code-review',
86
88
  command: 'node',
87
89
  args: [`${basePath}/knowledge-skills/code-review.server.js`],
88
- timeout: 30000,
90
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
89
91
  });
90
92
  // Security Audit Server
91
93
  this.register({
92
94
  serverName: 'security-audit',
93
95
  command: 'node',
94
96
  args: [`${basePath}/knowledge-skills/security-audit.server.js`],
95
- timeout: 45000,
97
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
96
98
  });
97
99
  // Performance Optimization Server
98
100
  this.register({
@@ -113,7 +115,7 @@ export class ServerConfigRegistry {
113
115
  serverName: 'researcher',
114
116
  command: 'node',
115
117
  args: [`${basePath}/researcher.server.js`],
116
- timeout: 60000,
118
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
117
119
  });
118
120
  // Governance Service (meta-MCP that orchestrates the three skill servers + required external Dynamo)
119
121
  this.register({
@@ -176,7 +178,7 @@ export class ServerConfigRegistry {
176
178
  serverName: 'architect',
177
179
  command: 'node',
178
180
  args: [`${basePath}/architect-tools.server.js`],
179
- timeout: 45000,
181
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
180
182
  });
181
183
  // Bug Triage Specialist Server
182
184
  this.register({
@@ -198,14 +200,14 @@ export class ServerConfigRegistry {
198
200
  serverName: 'code-reviewer',
199
201
  command: 'node',
200
202
  args: [`${basePath}/knowledge-skills/code-review.server.js`],
201
- timeout: 30000,
203
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
202
204
  });
203
205
  // Security Auditor (alias for security-audit)
204
206
  this.register({
205
207
  serverName: 'security-auditor',
206
208
  command: 'node',
207
209
  args: [`${basePath}/knowledge-skills/security-audit.server.js`],
208
- timeout: 45000,
210
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
209
211
  });
210
212
  // Refactorer Server
211
213
  this.register({
@@ -24,6 +24,7 @@ declare class GovernanceServer extends XrayKnowledgeSkillBase {
24
24
  private armRequiredDynamo;
25
25
  private handleGovernProposals;
26
26
  private handleGovernReflection;
27
+ private handleRecordConferReceipt;
27
28
  private handleGetActiveCodex;
28
29
  private parseCodexTermsFromReflection;
29
30
  private initializeGovernance;
@@ -20,6 +20,7 @@ import { featuresConfigLoader } from "../core/features-config.js";
20
20
  import * as fs from "fs";
21
21
  import { fileURLToPath } from "url";
22
22
  import { randomUUID } from "crypto";
23
+ import { applyConferConsultResult } from "../nucleus/confer.js";
23
24
  import { getGovernanceService } from "../governance/governance-service.js";
24
25
  import { getCodexPolicyService } from "../governance/codex-policy.service.js";
25
26
  import { initializeGovernanceIntegration } from "../integrations/governance/index.js";
@@ -95,6 +96,23 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
95
96
  required: [],
96
97
  },
97
98
  },
99
+ {
100
+ name: "record_confer_receipt",
101
+ description: "Record a confer verdict written by the wearing TUI. " +
102
+ "The TUI already called the seat's MCP server. This tool stores the receipt. " +
103
+ "Only Verdict: PASS completes the consult todo.",
104
+ inputSchema: {
105
+ type: "object",
106
+ properties: {
107
+ todoId: { type: "string", description: "Consult todo id, such as s.1" },
108
+ subagent: { type: "string", description: "researcher, architect-tools, or code-review" },
109
+ sessionId: { type: "string", description: "Plan session id" },
110
+ outputText: { type: "string", description: "Host verdict text, including a Verdict line" },
111
+ projectRoot: { type: "string", description: "Checkout root. Defaults to XRAY_ROOT or the process cwd." },
112
+ },
113
+ required: ["todoId", "subagent", "sessionId", "outputText"],
114
+ },
115
+ },
98
116
  {
99
117
  name: "get_active_codex",
100
118
  description: "Get the currently active Codex (SSOT) — returns metadata about the loaded codex terms, " +
@@ -117,6 +135,7 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
117
135
  "govern_proposals": async (args) => this.handleGovernProposals(this.validateGovernProposalsArgs(args)),
118
136
  "govern_reflection": async (args) => this.handleGovernReflection(this.validateGovernReflectionArgs(args)),
119
137
  "get_active_codex": async (args) => this.handleGetActiveCodex(args),
138
+ "record_confer_receipt": async (args) => this.handleRecordConferReceipt(args),
120
139
  };
121
140
  this.setupToolHandlers();
122
141
  }
@@ -163,6 +182,8 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
163
182
  return await this.handleGovernReflection(this.validateGovernReflectionArgs(args));
164
183
  case "get_active_codex":
165
184
  return await this.handleGetActiveCodex(args);
185
+ case "record_confer_receipt":
186
+ return await this.handleRecordConferReceipt(args);
166
187
  default:
167
188
  throw new Error(`Unknown tool: ${name}`);
168
189
  }
@@ -272,6 +293,31 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
272
293
  options: { require_external: true },
273
294
  });
274
295
  }
296
+ async handleRecordConferReceipt(args) {
297
+ if (!args || typeof args !== "object") {
298
+ throw new Error("record_confer_receipt requires an object argument");
299
+ }
300
+ const obj = args;
301
+ const todoId = typeof obj.todoId === "string" ? obj.todoId.trim() : "";
302
+ const subagent = typeof obj.subagent === "string" ? obj.subagent.trim() : "";
303
+ const sessionId = typeof obj.sessionId === "string" ? obj.sessionId.trim() : "";
304
+ const outputText = typeof obj.outputText === "string" ? obj.outputText : "";
305
+ if (!todoId || !subagent || !sessionId || !outputText.trim()) {
306
+ throw new Error("record_confer_receipt requires todoId, subagent, sessionId, and outputText");
307
+ }
308
+ const projectRoot = typeof obj.projectRoot === "string" && obj.projectRoot.trim()
309
+ ? obj.projectRoot
310
+ : process.env.XRAY_ROOT || process.cwd();
311
+ const result = applyConferConsultResult(todoId, subagent, sessionId, outputText, projectRoot);
312
+ return {
313
+ content: [
314
+ {
315
+ type: "text",
316
+ text: JSON.stringify(result),
317
+ },
318
+ ],
319
+ };
320
+ }
275
321
  async handleGetActiveCodex(args) {
276
322
  const includeRaw = args?.includeRaw === true;
277
323
  const snapshot = await getCodexPolicyService().getCurrentCodex(includeRaw);
@@ -9,6 +9,7 @@ import * as fs from "fs";
9
9
  import * as path from "path";
10
10
  import { isGovernanceLlmConfigured, tryLLMGovernance, } from "../../governance/llm-governance-provider.js";
11
11
  import { formatGovernanceVoteText, localConferVote } from "../../governance/local-confer.js";
12
+ import { buildHostConferEvidence } from "../../nucleus/confer-host.js";
12
13
  class XrayCodeReviewServer extends XrayKnowledgeSkillBase {
13
14
  constructor() {
14
15
  super("code-review", "3.1.0");
@@ -316,6 +317,12 @@ class XrayCodeReviewServer extends XrayKnowledgeSkillBase {
316
317
  */
317
318
  async analyzeProposal(args) {
318
319
  const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args;
320
+ if (proposalType === "synthesis-confer") {
321
+ const packet = [proposalTitle, proposalDescription, ...(evidence || [])].filter(Boolean).join("\n\n");
322
+ return {
323
+ content: [{ type: "text", text: buildHostConferEvidence("code-review", packet) }],
324
+ };
325
+ }
319
326
  const vote = (await tryLLMGovernance("code-review", proposalTitle, proposalDescription, evidence, proposalType)) ??
320
327
  localConferVote({
321
328
  role: "code-review",
@@ -7,7 +7,7 @@
7
7
  import * as fs from "fs";
8
8
  import * as path from "path";
9
9
  import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
10
- import { isGovernanceLlmConfigured, tryLLMGovernance, } from "../../governance/llm-governance-provider.js";
10
+ import { attemptLLMGovernance } from "../../governance/llm-governance-provider.js";
11
11
  import { formatGovernanceVoteText, localConferVote } from "../../governance/local-confer.js";
12
12
  class XraySecurityAuditServer extends XrayKnowledgeSkillBase {
13
13
  constructor() {
@@ -661,10 +661,12 @@ class XraySecurityAuditServer extends XrayKnowledgeSkillBase {
661
661
  */
662
662
  async analyzeProposal(args) {
663
663
  const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args;
664
- const vote = (await tryLLMGovernance("security-audit", proposalTitle, proposalDescription, evidence, proposalType)) ??
664
+ const attempt = await attemptLLMGovernance("security-audit", proposalTitle, proposalDescription, evidence, proposalType);
665
+ const vote = attempt.vote ??
665
666
  localConferVote({
666
667
  role: "security-audit",
667
- llmConfigured: isGovernanceLlmConfigured(),
668
+ llmConfigured: attempt.miss !== "not-configured",
669
+ ...(attempt.miss ? { miss: attempt.miss } : {}),
668
670
  });
669
671
  return {
670
672
  content: [
@@ -319,7 +319,7 @@ export class MCPClient extends EventEmitter {
319
319
  };
320
320
  this.emit('tool.before', beforeEvent);
321
321
  const serverName = this.config.serverName;
322
- const isGovernanceServer = ['code-review', 'security-audit', 'researcher', 'governance'].includes(serverName);
322
+ const isGovernanceServer = ['code-review', 'security-audit', 'researcher', 'architect', 'governance'].includes(serverName);
323
323
  const isGovernanceTool = toolName === 'analyze_proposal';
324
324
  const preferReal = this.isPureMcpMode || isGovernanceServer || isGovernanceTool;
325
325
  try {
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { frameworkLogger } from '../../../core/framework-logger.js';
7
7
  import { buildLeadDevPlan, buildSynthesisCheckpointPlan, isLeadDevModeActive, persistLeadDevPlan, } from '../../../nucleus/autonomy-kernel.js';
8
- import { formatConferQuorumReport, isConferEnabled, runConferQuorum, } from '../../../nucleus/confer.js';
8
+ import { conferFixtureAllowed, formatConferQuorumReport, isConferEnabled, runConferQuorum, } from '../../../nucleus/confer.js';
9
9
  import { bindPlanToSession } from '../../../nucleus/lead-dev-plan-persistence.js';
10
10
  import { clearPendingDelegations } from '../../../nucleus/pending-delegations.js';
11
11
  import { isAutoProvisionWorktreeEnabled, provisionGitWorktree, } from '../../../nucleus/aside-worktree.js';
@@ -177,7 +177,7 @@ export class ComplexityHandler {
177
177
  args.sessionId) {
178
178
  const conferOpts = {
179
179
  dueReason: args.synthesisDueReason ?? null,
180
- fixture: args.conferFixture === true,
180
+ fixture: conferFixtureAllowed() && args.conferFixture === true,
181
181
  };
182
182
  if (args.collocatedText)
183
183
  conferOpts.collocatedText = args.collocatedText;
@@ -10,10 +10,11 @@
10
10
  import * as fs from "fs";
11
11
  import * as path from "path";
12
12
  import { frameworkLogger } from "../core/framework-logger.js";
13
- import { isGovernanceLlmConfigured, tryLLMGovernance, } from "../governance/llm-governance-provider.js";
13
+ import { attemptLLMGovernance, } from "../governance/llm-governance-provider.js";
14
14
  import { formatGovernanceVoteText, localConferVote } from "../governance/local-confer.js";
15
15
  import { initializeMemoryRouting } from "../memory-routing/index.js";
16
16
  import { buildMemoryRoutingEvidence, formatMemoryRoutingBlock, resolveResearcherMemoryContext, } from "./researcher-confidence.js";
17
+ import { buildHostConferEvidence } from "../nucleus/confer-host.js";
17
18
  import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
18
19
  class XrayLibrarianServer extends XrayKnowledgeSkillBase {
19
20
  constructor() {
@@ -382,6 +383,12 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
382
383
  */
383
384
  async analyzeProposal(args) {
384
385
  const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args || {};
386
+ if (proposalType === "synthesis-confer") {
387
+ const packet = [proposalTitle, proposalDescription, ...(evidence || [])].filter(Boolean).join("\n\n");
388
+ return {
389
+ content: [{ type: "text", text: buildHostConferEvidence("researcher", packet) }],
390
+ };
391
+ }
385
392
  const memoryContext = await resolveResearcherMemoryContext({
386
393
  proposalTitle,
387
394
  proposalDescription,
@@ -391,12 +398,14 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
391
398
  if (memoryContext) {
392
399
  enrichedEvidence.push(...buildMemoryRoutingEvidence(memoryContext));
393
400
  }
394
- const vote = (await tryLLMGovernance("researcher", proposalTitle || "", proposalDescription || "", enrichedEvidence, proposalType || "")) ??
401
+ const attempt = await attemptLLMGovernance("researcher", proposalTitle || "", proposalDescription || "", enrichedEvidence, proposalType || "");
402
+ const vote = attempt.vote ??
395
403
  localConferVote({
396
404
  role: "researcher",
397
- llmConfigured: isGovernanceLlmConfigured(),
405
+ llmConfigured: attempt.miss !== "not-configured",
398
406
  highConfidenceTrapPresent: memoryContext?.confidence.highConfidenceTrapPresent,
399
407
  recommendedAgent: memoryContext?.recommendedAgent,
408
+ ...(attempt.miss ? { miss: attempt.miss } : {}),
400
409
  });
401
410
  const memoryRoutingBlock = memoryContext
402
411
  ? `\n${formatMemoryRoutingBlock(memoryContext)}`
@@ -38,6 +38,8 @@ export interface LeadDevPlan {
38
38
  fullSuiteGate: boolean;
39
39
  hint: string;
40
40
  };
41
+ /** New id each synthesis plan. Receipts from an older id cannot complete s.1–s.3. */
42
+ consultCycleId?: string;
41
43
  }
42
44
  export declare function isLeadDevModeActive(): boolean;
43
45
  export declare function routeSubagent(taskType: string): SubagentRoute;
@@ -2,11 +2,11 @@
2
2
  * Lead-dev plan builder — internal implementation for orchestrator + hooks.
3
3
  * Config: features.json → multi_agent_orchestration (lead_dev_mode), NOT a separate surface.
4
4
  */
5
+ import { randomUUID } from 'node:crypto';
5
6
  import * as fs from 'fs';
6
7
  import * as path from 'path';
7
8
  import { featuresConfigLoader } from '../core/features-config.js';
8
9
  import { scoreComplexity } from './thin-dispatch.js';
9
- import { conferDefaultForProfile, resolveRuntimeSuitProfile } from './suit-temperament.js';
10
10
  export const LEAD_DEV_RULES = [
11
11
  'Phased plan + detailed todos; assign best subagent; monitor output; iterate fully',
12
12
  'Lead dev takes helm — loop test fix until complete; no permission pings',
@@ -33,6 +33,19 @@ const TASK_TYPE_ROUTES = {
33
33
  security: 'security-audit',
34
34
  governance: 'enforcer',
35
35
  };
36
+ function conferExplicitlyEnabled(projectRoot) {
37
+ const featuresPath = path.join(projectRoot, '.xray', 'features.json');
38
+ if (!fs.existsSync(featuresPath))
39
+ return false;
40
+ try {
41
+ const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
42
+ const orch = data.multi_agent_orchestration ?? {};
43
+ return orch.confer?.enabled === true || orch.confer_on_synthesis === true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
36
49
  function orchestrationConfig() {
37
50
  try {
38
51
  return featuresConfigLoader.loadConfig().multi_agent_orchestration;
@@ -80,12 +93,7 @@ export function buildSynthesisCheckpointPlan(dueReason, projectRoot = process.cw
80
93
  if (!isLeadDevModeActive())
81
94
  return null;
82
95
  const cfg = orchestrationConfig();
83
- const profile = resolveRuntimeSuitProfile(projectRoot);
84
- const autoConsult = profile === 'frontier'
85
- ? cfg.confer?.enabled === true || cfg.confer_on_synthesis === true
86
- : profile === 'strict'
87
- ? true
88
- : cfg.auto_consult_major_work !== false;
96
+ const autoConsult = conferExplicitlyEnabled(projectRoot);
89
97
  const mandatoryConsults = autoConsult ? [...MANDATORY_MAJOR_CONSULTS] : [];
90
98
  if (mandatoryConsults.length === 0)
91
99
  return null;
@@ -118,13 +126,13 @@ export function buildSynthesisCheckpointPlan(dueReason, projectRoot = process.cw
118
126
  fullSuiteGate: false,
119
127
  hint: 'Synthesis checkpoint — consult mandatory agents before resuming gated work',
120
128
  },
129
+ consultCycleId: randomUUID(),
121
130
  };
122
131
  }
123
132
  export function buildLeadDevPlan(description, taskTypes = ['implement'], taskInputs = [], mcpOverallComplexity, projectRoot = process.cwd()) {
124
133
  if (!isLeadDevModeActive())
125
134
  return null;
126
135
  const cfg = orchestrationConfig();
127
- const profile = resolveRuntimeSuitProfile(projectRoot);
128
136
  const threshold = cfg.phased_plan_threshold ?? 25;
129
137
  const score = scoreComplexity(description, { taskTypes });
130
138
  const mcpScore = mcpOverallComplexity !== undefined && Number.isFinite(mcpOverallComplexity)
@@ -132,11 +140,7 @@ export function buildLeadDevPlan(description, taskTypes = ['implement'], taskInp
132
140
  : 0;
133
141
  const complexity = Math.max(score.score, mcpScore);
134
142
  const requiresPhasedPlan = complexity > threshold || taskInputs.length > 1;
135
- const autoConsult = profile === 'frontier'
136
- ? cfg.confer?.enabled === true || cfg.confer_on_synthesis === true
137
- : profile === 'strict' || conferDefaultForProfile(profile)
138
- ? cfg.auto_consult_major_work !== false
139
- : false;
143
+ const autoConsult = conferExplicitlyEnabled(projectRoot);
140
144
  const mandatoryConsults = autoConsult && requiresPhasedPlan ? [...MANDATORY_MAJOR_CONSULTS] : [];
141
145
  const phases = requiresPhasedPlan
142
146
  ? [
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Confer host port. The wearing TUI already spawned the MCP servers.
3
+ * This port writes the ask or returns the host's answer. It does not spawn a server.
4
+ */
5
+ export interface ConferHostSeat {
6
+ todoId: string;
7
+ subagent: string;
8
+ server: string;
9
+ tool: string;
10
+ args: Record<string, unknown>;
11
+ sessionId: string;
12
+ prompt: string;
13
+ dueReason: string | null;
14
+ }
15
+ export type ConferHostCall = {
16
+ status: 'pending';
17
+ askPath: string;
18
+ } | {
19
+ status: 'answered';
20
+ text: string;
21
+ };
22
+ export interface ConferHostPort {
23
+ call(seat: ConferHostSeat, projectRoot: string): Promise<ConferHostCall>;
24
+ }
25
+ export declare function conferAskPath(todoId: string, projectRoot: string): string;
26
+ /** Evidence for the host model. The schema line is not a vote. */
27
+ export declare function buildHostConferEvidence(role: string, evidence: string): string;
28
+ export declare class SessionConferHost implements ConferHostPort {
29
+ call(seat: ConferHostSeat, projectRoot: string): Promise<ConferHostCall>;
30
+ }
31
+ export declare class InProcessConferHost implements ConferHostPort {
32
+ private readonly answers;
33
+ constructor(answers: Record<string, string>);
34
+ call(seat: ConferHostSeat): Promise<ConferHostCall>;
35
+ }