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
|
@@ -20,7 +20,10 @@ const {
|
|
|
20
20
|
const { join, resolve } = require("path");
|
|
21
21
|
|
|
22
22
|
const HOOKS_DIR = __dirname;
|
|
23
|
-
const { plateStockLine, recallPlate
|
|
23
|
+
const { ensureWornPlate, plateStockLine, recallPlate } = require("./plates.cjs");
|
|
24
|
+
const { rememberSessionNote, retainProjectNotes } = require("./session-note-index.cjs");
|
|
25
|
+
const { formatCascadePointer, readWakeCascade } = require("./wake-cascade.cjs");
|
|
26
|
+
const { formatSleevePointer, readSleeve, runFoundryMill } = require("./sleeve.cjs");
|
|
24
27
|
|
|
25
28
|
const INTENT_MAX = 240;
|
|
26
29
|
|
|
@@ -111,6 +114,12 @@ function isStockTicket(value) {
|
|
|
111
114
|
return !text || text === "(none)" || text === "(none yet)";
|
|
112
115
|
}
|
|
113
116
|
|
|
117
|
+
function readStationHostToken(root) {
|
|
118
|
+
const md = readExistingStationMarkdown(root);
|
|
119
|
+
const match = md.match(/^Host:\s*(\S+)/m);
|
|
120
|
+
return match ? match[1] : "";
|
|
121
|
+
}
|
|
122
|
+
|
|
114
123
|
function readStationTicketField(root, field) {
|
|
115
124
|
const md = readExistingStationMarkdown(root);
|
|
116
125
|
const re = field === "Plan" ? /^Plan:\s*(.*)$/im : /^Intent:\s*(.*)$/im;
|
|
@@ -136,9 +145,20 @@ function resolveHeatIntent(root, extra, existing) {
|
|
|
136
145
|
const pickup = clipIntent(readNotesPickup(root));
|
|
137
146
|
const bootIntent = typeof existing.intent === "string" ? clipIntent(existing.intent) : null;
|
|
138
147
|
const cardIsBootEcho = Boolean(card && bootIntent && card === bootIntent);
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
148
|
+
const named = String(extra.hookEvent || "");
|
|
149
|
+
const channel = String(extra.hookEvent || extra.source || "");
|
|
150
|
+
const keepsTicket = /user_prompt|compact/i.test(channel);
|
|
151
|
+
const compact = /compact/i.test(channel);
|
|
152
|
+
let receipt = null;
|
|
153
|
+
try {
|
|
154
|
+
receipt = readSleeve(root);
|
|
155
|
+
} catch {
|
|
156
|
+
receipt = null;
|
|
157
|
+
}
|
|
158
|
+
// A proved receipt keeps the ticket. A new session must not restate it.
|
|
159
|
+
if (receipt && receipt.on && card && /^(session_start|session-start)$/i.test(named)) {
|
|
160
|
+
return { intent: card, rematch: false, kept: true };
|
|
161
|
+
}
|
|
142
162
|
// A chat sentence or a compact summary does not replace a ticket already on disk.
|
|
143
163
|
if (incoming && !(keepsTicket && (card || pickup))) {
|
|
144
164
|
return { intent: incoming, rematch: true };
|
|
@@ -520,6 +540,7 @@ function stampNotesPickup(root, line) {
|
|
|
520
540
|
: body
|
|
521
541
|
? `${marker}\n\n${body.replace(/^\n+/, "")}`
|
|
522
542
|
: `${marker}\n`;
|
|
543
|
+
rememberSessionNote(root, text, { source: "NOTES.md", slot: "pickup" });
|
|
523
544
|
if (next === body) return text;
|
|
524
545
|
mkdirSync(join(root, ".xray", "state"), { recursive: true });
|
|
525
546
|
writeFileSync(dest, next.endsWith("\n") ? next : `${next}\n`);
|
|
@@ -973,6 +994,7 @@ function buildRepertoireResume(root) {
|
|
|
973
994
|
}
|
|
974
995
|
|
|
975
996
|
function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
997
|
+
retainProjectNotes(root);
|
|
976
998
|
const mr = readMemoryRoutingConfig(root);
|
|
977
999
|
const memoryOff = isExplicitMemoryRoutingOptOut(mr);
|
|
978
1000
|
const promptHook = isPromptHook(extra);
|
|
@@ -1005,6 +1027,8 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
1005
1027
|
? existing.hotSwap
|
|
1006
1028
|
: null;
|
|
1007
1029
|
const hotSwap = nextSwap || keptSwap;
|
|
1030
|
+
const priorIntent = readStationTicketField(root, "Intent");
|
|
1031
|
+
const priorHost = readStationHostToken(root);
|
|
1008
1032
|
const resolved = resolveHeatIntent(root, extra, existing);
|
|
1009
1033
|
const intent = resolved.intent;
|
|
1010
1034
|
const rematch = resolved.rematch;
|
|
@@ -1018,7 +1042,10 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
1018
1042
|
}
|
|
1019
1043
|
const matchText = clipIntent([intent, pickup, approaches].filter(Boolean).join(" "));
|
|
1020
1044
|
const git = readGitBrief(root);
|
|
1021
|
-
|
|
1045
|
+
let planLine = resolveHeatPlan(root, extra, existing);
|
|
1046
|
+
if (priorIntent && intent && intent !== priorIntent && !(extra.plan || extra.planLine)) {
|
|
1047
|
+
planLine = null;
|
|
1048
|
+
}
|
|
1022
1049
|
// A passed string is the previous card. Count the project file after this wake.
|
|
1023
1050
|
const repertoireResume = buildRepertoireResume(root);
|
|
1024
1051
|
const swapBit = hotSwap ? `hot-swap ${hotSwap.from} → ${hotSwap.to}` : `host ${host}`;
|
|
@@ -1056,11 +1083,20 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
1056
1083
|
if (!matchedSignals.length && priorWorking) {
|
|
1057
1084
|
matchedSignals = stationSafeSignals(priorWorking.matchedSignals);
|
|
1058
1085
|
}
|
|
1086
|
+
if (resolved.kept) {
|
|
1087
|
+
matchedSignals = preferLawHits(priorWorking && priorWorking.matchedSignals, 8);
|
|
1088
|
+
}
|
|
1059
1089
|
const compactHold = retainCompactFields(existing, extra);
|
|
1060
1090
|
const nextHook = compactHold.hookEvent || extra.hookEvent || extra.source || null;
|
|
1061
1091
|
const workingSnapshot = {
|
|
1062
1092
|
host,
|
|
1063
1093
|
intent,
|
|
1094
|
+
priorIntent: priorIntent || null,
|
|
1095
|
+
priorHost: priorHost || null,
|
|
1096
|
+
priorWorkingIntent: priorWorking && typeof priorWorking.intent === "string" ? priorWorking.intent : null,
|
|
1097
|
+
priorWorkingHost: priorWorking && typeof priorWorking.host === "string"
|
|
1098
|
+
? (priorWorking.host.split(/\s+/)[0] || null)
|
|
1099
|
+
: null,
|
|
1064
1100
|
git,
|
|
1065
1101
|
hotSwap,
|
|
1066
1102
|
memoryRouting: repertoireResume.startsWith("Repertoire: on") ? "on" : "off",
|
|
@@ -1074,6 +1110,13 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
1074
1110
|
const growReceipt = workingGrowSnapshot(grow);
|
|
1075
1111
|
if (growReceipt) workingSnapshot.grow = growReceipt;
|
|
1076
1112
|
if (matchedSignals.length) workingSnapshot.matchedSignals = matchedSignals.slice(0, 8);
|
|
1113
|
+
const cascade = readWakeCascade(root, { laws: matchedSignals });
|
|
1114
|
+
workingSnapshot.cascade = {
|
|
1115
|
+
plane: cascade.plane,
|
|
1116
|
+
laws: cascade.laws,
|
|
1117
|
+
notes: cascade.notes.length,
|
|
1118
|
+
};
|
|
1119
|
+
const cascadeLine = formatCascadePointer(cascade);
|
|
1077
1120
|
const opProcNames = readOpProcNames(root);
|
|
1078
1121
|
if (opProcNames.length) workingSnapshot.opProcNames = opProcNames;
|
|
1079
1122
|
const pickupChanged = Boolean(pickup && (!priorWorking || priorWorking.pickup !== pickup));
|
|
@@ -1106,8 +1149,10 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
1106
1149
|
repertoireResume,
|
|
1107
1150
|
workingLine,
|
|
1108
1151
|
stationLine,
|
|
1152
|
+
cascadeLine,
|
|
1109
1153
|
...(notesLine ? { notesLine } : {}),
|
|
1110
1154
|
...compactHold,
|
|
1155
|
+
arrivedHook: extra && extra.hookEvent ? String(extra.hookEvent) : "",
|
|
1111
1156
|
};
|
|
1112
1157
|
}
|
|
1113
1158
|
|
|
@@ -1122,7 +1167,12 @@ const STOCK_STATION_PREFIXES = [
|
|
|
1122
1167
|
"working:",
|
|
1123
1168
|
"plate:",
|
|
1124
1169
|
"library:",
|
|
1170
|
+
"cascade:",
|
|
1171
|
+
"sleeve:",
|
|
1125
1172
|
"notes:",
|
|
1173
|
+
"plane:",
|
|
1174
|
+
"judgement:",
|
|
1175
|
+
"payments:",
|
|
1126
1176
|
];
|
|
1127
1177
|
|
|
1128
1178
|
/** Stock design map. Exact lines so a later heat does not preserve a second copy. */
|
|
@@ -1318,6 +1368,11 @@ function formatStationMarkdown(fields) {
|
|
|
1318
1368
|
const plateLine = plateStockLine(fields.intent);
|
|
1319
1369
|
if (plateLine) lines.push(plateLine);
|
|
1320
1370
|
lines.push("Library: record-map — .agents/skills/record-map/SKILL.md");
|
|
1371
|
+
if (fields.cascadeLine) lines.push(fields.cascadeLine);
|
|
1372
|
+
if (fields.sleeveLine) lines.push(fields.sleeveLine);
|
|
1373
|
+
lines.push(`Plane: ${fields.plane || "(none)"}`);
|
|
1374
|
+
lines.push(`Judgement: ${fields.judgement || "start"}`);
|
|
1375
|
+
lines.push(`Payments: ${fields.payments === "on" ? "on" : "off"}`);
|
|
1321
1376
|
if (fields.notesLine) lines.push(fields.notesLine);
|
|
1322
1377
|
lines.push("");
|
|
1323
1378
|
lines.push(...DESIGN_MAP_LINES);
|
|
@@ -1338,19 +1393,160 @@ function readExistingStationMarkdown(root) {
|
|
|
1338
1393
|
}
|
|
1339
1394
|
}
|
|
1340
1395
|
|
|
1396
|
+
|
|
1397
|
+
|
|
1398
|
+
function ticketIsPayment(intent) {
|
|
1399
|
+
return /\b(pay|payment|payments|x402|invoice)\b/i.test(String(intent || ""));
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
function readHeldPlane(root) {
|
|
1403
|
+
try {
|
|
1404
|
+
const data = JSON.parse(readFileSync(join(root, ".xray", "state", "goggles-plane.json"), "utf8"));
|
|
1405
|
+
return data && typeof data.plane === "string" ? data.plane : "";
|
|
1406
|
+
} catch {
|
|
1407
|
+
return "";
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function missKey(proved) {
|
|
1412
|
+
const missing = proved && Array.isArray(proved.missing) ? proved.missing.slice() : [];
|
|
1413
|
+
const rows = proved && Array.isArray(proved.stitches) ? proved.stitches : [];
|
|
1414
|
+
const at = {};
|
|
1415
|
+
for (const row of rows) {
|
|
1416
|
+
if (!row || !missing.includes(row.name)) continue;
|
|
1417
|
+
at[row.name] = Array.isArray(row.at) ? row.at.slice() : [];
|
|
1418
|
+
}
|
|
1419
|
+
return JSON.stringify({ missing, at });
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
function judgementLine(order, saved, proved) {
|
|
1423
|
+
const wake = order && order.wake ? order.wake : "start";
|
|
1424
|
+
if (wake === "repair") {
|
|
1425
|
+
const prior = saved && Number.isFinite(saved.retries) ? saved.retries : 0;
|
|
1426
|
+
const key = missKey(proved);
|
|
1427
|
+
if (saved && saved.missKey === key && prior > 0) return `retry ${prior}`;
|
|
1428
|
+
const next = prior + 1;
|
|
1429
|
+
if (saved) {
|
|
1430
|
+
saved.retries = next;
|
|
1431
|
+
saved.missKey = key;
|
|
1432
|
+
}
|
|
1433
|
+
return `retry ${next}`;
|
|
1434
|
+
}
|
|
1435
|
+
if (saved) {
|
|
1436
|
+
saved.retries = 0;
|
|
1437
|
+
delete saved.missKey;
|
|
1438
|
+
}
|
|
1439
|
+
if (wake === "continue" || wake === "stop" || wake === "start") return wake;
|
|
1440
|
+
return "start";
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
function isMissPlan(line) {
|
|
1444
|
+
return typeof line === "string" && line.startsWith("The work is ");
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
function missWork(missing) {
|
|
1448
|
+
const words = { loop: "the ticket", domain: "the plate", state: "the record", foundry: "the mill" };
|
|
1449
|
+
const parts = (Array.isArray(missing) ? missing : []).map((name) => words[name]).filter(Boolean);
|
|
1450
|
+
if (parts.length === 1) return `The work is ${parts[0]}.`;
|
|
1451
|
+
if (parts.length > 1) {
|
|
1452
|
+
const last = parts[parts.length - 1];
|
|
1453
|
+
return `The work is ${parts.slice(0, -1).join(", ")} and ${last}.`;
|
|
1454
|
+
}
|
|
1455
|
+
return "The work is the suit.";
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function wakeOrder(saved, proved, fields) {
|
|
1459
|
+
const missing = proved && Array.isArray(proved.missing) ? proved.missing : [];
|
|
1460
|
+
const sameJob = Boolean(saved && saved.priorIntent && saved.intent === saved.priorIntent);
|
|
1461
|
+
const suitMiss = missing.some((name) => name === "domain" || name === "foundry" || name === "state");
|
|
1462
|
+
if (proved && proved.on) {
|
|
1463
|
+
const paused = saved && typeof saved.pausedPlan === "string" ? saved.pausedPlan : "";
|
|
1464
|
+
if (isMissPlan(fields && fields.planLine) && saved && Object.prototype.hasOwnProperty.call(saved, "pausedPlan")) {
|
|
1465
|
+
return { wake: "continue", planLine: paused, pausedPlan: undefined };
|
|
1466
|
+
}
|
|
1467
|
+
return { wake: "continue", pausedPlan: undefined };
|
|
1468
|
+
}
|
|
1469
|
+
if (!saved || !saved.priorIntent) return { wake: "start" };
|
|
1470
|
+
if (saved.intent && saved.priorIntent && saved.intent !== saved.priorIntent) {
|
|
1471
|
+
return { wake: "stop", pausedPlan: undefined };
|
|
1472
|
+
}
|
|
1473
|
+
if (sameJob && !missing.includes("loop") && suitMiss) {
|
|
1474
|
+
const current = fields && fields.planLine ? String(fields.planLine) : "";
|
|
1475
|
+
const paused = isMissPlan(current)
|
|
1476
|
+
? (typeof saved.pausedPlan === "string" ? saved.pausedPlan : "")
|
|
1477
|
+
: current;
|
|
1478
|
+
return { wake: "repair", planLine: missWork(missing), pausedPlan: paused };
|
|
1479
|
+
}
|
|
1480
|
+
return { wake: "start" };
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1341
1483
|
function writeStationMarkdown(root, fields) {
|
|
1342
1484
|
try {
|
|
1343
1485
|
const dir = join(root, ".xray", "state");
|
|
1344
1486
|
mkdirSync(dir, { recursive: true });
|
|
1487
|
+
const arrivedKnown = Boolean(fields && Object.prototype.hasOwnProperty.call(fields, "arrivedHook"));
|
|
1488
|
+
const wake = arrivedKnown
|
|
1489
|
+
? String(fields.arrivedHook || "")
|
|
1490
|
+
: String((fields && fields.hookEvent) || "");
|
|
1491
|
+
if (/^(session_start|session-start|pre_compact|post_compact)$/i.test(wake)) {
|
|
1492
|
+
try {
|
|
1493
|
+
runFoundryMill(root);
|
|
1494
|
+
} catch {
|
|
1495
|
+
/* the card still writes when the mill does not */
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
const dest = stationMarkdownPath(root);
|
|
1499
|
+
const base = { ...(fields || {}) };
|
|
1500
|
+
delete base.sleeveLine;
|
|
1501
|
+
writeFileSync(dest, mergeStationMarkdown(formatStationMarkdown(base), readExistingStationMarkdown(root)));
|
|
1502
|
+
// Solar phase is not this judgement. The moral overlay cannot pass alone.
|
|
1503
|
+
fields.plane = readHeldPlane(root);
|
|
1504
|
+
fields.payments = ticketIsPayment(fields.intent) ? "on" : "off";
|
|
1505
|
+
let sleeveLine = "Sleeve: off loop, domain, state, foundry";
|
|
1506
|
+
try {
|
|
1507
|
+
const proved = readSleeve(root);
|
|
1508
|
+
sleeveLine = formatSleevePointer(proved);
|
|
1509
|
+
const workingPath = join(root, ".xray", "state", "repertoire-working.json");
|
|
1510
|
+
if (existsSync(workingPath)) {
|
|
1511
|
+
const saved = JSON.parse(readFileSync(workingPath, "utf8"));
|
|
1512
|
+
if (saved && typeof saved === "object") {
|
|
1513
|
+
const order = wakeOrder(saved, proved, fields);
|
|
1514
|
+
fields.wake = order.wake;
|
|
1515
|
+
if (Object.prototype.hasOwnProperty.call(order, "planLine")) {
|
|
1516
|
+
fields.planLine = order.planLine;
|
|
1517
|
+
}
|
|
1518
|
+
fields.judgement = judgementLine(order, saved, proved);
|
|
1519
|
+
saved.wake = order.wake;
|
|
1520
|
+
saved.retries = saved.retries || 0;
|
|
1521
|
+
saved.judgement = fields.judgement;
|
|
1522
|
+
saved.plane = fields.plane;
|
|
1523
|
+
saved.payments = fields.payments;
|
|
1524
|
+
if (order.pausedPlan !== undefined) saved.pausedPlan = order.pausedPlan;
|
|
1525
|
+
else delete saved.pausedPlan;
|
|
1526
|
+
saved.sleeve = {
|
|
1527
|
+
on: proved.on,
|
|
1528
|
+
missing: proved.missing,
|
|
1529
|
+
stitches: proved.stitches,
|
|
1530
|
+
};
|
|
1531
|
+
writeFileSync(workingPath, `${JSON.stringify(saved)}\n`);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
} catch {
|
|
1535
|
+
/* the card still writes; the sleeve stays off */
|
|
1536
|
+
}
|
|
1537
|
+
writeFileSync(
|
|
1538
|
+
dest,
|
|
1539
|
+
mergeStationMarkdown(
|
|
1540
|
+
formatStationMarkdown({ ...(fields || {}), sleeveLine }),
|
|
1541
|
+
readExistingStationMarkdown(root),
|
|
1542
|
+
),
|
|
1543
|
+
);
|
|
1345
1544
|
try {
|
|
1346
1545
|
const plate = recallPlate(fields && fields.intent);
|
|
1347
|
-
if (plate)
|
|
1546
|
+
if (plate) ensureWornPlate(root, plate.id);
|
|
1348
1547
|
} catch {
|
|
1349
1548
|
/* a missing plate doc must not block the card */
|
|
1350
1549
|
}
|
|
1351
|
-
const dest = stationMarkdownPath(root);
|
|
1352
|
-
const next = mergeStationMarkdown(formatStationMarkdown(fields), readExistingStationMarkdown(root));
|
|
1353
|
-
writeFileSync(dest, next);
|
|
1354
1550
|
return dest;
|
|
1355
1551
|
} catch {
|
|
1356
1552
|
return null;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One wake hands the lens, the ticket, the laws, and the current notes.
|
|
3
|
+
* Goggles names plane record-map.
|
|
4
|
+
* The station module writes STATION.md and NOTES.md.
|
|
5
|
+
* Repertoire keeps the law list.
|
|
6
|
+
* The current session-note index sits beside that list.
|
|
7
|
+
* The card carries the count. A memory look carries the current lines.
|
|
8
|
+
*/
|
|
9
|
+
const { existsSync, readFileSync } = require("fs");
|
|
10
|
+
const { join } = require("path");
|
|
11
|
+
const { shortSessionIndex } = require("./session-note-index.cjs");
|
|
12
|
+
|
|
13
|
+
const CASCADE_PLANE = "record-map";
|
|
14
|
+
const MEMORY_PLANES = ["record-map", "station-card", "notes-page", "memory-recall"];
|
|
15
|
+
|
|
16
|
+
function isCascadePlane(id) {
|
|
17
|
+
return MEMORY_PLANES.includes(String(id || ""));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function clipNames(names) {
|
|
21
|
+
if (!Array.isArray(names)) return [];
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const raw of names) {
|
|
24
|
+
const name = String(raw || "").trim();
|
|
25
|
+
if (!name || out.includes(name)) continue;
|
|
26
|
+
out.push(name);
|
|
27
|
+
if (out.length >= 8) break;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function lawNamesFromWorking(root) {
|
|
33
|
+
const file = join(root, ".xray", "state", "repertoire-working.json");
|
|
34
|
+
if (!existsSync(file)) return [];
|
|
35
|
+
try {
|
|
36
|
+
const data = JSON.parse(readFileSync(file, "utf8"));
|
|
37
|
+
return clipNames(data && data.matchedSignals);
|
|
38
|
+
} catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function currentNotes(root) {
|
|
44
|
+
try {
|
|
45
|
+
return shortSessionIndex(root).map((note) => {
|
|
46
|
+
const row = { id: note.id, text: note.text };
|
|
47
|
+
if (note.slot) row.slot = note.slot;
|
|
48
|
+
return row;
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Laws come from this wake when passed. Otherwise the working file. Notes are current lines only. */
|
|
56
|
+
function readWakeCascade(root, opts = {}) {
|
|
57
|
+
const laws = Array.isArray(opts.laws) ? clipNames(opts.laws) : lawNamesFromWorking(root);
|
|
58
|
+
return {
|
|
59
|
+
plane: CASCADE_PLANE,
|
|
60
|
+
station: ".xray/state/STATION.md",
|
|
61
|
+
notesPage: ".xray/state/NOTES.md",
|
|
62
|
+
index: ".xray/state/repertoire/session-notes.json",
|
|
63
|
+
laws,
|
|
64
|
+
notes: currentNotes(root),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatCascadePointer(cascade) {
|
|
69
|
+
const count = cascade && Array.isArray(cascade.notes) ? cascade.notes.length : 0;
|
|
70
|
+
const plane = cascade && cascade.plane ? cascade.plane : CASCADE_PLANE;
|
|
71
|
+
return `Cascade: ${plane} · notes ${count} current`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function formatCascadeReading(cascade) {
|
|
75
|
+
const plane = cascade && cascade.plane ? cascade.plane : CASCADE_PLANE;
|
|
76
|
+
const laws = cascade && Array.isArray(cascade.laws) && cascade.laws.length
|
|
77
|
+
? cascade.laws.join(", ")
|
|
78
|
+
: "(none yet)";
|
|
79
|
+
const notes = cascade && Array.isArray(cascade.notes) ? cascade.notes : [];
|
|
80
|
+
const lines = [
|
|
81
|
+
`Cascade: ${plane}`,
|
|
82
|
+
`Station: ${cascade && cascade.station ? cascade.station : ".xray/state/STATION.md"}`,
|
|
83
|
+
`Notes page: ${cascade && cascade.notesPage ? cascade.notesPage : ".xray/state/NOTES.md"}`,
|
|
84
|
+
`Index: ${cascade && cascade.index ? cascade.index : ".xray/state/repertoire/session-notes.json"}`,
|
|
85
|
+
`Laws: ${laws}`,
|
|
86
|
+
`Notes: ${notes.length} current`,
|
|
87
|
+
];
|
|
88
|
+
for (const note of notes) {
|
|
89
|
+
const slot = note.slot ? `${note.slot} ` : "";
|
|
90
|
+
lines.push(`- ${note.id} ${slot}${note.text}`);
|
|
91
|
+
}
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
CASCADE_PLANE,
|
|
97
|
+
MEMORY_PLANES,
|
|
98
|
+
isCascadePlane,
|
|
99
|
+
readWakeCascade,
|
|
100
|
+
formatCascadePointer,
|
|
101
|
+
formatCascadeReading,
|
|
102
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESM face for wake-cascade.cjs.
|
|
3
|
+
*/
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const impl = require(join(dirname(fileURLToPath(import.meta.url)), "wake-cascade.cjs"));
|
|
10
|
+
|
|
11
|
+
export const CASCADE_PLANE = impl.CASCADE_PLANE;
|
|
12
|
+
export const MEMORY_PLANES = impl.MEMORY_PLANES;
|
|
13
|
+
export const isCascadePlane = impl.isCascadePlane;
|
|
14
|
+
export const readWakeCascade = impl.readWakeCascade;
|
|
15
|
+
export const formatCascadePointer = impl.formatCascadePointer;
|
|
16
|
+
export const formatCascadeReading = impl.formatCascadeReading;
|
|
@@ -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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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",
|