0xray 4.0.40 → 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.
- package/.grok-plugin/plugin.json +1 -1
- package/.opencode/agents/api-design.yml +31 -0
- package/.opencode/agents/architect.yml +15 -0
- package/.opencode/agents/architecture-patterns.yml +14 -0
- package/.opencode/agents/auto-format.yml +14 -0
- package/.opencode/agents/backend-engineer.yml +12 -0
- package/.opencode/agents/boot-orchestrator.yml +14 -0
- package/.opencode/agents/bug-triage-specialist.yml +15 -0
- package/.opencode/agents/code-analyzer.yml +12 -0
- package/.opencode/agents/code-reviewer.yml +14 -0
- package/.opencode/agents/content-creator.yml +12 -0
- package/.opencode/agents/database-engineer.yml +12 -0
- package/.opencode/agents/devops-engineer.yml +12 -0
- package/.opencode/agents/framework-compliance-audit.yml +14 -0
- package/.opencode/agents/frontend-engineer.yml +12 -0
- package/.opencode/agents/frontend-ui-ux-engineer.yml +12 -0
- package/.opencode/agents/git-workflow.yml +15 -0
- package/.opencode/agents/growth-strategist.yml +12 -0
- package/.opencode/agents/hermes-agent.yml +12 -0
- package/.opencode/agents/inference-improve.yml +12 -0
- package/.opencode/agents/lint.yml +14 -0
- package/.opencode/agents/log-monitor.yml +12 -0
- package/.opencode/agents/mobile-developer.yml +12 -0
- package/.opencode/agents/model-health-check.yml +12 -0
- package/.opencode/agents/multimodal-looker.yml +12 -0
- package/.opencode/agents/performance-analysis.yml +12 -0
- package/.opencode/agents/performance-engineer.yml +12 -0
- package/.opencode/agents/performance-optimization.yml +12 -0
- package/.opencode/agents/processor-pipeline.yml +14 -0
- package/.opencode/agents/project-analysis.yml +12 -0
- package/.opencode/agents/refactorer.yml +12 -0
- package/.opencode/agents/researcher.yml +12 -0
- package/.opencode/agents/security-auditor.yml +12 -0
- package/.opencode/agents/security-scan.yml +12 -0
- package/.opencode/agents/seo-consultant.yml +12 -0
- package/.opencode/agents/session-management.yml +12 -0
- package/.opencode/agents/state-manager.yml +12 -0
- package/.opencode/agents/storyteller.yml +12 -0
- package/.opencode/agents/strategist.yml +12 -0
- package/.opencode/agents/tech-writer.yml +12 -0
- package/.opencode/agents/testing-best-practices.yml +12 -0
- package/.opencode/agents/testing-lead.yml +12 -0
- package/.opencode/agents/ui-ux-design.yml +12 -0
- package/.opencode/codex.codex +8 -0
- package/.opencode/commands/auto-format.md +99 -0
- package/.opencode/commands/auto-summary-capture.md +90 -0
- package/.opencode/commands/dependency-audit.md +184 -0
- package/.opencode/commands/enforcer-daily-scan.md +137 -0
- package/.opencode/commands/framework-compliance-audit.md +205 -0
- package/.opencode/commands/interactive-validator.md +75 -0
- package/.opencode/commands/job-summary-logger.md +68 -0
- package/.opencode/commands/lint.md +11 -0
- package/.opencode/commands/mode-switch.md +95 -0
- package/.opencode/commands/model-health-check.md +186 -0
- package/.opencode/commands/performance-analysis.md +144 -0
- package/.opencode/commands/pre-commit-introspection.md +185 -0
- package/.opencode/commands/pre-commit-introspection.sh +133 -0
- package/.opencode/commands/security-scan.md +157 -0
- package/.opencode/commands/sisyphus-validation.md +128 -0
- package/.opencode/commands/summary-logger.md +83 -0
- package/.opencode/enforcer-config.json +282 -0
- package/.opencode/skills/orchestrator/SKILL.md +12 -0
- package/.opencode/workflows/post-deployment-audit.yml +123 -0
- package/.xray/features.json +1 -1
- package/.xray/features.schema.json +1 -1
- package/AGENTS-consumer.md +2 -0
- package/AGENTS.md +2 -0
- package/README.md +52 -12
- package/SKILLS.md +2 -0
- package/dist/AGENTS.md +2 -0
- package/dist/CHANGELOG.md +29 -0
- package/dist/README.md +52 -12
- package/dist/SKILLS.md +2 -0
- package/dist/cli/commands/openclaw-install.js +1 -1
- package/dist/cli/index.js +12 -0
- package/dist/enforcement/index.d.ts +4 -0
- package/dist/enforcement/index.js +4 -0
- package/dist/enforcement/validators/code-quality-validators.js +6 -4
- package/dist/governance/llm-governance-provider.d.ts +7 -1
- package/dist/governance/llm-governance-provider.js +72 -14
- package/dist/governance/local-confer.d.ts +4 -1
- package/dist/governance/local-confer.js +22 -1
- package/dist/integrations/grok/grok-cli.js +10 -37
- package/dist/integrations/grok/hooks/grok-hook-utils.js +29 -2
- package/dist/integrations/grok/hooks/session-start.js +2 -20
- package/dist/integrations/hooks/confer-hook-runtime.mjs +23 -1
- package/dist/integrations/hooks/goggles-mcp.mjs +36 -4
- package/dist/integrations/hooks/goggles-pipeline.mjs +55 -15
- package/dist/integrations/hooks/goggles-planes.json +13 -4
- package/dist/integrations/hooks/plates.cjs +22 -0
- package/dist/integrations/hooks/session-note-index.cjs +298 -0
- package/dist/integrations/hooks/session-note-index.mjs +22 -0
- package/dist/integrations/hooks/sleeve.cjs +335 -0
- package/dist/integrations/hooks/sleeve.mjs +18 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +205 -9
- package/dist/integrations/hooks/wake-cascade.cjs +102 -0
- package/dist/integrations/hooks/wake-cascade.mjs +16 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/architect-tools.server.d.ts +6 -0
- package/dist/mcps/architect-tools.server.js +19 -2
- package/dist/mcps/config/server-config-registry.js +8 -6
- package/dist/mcps/governance.server.d.ts +1 -0
- package/dist/mcps/governance.server.js +46 -0
- package/dist/mcps/knowledge-skills/code-review.server.js +7 -0
- package/dist/mcps/knowledge-skills/security-audit.server.js +5 -3
- package/dist/mcps/mcp-client.js +1 -1
- package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
- package/dist/mcps/researcher.server.js +12 -3
- package/dist/memory-routing/plates.d.ts +1 -1
- package/dist/memory-routing/plates.js +1 -0
- package/dist/nucleus/autonomy-kernel.d.ts +2 -0
- package/dist/nucleus/autonomy-kernel.js +17 -13
- package/dist/nucleus/confer-host.d.ts +35 -0
- package/dist/nucleus/confer-host.js +55 -0
- package/dist/nucleus/confer.d.ts +19 -3
- package/dist/nucleus/confer.js +111 -55
- package/dist/nucleus/lead-dev-plan-persistence.js +4 -2
- package/dist/nucleus/suit-temperament.d.ts +1 -1
- package/dist/nucleus/suit-temperament.js +2 -2
- package/dist/nucleus/synthesis-consult-receipt.d.ts +7 -1
- package/dist/nucleus/synthesis-consult-receipt.js +86 -35
- package/dist/reporting/framework-reporting-system.js +2 -5
- package/dist/reporting/log-parser.d.ts +2 -0
- package/dist/reporting/log-parser.js +7 -6
- package/dist/skills/orchestrator/SKILL.md +12 -0
- package/docs-site/docs/plates/activity-log.md +1 -1
- package/docs-site/docs/plates/boot.md +2 -0
- package/docs-site/docs/plates/centrifuge.md +30 -0
- package/docs-site/docs/plates/cursor-compact.md +1 -1
- package/docs-site/docs/plates/glossary.md +1 -2
- package/docs-site/docs/plates/goggles.md +4 -0
- package/docs-site/docs/plates/governance.md +2 -0
- package/docs-site/docs/plates/grok-compact.md +1 -1
- package/docs-site/docs/plates/grokbot.md +1 -1
- package/docs-site/docs/plates/host-pack.md +2 -0
- package/docs-site/docs/plates/house.md +1 -1
- package/docs-site/docs/plates/index.md +3 -1
- package/docs-site/docs/plates/inference-files.md +1 -1
- package/docs-site/docs/plates/kept-line.md +1 -1
- package/docs-site/docs/plates/kits.md +2 -0
- package/docs-site/docs/plates/lens-gate.md +1 -1
- package/docs-site/docs/plates/lens-page.md +1 -1
- package/docs-site/docs/plates/live-mesh.md +1 -1
- package/docs-site/docs/plates/memory-recall.md +4 -0
- package/docs-site/docs/plates/notes-page.md +2 -1
- package/docs-site/docs/plates/orchestration.md +2 -0
- package/docs-site/docs/plates/package-face.md +1 -1
- package/docs-site/docs/plates/payload-heat.md +1 -1
- package/docs-site/docs/plates/pickup-stamp.md +1 -1
- package/docs-site/docs/plates/pre-tool.md +1 -1
- package/docs-site/docs/plates/processor.md +2 -0
- package/docs-site/docs/plates/record-map.md +2 -1
- package/docs-site/docs/plates/reflection-page.md +1 -1
- package/docs-site/docs/plates/reporting.md +2 -0
- package/docs-site/docs/plates/review.md +1 -1
- package/docs-site/docs/plates/routing.md +2 -0
- package/docs-site/docs/plates/session-capture.md +1 -1
- package/docs-site/docs/plates/site-manual.md +1 -1
- package/docs-site/docs/plates/stamp-plate.md +1 -1
- package/docs-site/docs/plates/station-card.md +4 -1
- package/docs-site/docs/plates/station-heat.md +1 -1
- package/docs-site/docs/plates/suit-organs.md +1 -1
- package/docs-site/docs/plates/suit-settings.md +1 -1
- package/docs-site/docs/plates/suit-wear.md +3 -1
- package/docs-site/docs/plates/suit.md +2 -0
- package/docs-site/docs/plates/trail-state.md +1 -1
- package/docs-site/docs/plates/work-fresh.md +1 -1
- package/docs-site/docs/plates/write-home.md +1 -1
- package/llms.txt +2 -0
- package/package.json +2 -2
- package/scripts/mjs/verify-confer-core.mjs +153 -29
- package/scripts/mjs/verify-user-aside-core.mjs +3 -4
- package/scripts/node/bridge-mcp-wiring.cjs +273 -9
- package/scripts/node/confer-switch.cjs +66 -0
- package/scripts/node/install-bridges.cjs +188 -60
- package/scripts/node/setup.cjs +2 -46
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/architect-tools.server.ts +26 -2
- package/src/mcps/config/__tests__/server-config-registry.test.ts +15 -6
- package/src/mcps/config/server-config-registry.ts +9 -6
- package/src/mcps/governance.server.ts +49 -0
- package/src/mcps/knowledge-skills/code-review.server.ts +8 -0
- package/src/mcps/knowledge-skills/security-audit.server.ts +11 -12
- package/src/mcps/mcp-client.ts +1 -1
- package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
- package/src/mcps/researcher.server.ts +19 -10
- package/src/skills/orchestrator/SKILL.md +12 -0
- package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +5 -51
- package/xray/features.json +1 -1
- package/xray/features.schema.json +1 -1
|
@@ -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 {
|
|
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
|
|
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:
|
|
668
|
+
llmConfigured: attempt.miss !== "not-configured",
|
|
669
|
+
...(attempt.miss ? { miss: attempt.miss } : {}),
|
|
668
670
|
});
|
|
669
671
|
return {
|
|
670
672
|
content: [
|
package/dist/mcps/mcp-client.js
CHANGED
|
@@ -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 {
|
|
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
|
|
401
|
+
const attempt = await attemptLLMGovernance("researcher", proposalTitle || "", proposalDescription || "", enrichedEvidence, proposalType || "");
|
|
402
|
+
const vote = attempt.vote ??
|
|
395
403
|
localConferVote({
|
|
396
404
|
role: "researcher",
|
|
397
|
-
llmConfigured:
|
|
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)}`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PLATE_IDS: readonly ["routing", "governance", "boot", "orchestration", "processor", "reporting", "memory-recall", "stamp-plate", "house", "review", "goggles", "suit", "kits", "host-pack", "glossary", "record-map", "write-home", "activity-log", "session-capture", "suit-wear", "suit-organs", "station-card", "notes-page", "reflection-page", "site-manual", "package-face", "suit-settings", "trail-state", "inference-files", "grok-compact", "payload-heat", "station-heat", "pickup-stamp", "cursor-compact", "work-fresh", "kept-line", "lens-gate", "pre-tool", "lens-page"];
|
|
1
|
+
export declare const PLATE_IDS: readonly ["routing", "governance", "boot", "orchestration", "processor", "reporting", "memory-recall", "stamp-plate", "house", "review", "goggles", "suit", "kits", "host-pack", "glossary", "record-map", "write-home", "activity-log", "session-capture", "suit-wear", "suit-organs", "station-card", "notes-page", "reflection-page", "site-manual", "package-face", "suit-settings", "trail-state", "inference-files", "grok-compact", "payload-heat", "station-heat", "pickup-stamp", "cursor-compact", "work-fresh", "kept-line", "lens-gate", "pre-tool", "lens-page", "centrifuge"];
|
|
2
2
|
export type PlateId = (typeof PLATE_IDS)[number];
|
|
3
3
|
export interface PlateStamp {
|
|
4
4
|
id: PlateId;
|
|
@@ -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
|
|
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 =
|
|
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
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
export function conferAskPath(todoId, projectRoot) {
|
|
8
|
+
return path.join(projectRoot, '.xray', 'state', `confer-ask-${todoId}.json`);
|
|
9
|
+
}
|
|
10
|
+
/** Evidence for the host model. The schema line is not a vote. */
|
|
11
|
+
export function buildHostConferEvidence(role, evidence) {
|
|
12
|
+
const body = evidence.trim();
|
|
13
|
+
return [
|
|
14
|
+
`## Confer evidence — ${role}`,
|
|
15
|
+
'',
|
|
16
|
+
body || 'No local evidence. The wearing TUI has the confer ask.',
|
|
17
|
+
'',
|
|
18
|
+
'The wearing TUI writes the verdict on its own line after this evidence.',
|
|
19
|
+
'',
|
|
20
|
+
'Verdict: PASS | CONDITIONAL | FAIL',
|
|
21
|
+
'- Top risks (bulleted)',
|
|
22
|
+
'- Hardening note / recommendations',
|
|
23
|
+
].join('\n');
|
|
24
|
+
}
|
|
25
|
+
export class SessionConferHost {
|
|
26
|
+
async call(seat, projectRoot) {
|
|
27
|
+
const askPath = conferAskPath(seat.todoId, projectRoot);
|
|
28
|
+
fs.mkdirSync(path.dirname(askPath), { recursive: true });
|
|
29
|
+
const payload = {
|
|
30
|
+
todoId: seat.todoId,
|
|
31
|
+
subagent: seat.subagent,
|
|
32
|
+
server: seat.server,
|
|
33
|
+
tool: seat.tool,
|
|
34
|
+
args: seat.args,
|
|
35
|
+
sessionId: seat.sessionId,
|
|
36
|
+
prompt: seat.prompt,
|
|
37
|
+
dueReason: seat.dueReason,
|
|
38
|
+
};
|
|
39
|
+
fs.writeFileSync(askPath, `${JSON.stringify(payload, null, 2)}\n`);
|
|
40
|
+
return { status: 'pending', askPath };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class InProcessConferHost {
|
|
44
|
+
answers;
|
|
45
|
+
constructor(answers) {
|
|
46
|
+
this.answers = answers;
|
|
47
|
+
}
|
|
48
|
+
async call(seat) {
|
|
49
|
+
const text = this.answers[seat.todoId] ?? this.answers[seat.subagent];
|
|
50
|
+
if (text === undefined) {
|
|
51
|
+
return { status: 'pending', askPath: '' };
|
|
52
|
+
}
|
|
53
|
+
return { status: 'answered', text };
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/nucleus/confer.d.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* Triggered automatically at synthesis checkpoint; records consult receipts + plan todos.
|
|
4
4
|
*/
|
|
5
5
|
import { type SynthesisConsultReceipt } from './synthesis-consult-receipt.js';
|
|
6
|
+
import { InProcessConferHost, SessionConferHost, type ConferHostPort, type ConferHostSeat } from './confer-host.js';
|
|
7
|
+
export { InProcessConferHost, SessionConferHost };
|
|
8
|
+
export type { ConferHostPort, ConferHostSeat };
|
|
6
9
|
export declare const CONFER_AGENTS: readonly ["researcher", "architect-tools", "code-review"];
|
|
7
10
|
export interface ConferConfig {
|
|
8
11
|
enabled: boolean;
|
|
@@ -27,12 +30,16 @@ export interface ConferAgentResult {
|
|
|
27
30
|
error?: string;
|
|
28
31
|
}
|
|
29
32
|
export interface ConferQuorumResult {
|
|
30
|
-
status: 'completed' | 'partial' | 'skipped' | 'failed';
|
|
33
|
+
status: 'completed' | 'partial' | 'skipped' | 'failed' | 'pending';
|
|
31
34
|
agents: ConferAgentResult[];
|
|
32
35
|
message: string;
|
|
33
36
|
}
|
|
34
37
|
export declare function conferCheckpointPath(projectRoot?: string): string;
|
|
35
38
|
export declare function defaultConferConfig(): ConferConfig;
|
|
39
|
+
/** Fixture PASS is a test harness. Live orchestrate-task and XRAY_CONFER_FIXTURE cannot reach it. */
|
|
40
|
+
export declare const CONFER_FIXTURE_UNREACHABLE = "Confer fixture is unreachable outside tests";
|
|
41
|
+
export declare function conferFixtureAllowed(): boolean;
|
|
42
|
+
/** A suit profile does not enable Confer. Missing confer key stays off, including strict. */
|
|
36
43
|
export declare function loadConferConfig(projectRoot?: string): ConferConfig;
|
|
37
44
|
export declare function isConferEnabled(projectRoot?: string): boolean;
|
|
38
45
|
export declare function loadConferCheckpoint(projectRoot?: string): ConferCheckpointState | null;
|
|
@@ -46,12 +53,21 @@ export declare function conferAgentMcpTarget(subagent: string): {
|
|
|
46
53
|
tool: string;
|
|
47
54
|
args: (prompt: string, projectRoot: string) => Record<string, unknown>;
|
|
48
55
|
};
|
|
49
|
-
export declare function invokeConferAgent(subagent: string, prompt: string, projectRoot?: string): Promise<string>;
|
|
56
|
+
export declare function invokeConferAgent(subagent: string, prompt: string, projectRoot?: string, host?: ConferHostPort, sessionId?: string, todoId?: string, dueReason?: string | null): Promise<string>;
|
|
50
57
|
export declare function applyConferConsultResult(todoId: string, subagent: string, sessionId: string, outputText: string, projectRoot?: string): ConferAgentResult;
|
|
51
|
-
export declare function
|
|
58
|
+
export declare function buildFixtureConferOutput(subagent: string, verdict?: SynthesisConsultReceipt['verdict'], noLlm?: boolean): string;
|
|
59
|
+
export declare function writeFixtureConferReceipt(todoId: string, subagent: string, sessionId: string, projectRoot?: string, verdict?: SynthesisConsultReceipt['verdict'], noLlm?: boolean): ConferAgentResult;
|
|
52
60
|
export declare function runConferQuorum(projectRoot: string | undefined, sessionId: string, options?: {
|
|
53
61
|
collocatedText?: string;
|
|
54
62
|
dueReason?: string | null;
|
|
55
63
|
fixture?: boolean;
|
|
64
|
+
/** Used only when fixture is true. Defaults to PASS. FAIL and UNREVIEWED do not complete the todo. */
|
|
65
|
+
fixtureVerdict?: SynthesisConsultReceipt['verdict'];
|
|
66
|
+
/** Fixture text is the real no-model localConferVote (UNREVIEWED), not a hardcoded PASS. */
|
|
67
|
+
fixtureNoLlm?: boolean;
|
|
68
|
+
/** Used only when fixture is true. Replaces the generated fixture text for every consult. */
|
|
69
|
+
fixtureOutput?: string;
|
|
70
|
+
/** Live path. Default writes an ask for the wearing TUI and does not spawn a server. */
|
|
71
|
+
host?: ConferHostPort;
|
|
56
72
|
}): Promise<ConferQuorumResult>;
|
|
57
73
|
export declare function formatConferQuorumReport(result: ConferQuorumResult): string;
|
package/dist/nucleus/confer.js
CHANGED
|
@@ -6,45 +6,41 @@ import * as fs from 'fs';
|
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { MANDATORY_MAJOR_CONSULTS } from './autonomy-kernel.js';
|
|
8
8
|
import { areSynthesisConsultTodosComplete, getNextRequiredTodo, getSynthesisConsultTodos, isSynthesisRealignmentPlan, loadPersistedLeadDevPlan, updatePlanTodoStatus, } from './lead-dev-plan-persistence.js';
|
|
9
|
-
import { buildReceiptFromConsultOutput, tryRecordSynthesisConsultReceipt, parseConsultVerdictFromText, } from './synthesis-consult-receipt.js';
|
|
9
|
+
import { buildReceiptFromConsultOutput, consultVerdictBlocksCompletion, loadSynthesisConsultReceipt, tryRecordSynthesisConsultReceipt, parseConsultVerdictFromText, } from './synthesis-consult-receipt.js';
|
|
10
|
+
import { formatGovernanceVoteText, localConferVote } from '../governance/local-confer.js';
|
|
10
11
|
import { isSynthesisCheckpointDue } from './synthesis.js';
|
|
11
|
-
import {
|
|
12
|
+
import { InProcessConferHost, SessionConferHost, } from './confer-host.js';
|
|
13
|
+
export { InProcessConferHost, SessionConferHost };
|
|
12
14
|
export const CONFER_AGENTS = [...MANDATORY_MAJOR_CONSULTS];
|
|
13
15
|
const STATE_VERSION = 1;
|
|
14
16
|
export function conferCheckpointPath(projectRoot = process.cwd()) {
|
|
15
17
|
return path.join(projectRoot, '.xray', 'state', 'confer-checkpoint.json');
|
|
16
18
|
}
|
|
17
19
|
export function defaultConferConfig() {
|
|
18
|
-
return { enabled:
|
|
20
|
+
return { enabled: false, on_synthesis: false };
|
|
19
21
|
}
|
|
22
|
+
/** Fixture PASS is a test harness. Live orchestrate-task and XRAY_CONFER_FIXTURE cannot reach it. */
|
|
23
|
+
export const CONFER_FIXTURE_UNREACHABLE = 'Confer fixture is unreachable outside tests';
|
|
24
|
+
export function conferFixtureAllowed() {
|
|
25
|
+
return process.env.NODE_ENV === 'test' || Boolean(process.env.VITEST);
|
|
26
|
+
}
|
|
27
|
+
/** A suit profile does not enable Confer. Missing confer key stays off, including strict. */
|
|
20
28
|
export function loadConferConfig(projectRoot = process.cwd()) {
|
|
21
29
|
const featuresPath = path.join(projectRoot, '.xray', 'features.json');
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return { enabled: true, on_synthesis: true };
|
|
25
|
-
}
|
|
26
|
-
if (!fs.existsSync(featuresPath)) {
|
|
27
|
-
return conferDefaultForProfile(profile)
|
|
28
|
-
? defaultConferConfig()
|
|
29
|
-
: { enabled: false, on_synthesis: false };
|
|
30
|
-
}
|
|
30
|
+
if (!fs.existsSync(featuresPath))
|
|
31
|
+
return defaultConferConfig();
|
|
31
32
|
try {
|
|
32
33
|
const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
|
|
33
34
|
const orch = data.multi_agent_orchestration ?? {};
|
|
34
35
|
const raw = orch.confer ?? {};
|
|
35
|
-
|
|
36
|
-
const optedIn = raw.enabled === true || orch.confer_on_synthesis === true;
|
|
37
|
-
return { enabled: optedIn, on_synthesis: optedIn };
|
|
38
|
-
}
|
|
36
|
+
const optedIn = raw.enabled === true || orch.confer_on_synthesis === true;
|
|
39
37
|
return {
|
|
40
|
-
enabled:
|
|
41
|
-
on_synthesis:
|
|
38
|
+
enabled: optedIn,
|
|
39
|
+
on_synthesis: optedIn && raw.on_synthesis !== false,
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
42
|
catch {
|
|
45
|
-
return
|
|
46
|
-
? defaultConferConfig()
|
|
47
|
-
: { enabled: false, on_synthesis: false };
|
|
43
|
+
return defaultConferConfig();
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
46
|
export function isConferEnabled(projectRoot = process.cwd()) {
|
|
@@ -116,7 +112,7 @@ export function conferAgentMcpTarget(subagent) {
|
|
|
116
112
|
switch (subagent) {
|
|
117
113
|
case 'architect-tools':
|
|
118
114
|
return {
|
|
119
|
-
server: 'architect-tools',
|
|
115
|
+
server: 'xray-architect-tools',
|
|
120
116
|
tool: 'architecture-assessment',
|
|
121
117
|
args: (prompt, projectRoot) => ({
|
|
122
118
|
projectRoot,
|
|
@@ -127,7 +123,7 @@ export function conferAgentMcpTarget(subagent) {
|
|
|
127
123
|
};
|
|
128
124
|
case 'researcher':
|
|
129
125
|
return {
|
|
130
|
-
server: 'researcher',
|
|
126
|
+
server: 'xray-researcher',
|
|
131
127
|
tool: 'analyze_proposal',
|
|
132
128
|
args: (prompt) => ({
|
|
133
129
|
proposalTitle: 'Synthesis confer — researcher',
|
|
@@ -139,7 +135,7 @@ export function conferAgentMcpTarget(subagent) {
|
|
|
139
135
|
case 'code-review':
|
|
140
136
|
default:
|
|
141
137
|
return {
|
|
142
|
-
server: 'code-review',
|
|
138
|
+
server: 'xray-code-review',
|
|
143
139
|
tool: 'analyze_proposal',
|
|
144
140
|
args: (prompt) => ({
|
|
145
141
|
proposalTitle: 'Synthesis confer — code-review',
|
|
@@ -150,38 +146,37 @@ export function conferAgentMcpTarget(subagent) {
|
|
|
150
146
|
};
|
|
151
147
|
}
|
|
152
148
|
}
|
|
153
|
-
function
|
|
154
|
-
const content = result?.content;
|
|
155
|
-
if (Array.isArray(content)) {
|
|
156
|
-
return content.map((c) => c.text ?? '').join('\n');
|
|
157
|
-
}
|
|
158
|
-
if (typeof result === 'string')
|
|
159
|
-
return result;
|
|
160
|
-
try {
|
|
161
|
-
return JSON.stringify(result);
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
return String(result);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
export async function invokeConferAgent(subagent, prompt, projectRoot = process.cwd()) {
|
|
168
|
-
const { mcpClientManager } = await import('../mcps/mcp-client.js');
|
|
149
|
+
function conferHostSeat(subagent, prompt, projectRoot, sessionId, todoId, dueReason) {
|
|
169
150
|
const target = conferAgentMcpTarget(subagent);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
151
|
+
return {
|
|
152
|
+
todoId,
|
|
153
|
+
subagent,
|
|
154
|
+
server: target.server,
|
|
155
|
+
tool: target.tool,
|
|
156
|
+
args: target.args(prompt, projectRoot),
|
|
157
|
+
sessionId,
|
|
158
|
+
prompt,
|
|
159
|
+
dueReason,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
export async function invokeConferAgent(subagent, prompt, projectRoot = process.cwd(), host = new SessionConferHost(), sessionId = '', todoId = subagent, dueReason = null) {
|
|
163
|
+
const call = await host.call(conferHostSeat(subagent, prompt, projectRoot, sessionId, todoId, dueReason), projectRoot);
|
|
164
|
+
if (call.status === 'pending') {
|
|
165
|
+
throw new Error(`Confer ask pending for ${subagent} at ${call.askPath}`);
|
|
166
|
+
}
|
|
167
|
+
const text = call.text;
|
|
173
168
|
if (!text.trim()) {
|
|
174
169
|
throw new Error(`Empty confer response from ${subagent}`);
|
|
175
170
|
}
|
|
176
171
|
if (!parseConsultVerdictFromText(text)) {
|
|
177
|
-
throw new Error(`Confer response from ${subagent} missing parseable verdict (expected Verdict: PASS|CONDITIONAL|FAIL or DECISION: approve|reject|abstain)`);
|
|
172
|
+
throw new Error(`Confer response from ${subagent} missing parseable verdict (expected an explicit Verdict: PASS|CONDITIONAL|FAIL|UNREVIEWED line, or DECISION: approve|reject|abstain)`);
|
|
178
173
|
}
|
|
179
174
|
return text;
|
|
180
175
|
}
|
|
181
176
|
export function applyConferConsultResult(todoId, subagent, sessionId, outputText, projectRoot = process.cwd()) {
|
|
182
177
|
const receipt = tryRecordSynthesisConsultReceipt(todoId, subagent, sessionId, outputText, projectRoot);
|
|
183
178
|
let todoCompleted = false;
|
|
184
|
-
if (receipt && receipt.verdict
|
|
179
|
+
if (receipt && !consultVerdictBlocksCompletion(receipt.verdict)) {
|
|
185
180
|
todoCompleted = updatePlanTodoStatus(todoId, 'completed', projectRoot);
|
|
186
181
|
}
|
|
187
182
|
return {
|
|
@@ -193,11 +188,24 @@ export function applyConferConsultResult(todoId, subagent, sessionId, outputText
|
|
|
193
188
|
verdict: receipt?.verdict ?? buildReceiptFromConsultOutput(todoId, subagent, sessionId, outputText)?.verdict ?? null,
|
|
194
189
|
};
|
|
195
190
|
}
|
|
196
|
-
export function
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
export function buildFixtureConferOutput(subagent, verdict = 'PASS', noLlm = false) {
|
|
192
|
+
if (noLlm) {
|
|
193
|
+
const role = subagent === 'code-review' ? 'code-review' : 'researcher';
|
|
194
|
+
return formatGovernanceVoteText(localConferVote({ role, llmConfigured: false }));
|
|
195
|
+
}
|
|
196
|
+
return `Verdict: ${verdict}\nTop risks: none\nHardening: confer fixture quorum for ${subagent}`;
|
|
197
|
+
}
|
|
198
|
+
export function writeFixtureConferReceipt(todoId, subagent, sessionId, projectRoot = process.cwd(), verdict = 'PASS', noLlm = false) {
|
|
199
|
+
return applyConferConsultResult(todoId, subagent, sessionId, buildFixtureConferOutput(subagent, verdict, noLlm), projectRoot);
|
|
199
200
|
}
|
|
200
201
|
export async function runConferQuorum(projectRoot = process.cwd(), sessionId, options = {}) {
|
|
202
|
+
if (options.fixture && !conferFixtureAllowed()) {
|
|
203
|
+
return {
|
|
204
|
+
status: 'failed',
|
|
205
|
+
agents: [],
|
|
206
|
+
message: CONFER_FIXTURE_UNREACHABLE,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
201
209
|
const cfg = loadConferConfig(projectRoot);
|
|
202
210
|
if (!cfg.enabled || !cfg.on_synthesis) {
|
|
203
211
|
return {
|
|
@@ -214,7 +222,7 @@ export async function runConferQuorum(projectRoot = process.cwd(), sessionId, op
|
|
|
214
222
|
message: 'No synthesis realignment plan — confer not applicable',
|
|
215
223
|
};
|
|
216
224
|
}
|
|
217
|
-
if (areSynthesisConsultTodosComplete(plan)) {
|
|
225
|
+
if (areSynthesisConsultTodosComplete(plan) && consultReceiptsAreRealApproves(plan, sessionId, projectRoot)) {
|
|
218
226
|
return {
|
|
219
227
|
status: 'completed',
|
|
220
228
|
agents: [],
|
|
@@ -228,17 +236,36 @@ export async function runConferQuorum(projectRoot = process.cwd(), sessionId, op
|
|
|
228
236
|
'# Synthesis confer\n\nReview checkpoint context and realign plan.';
|
|
229
237
|
const agents = [];
|
|
230
238
|
for (const todo of getSynthesisConsultTodos(plan)) {
|
|
231
|
-
if (todo.status === 'completed')
|
|
239
|
+
if (todo.status === 'completed' && consultReceiptIsRealPass(todo.id, sessionId, plan, projectRoot)) {
|
|
232
240
|
continue;
|
|
241
|
+
}
|
|
242
|
+
if (todo.status === 'completed') {
|
|
243
|
+
updatePlanTodoStatus(todo.id, 'pending', projectRoot);
|
|
244
|
+
}
|
|
233
245
|
try {
|
|
234
246
|
let agentResult;
|
|
235
247
|
if (options.fixture) {
|
|
236
|
-
agentResult =
|
|
248
|
+
agentResult =
|
|
249
|
+
options.fixtureOutput !== undefined
|
|
250
|
+
? applyConferConsultResult(todo.id, todo.subagent, sessionId, options.fixtureOutput, projectRoot)
|
|
251
|
+
: writeFixtureConferReceipt(todo.id, todo.subagent, sessionId, projectRoot, options.fixtureVerdict ?? 'PASS', options.fixtureNoLlm === true);
|
|
237
252
|
}
|
|
238
253
|
else {
|
|
239
254
|
const prompt = buildConferPrompt(todo.subagent, todo.task, collocated, options.dueReason ?? null);
|
|
240
|
-
const
|
|
241
|
-
|
|
255
|
+
const host = options.host ?? new SessionConferHost();
|
|
256
|
+
const call = await host.call(conferHostSeat(todo.subagent, prompt, projectRoot, sessionId, todo.id, options.dueReason ?? null), projectRoot);
|
|
257
|
+
if (call.status === 'pending') {
|
|
258
|
+
agents.push({
|
|
259
|
+
todoId: todo.id,
|
|
260
|
+
subagent: todo.subagent,
|
|
261
|
+
outputText: '',
|
|
262
|
+
receiptRecorded: false,
|
|
263
|
+
todoCompleted: false,
|
|
264
|
+
verdict: null,
|
|
265
|
+
});
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
agentResult = applyConferConsultResult(todo.id, todo.subagent, sessionId, call.text, projectRoot);
|
|
242
269
|
}
|
|
243
270
|
agents.push(agentResult);
|
|
244
271
|
if (agentResult.receiptRecorded && agentResult.todoCompleted) {
|
|
@@ -247,7 +274,9 @@ export async function runConferQuorum(projectRoot = process.cwd(), sessionId, op
|
|
|
247
274
|
}
|
|
248
275
|
else {
|
|
249
276
|
state.status = 'failed';
|
|
250
|
-
state.lastError =
|
|
277
|
+
state.lastError = consultVerdictBlocksCompletion(agentResult.verdict)
|
|
278
|
+
? `${agentResult.verdict} blocks ${todo.id}`
|
|
279
|
+
: `Receipt or todo completion failed for ${todo.id}`;
|
|
251
280
|
saveConferCheckpoint(state, projectRoot);
|
|
252
281
|
return {
|
|
253
282
|
status: 'partial',
|
|
@@ -273,10 +302,21 @@ export async function runConferQuorum(projectRoot = process.cwd(), sessionId, op
|
|
|
273
302
|
return { status: 'failed', agents, message };
|
|
274
303
|
}
|
|
275
304
|
}
|
|
305
|
+
if (agents.some((agent) => agent.verdict === null && !agent.receiptRecorded && !agent.error)) {
|
|
306
|
+
state.status = 'pending';
|
|
307
|
+
delete state.lastError;
|
|
308
|
+
saveConferCheckpoint(state, projectRoot);
|
|
309
|
+
return {
|
|
310
|
+
status: 'pending',
|
|
311
|
+
agents,
|
|
312
|
+
message: 'Confer asks written. The wearing TUI calls its MCP servers and records each verdict.',
|
|
313
|
+
};
|
|
314
|
+
}
|
|
276
315
|
const refreshed = loadPersistedLeadDevPlan(projectRoot);
|
|
277
316
|
const done = refreshed &&
|
|
278
317
|
isSynthesisRealignmentPlan(refreshed) &&
|
|
279
|
-
areSynthesisConsultTodosComplete(refreshed)
|
|
318
|
+
areSynthesisConsultTodosComplete(refreshed) &&
|
|
319
|
+
consultReceiptsAreRealApproves(refreshed, sessionId, projectRoot);
|
|
280
320
|
state.status = done ? 'completed' : 'failed';
|
|
281
321
|
if (!done)
|
|
282
322
|
state.lastError = 'Consult todos remain after confer loop';
|
|
@@ -289,6 +329,22 @@ export async function runConferQuorum(projectRoot = process.cwd(), sessionId, op
|
|
|
289
329
|
: (state.lastError ?? 'Confer incomplete'),
|
|
290
330
|
};
|
|
291
331
|
}
|
|
332
|
+
function consultReceiptIsRealPass(todoId, sessionId, plan, projectRoot) {
|
|
333
|
+
const receipt = loadSynthesisConsultReceipt(todoId, projectRoot);
|
|
334
|
+
if (receipt?.verdict !== 'PASS')
|
|
335
|
+
return false;
|
|
336
|
+
if (sessionId && receipt.sessionId !== sessionId)
|
|
337
|
+
return false;
|
|
338
|
+
if (plan.consultCycleId && receipt.cycleId !== plan.consultCycleId)
|
|
339
|
+
return false;
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
function consultReceiptsAreRealApproves(plan, sessionId, projectRoot) {
|
|
343
|
+
const todos = getSynthesisConsultTodos(plan);
|
|
344
|
+
if (todos.length === 0)
|
|
345
|
+
return false;
|
|
346
|
+
return todos.every((todo) => consultReceiptIsRealPass(todo.id, sessionId, plan, projectRoot));
|
|
347
|
+
}
|
|
292
348
|
const CONFER_AGENT_EMOJI = {
|
|
293
349
|
researcher: '🔍',
|
|
294
350
|
'architect-tools': '🏗️',
|
|
@@ -7,7 +7,7 @@ import * as path from 'path';
|
|
|
7
7
|
import { SYNTHESIS_REALIGNMENT_PHASE_ID } from './autonomy-kernel.js';
|
|
8
8
|
import { DEFAULT_PENDING_TTL_MS } from './pending-delegations.js';
|
|
9
9
|
import { completeSynthesisCheckpoint, getSynthesisCheckpointSessionId, isSynthesisCheckpointDue, recordExecutionSlice, } from './synthesis.js';
|
|
10
|
-
import { hasValidSynthesisConsultReceipt, isSynthesisConsultTodoId, loadSynthesisConsultReceipt, } from './synthesis-consult-receipt.js';
|
|
10
|
+
import { consultVerdictBlocksCompletion, hasValidSynthesisConsultReceipt, isSynthesisConsultTodoId, loadSynthesisConsultReceipt, } from './synthesis-consult-receipt.js';
|
|
11
11
|
import { resolveSpawnPlan } from './spawn-plan-resolution.js';
|
|
12
12
|
import { isAsideActiveForSession, isUserAsideTodoId, isUserAsidesEnabled, parseAsideIdFromTodoId, findAsideContainingTodo, updateUserAsideTodoStatus, } from './user-aside.js';
|
|
13
13
|
export const DEFAULT_PLAN_STALE_MS = 8 * 60 * 60 * 1000;
|
|
@@ -259,11 +259,13 @@ export function updatePlanTodoStatus(todoId, status, projectRoot = process.cwd()
|
|
|
259
259
|
receiptExpected.sessionId = plan.sessionId;
|
|
260
260
|
if (targetTodo?.subagent)
|
|
261
261
|
receiptExpected.subagent = targetTodo.subagent;
|
|
262
|
+
if (plan.consultCycleId)
|
|
263
|
+
receiptExpected.cycleId = plan.consultCycleId;
|
|
262
264
|
if (!hasValidSynthesisConsultReceipt(todoId, projectRoot, receiptExpected)) {
|
|
263
265
|
return false;
|
|
264
266
|
}
|
|
265
267
|
const receipt = loadSynthesisConsultReceipt(todoId, projectRoot);
|
|
266
|
-
if (receipt?.verdict
|
|
268
|
+
if (consultVerdictBlocksCompletion(receipt?.verdict)) {
|
|
267
269
|
return false;
|
|
268
270
|
}
|
|
269
271
|
}
|