0xray 3.4.7 → 3.4.9
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/AGENTS-consumer.md +1 -1
- package/AGENTS.md +1 -1
- package/README.md +15 -1
- package/SKILLS.md +1 -1
- package/dist/AGENTS.md +1 -1
- package/dist/CHANGELOG.md +28 -0
- package/dist/README.md +15 -1
- package/dist/SKILLS.md +1 -1
- package/dist/governance/governance-service.js +3 -1
- package/dist/governance/llm-governance-provider.d.ts +2 -0
- package/dist/governance/llm-governance-provider.js +146 -104
- package/dist/mcps/governance.server.d.ts +1 -0
- package/dist/mcps/governance.server.js +79 -8
- package/dist/mcps/knowledge-skills/code-review.server.js +1 -1
- package/dist/mcps/knowledge-skills/security-audit.server.js +1 -1
- package/dist/mcps/orchestrator/config/memory-routing-bridge.js +2 -1
- package/dist/mcps/orchestrator/execution/execution-planner.d.ts +2 -0
- package/dist/mcps/orchestrator/execution/execution-planner.js +22 -11
- package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +13 -1
- package/dist/mcps/orchestrator/handlers/complexity-handler.js +40 -4
- package/dist/mcps/orchestrator/handlers/task-handler.js +13 -6
- package/dist/mcps/orchestrator/server.js +9 -1
- package/dist/mcps/orchestrator/types.d.ts +4 -1
- package/dist/mcps/researcher.server.js +1 -1
- package/dist/nucleus/autonomy-kernel.d.ts +5 -1
- package/dist/nucleus/autonomy-kernel.js +20 -16
- package/dist/skills/orchestrator/SKILL.md +3 -2
- package/package.json +1 -1
- package/scripts/node/release-gate.mjs +14 -6
- package/src/mcps/governance.server.ts +81 -8
- package/src/mcps/knowledge-skills/code-review.server.ts +1 -1
- package/src/mcps/knowledge-skills/security-audit.server.ts +1 -1
- package/src/mcps/orchestrator/config/memory-routing-bridge.ts +3 -1
- package/src/mcps/orchestrator/execution/execution-planner.ts +26 -11
- package/src/mcps/orchestrator/handlers/complexity-handler.ts +62 -5
- package/src/mcps/orchestrator/handlers/task-handler.ts +22 -6
- package/src/mcps/orchestrator/server.ts +9 -1
- package/src/mcps/orchestrator/types.ts +4 -1
- package/src/mcps/researcher.server.ts +1 -1
- package/src/skills/orchestrator/SKILL.md +3 -2
package/AGENTS-consumer.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Quick reference for the 0xRay AI orchestration framework (shipped to your project on `npm install 0xray`).
|
|
6
6
|
|
|
7
|
-
**v3.4.
|
|
7
|
+
**v3.4.9** — 7 MCP servers · 45 skills · 68 codex terms · 4 platform bridges
|
|
8
8
|
|
|
9
9
|
## Postinstall (automatic)
|
|
10
10
|
|
package/AGENTS.md
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# xray — MCP-Centric AI Governance OS
|
|
2
2
|
|
|
3
|
-
**v3.4.
|
|
3
|
+
**v3.4.9** — 42 agents · 45 skills · 7 MCP servers · 68 codex terms · 3,226 tests
|
|
4
|
+
|
|
5
|
+
## Your AI Power Suit
|
|
6
|
+
|
|
7
|
+
**Your AI Power ⚡️ Suit 🦾** — an exoskeleton that blocks AI slop from becoming real damage.
|
|
8
|
+
|
|
9
|
+
- learns with **Repertoire** 🧠
|
|
10
|
+
- has a **Solar Ban Hammer** ☀️🔨 (Dynamo governance)
|
|
11
|
+
- can also **Confer** 🗣️ (3-agent internal deliberation)
|
|
12
|
+
|
|
13
|
+
The core AI Governance OS stack is **complete and open source**. A commercial version is next — powers up the suit further.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install 0xray@latest
|
|
17
|
+
```
|
|
4
18
|
|
|
5
19
|
xray is the pure v2 three-subsystem AI orchestration framework — **MCP-centric**, governed by Dynamo, and autonomous via thinDispatch. Consumer `npm install 0xray` auto-wires all four platform bridges and seven MCP servers with zero manual config.
|
|
6
20
|
|
package/SKILLS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0xray Skills
|
|
2
2
|
|
|
3
|
-
**v3.4.
|
|
3
|
+
**v3.4.9** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
|
|
4
4
|
|
|
5
5
|
xray ships 45 knowledge skills (`src/skills/<name>/SKILL.md`) across the full software lifecycle. Lead-dev operating model lives in **`orchestrator`** skill + `multi_agent_orchestration.lead_dev_mode` in features.json. Skills load on demand via the **xray-skills** MCP server (`npx -y 0xray mcp skills`), agent `@mentions`, or platform skill directories after postinstall.
|
|
6
6
|
|
package/dist/AGENTS.md
CHANGED
package/dist/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Conventional Commits](https://www.conventionalcommits.org/).
|
|
6
6
|
|
|
7
|
+
## [3.4.9] - 2026-06-19
|
|
8
|
+
|
|
9
|
+
### ⚠️ Breaking Changes
|
|
10
|
+
- **`orchestrate-task`** — returns `success: false` when implementation agents (backend-engineer, frontend-engineer, bug-triage, etc.) are **deferred**. Only MCP consult skills execute in-process. Callers must not treat a completed response as work done; delegate via host `Task` / `spawn_subagent` per lead-dev-plan.
|
|
11
|
+
|
|
12
|
+
### 🐛 Bug Fixes
|
|
13
|
+
- **Orchestrator honesty** — no fake-complete for implementation agents; `delegate-deferred` + `lead-dev-plan-persisted` logged to `activity.log`
|
|
14
|
+
- **`analyze-complexity` NaN** — `dependencyCount` + `dependencies[]` schema; numeric dependency hints no longer produce NaN scores
|
|
15
|
+
- **Lead-dev plan** — `buildLeadDevPlan` uses per-task inputs; multi-task intake forces phased plan (not collapsed "Direct execution")
|
|
16
|
+
- **Hermes governance voters** — route through `hermes -z` CLI (same path as groover `hermes-runner`); honor `HERMES_BIN`, `HERMES_HOME`
|
|
17
|
+
|
|
18
|
+
### ✅ Tests
|
|
19
|
+
- Unit coverage: `execution-planner-dependencies`, deferred task-handler, per-task lead-dev todos
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [3.4.8] - 2026-06-19
|
|
24
|
+
|
|
25
|
+
### 🐛 Bug Fixes
|
|
26
|
+
- **Hermes v2 OAuth** — governance LLM voters read `providers.xai-oauth.tokens` from `~/.hermes/auth.json` (npm consumers on modern Hermes no longer abstain)
|
|
27
|
+
- **Headless deliberation** — honor `XRAY_GOVERNANCE_IN_PROCESS=1` for in-process skill voters without MCP subprocesses
|
|
28
|
+
- **Governance MCP HTTP** — per-session Streamable HTTP transports on Railway
|
|
29
|
+
|
|
30
|
+
### ✅ Tests
|
|
31
|
+
- Unit coverage for Hermes v2 auth parsing and `XRAY_GOVERNANCE_IN_PROCESS` governance path
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
7
35
|
## [3.4.7] - 2026-06-18
|
|
8
36
|
|
|
9
37
|
### 🐛 Bug Fixes
|
package/dist/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# xray — MCP-Centric AI Governance OS
|
|
2
2
|
|
|
3
|
-
**v3.4.
|
|
3
|
+
**v3.4.9** — 42 agents · 45 skills · 7 MCP servers · 68 codex terms · 3,226 tests
|
|
4
|
+
|
|
5
|
+
## Your AI Power Suit
|
|
6
|
+
|
|
7
|
+
**Your AI Power ⚡️ Suit 🦾** — an exoskeleton that blocks AI slop from becoming real damage.
|
|
8
|
+
|
|
9
|
+
- learns with **Repertoire** 🧠
|
|
10
|
+
- has a **Solar Ban Hammer** ☀️🔨 (Dynamo governance)
|
|
11
|
+
- can also **Confer** 🗣️ (3-agent internal deliberation)
|
|
12
|
+
|
|
13
|
+
The core AI Governance OS stack is **complete and open source**. A commercial version is next — powers up the suit further.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install 0xray@latest
|
|
17
|
+
```
|
|
4
18
|
|
|
5
19
|
xray is the pure v2 three-subsystem AI orchestration framework — **MCP-centric**, governed by Dynamo, and autonomous via thinDispatch. Consumer `npm install 0xray` auto-wires all four platform bridges and seven MCP servers with zero manual config.
|
|
6
20
|
|
package/dist/SKILLS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0xray Skills
|
|
2
2
|
|
|
3
|
-
**v3.4.
|
|
3
|
+
**v3.4.9** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
|
|
4
4
|
|
|
5
5
|
xray ships 45 knowledge skills (`src/skills/<name>/SKILL.md`) across the full software lifecycle. Lead-dev operating model lives in **`orchestrator`** skill + `multi_agent_orchestration.lead_dev_mode` in features.json. Skills load on demand via the **xray-skills** MCP server (`npx -y 0xray mcp skills`), agent `@mentions`, or platform skill directories after postinstall.
|
|
6
6
|
|
|
@@ -128,7 +128,9 @@ export class GovernanceService {
|
|
|
128
128
|
}
|
|
129
129
|
async callSkillServer(serverName, proposals, context) {
|
|
130
130
|
const votes = [];
|
|
131
|
-
const useInProcess = process.env.VERCEL === '1' ||
|
|
131
|
+
const useInProcess = process.env.VERCEL === '1' ||
|
|
132
|
+
!!process.env.RAILWAY_ENVIRONMENT ||
|
|
133
|
+
process.env.XRAY_GOVERNANCE_IN_PROCESS === '1';
|
|
132
134
|
for (const proposal of proposals) {
|
|
133
135
|
try {
|
|
134
136
|
let text = '';
|
|
@@ -4,6 +4,8 @@ export interface GovernanceVote {
|
|
|
4
4
|
confidence: number;
|
|
5
5
|
reasoning: string;
|
|
6
6
|
}
|
|
7
|
+
export declare function resolveHermesBin(): string;
|
|
8
|
+
export declare function hermesCliAvailable(): boolean;
|
|
7
9
|
export declare function tryLLMGovernance(role: GovernanceRole, proposalTitle: string, proposalDescription: string, evidence: string[], proposalType: string): Promise<GovernanceVote | null>;
|
|
8
10
|
export declare function checkHermesOAuthStatus(): {
|
|
9
11
|
configured: boolean;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
4
2
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
5
|
-
const
|
|
6
|
-
const
|
|
3
|
+
const DEFAULT_HERMES_PROVIDER = "xai-oauth";
|
|
4
|
+
const DEFAULT_HERMES_MODEL = "grok-4.3";
|
|
5
|
+
const DEFAULT_HERMES_TIMEOUT_MS = 120_000;
|
|
7
6
|
const ROLE_PROMPTS = {
|
|
8
7
|
"code-review": `You are a senior code reviewer on a governance committee for a software project. Analyze the following proposal from a code quality, maintainability, readability, and engineering best practices perspective.
|
|
9
8
|
|
|
@@ -42,69 +41,97 @@ DECISION: approve|reject|abstain
|
|
|
42
41
|
CONFIDENCE: <0.0-1.0>
|
|
43
42
|
REASONING: <2-4 sentence analysis of the proposal's broader project impact>`,
|
|
44
43
|
};
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
const authPath = join(homedir(), ".hermes", "auth.json");
|
|
48
|
-
if (!existsSync(authPath))
|
|
49
|
-
return null;
|
|
50
|
-
const raw = readFileSync(authPath, "utf-8");
|
|
51
|
-
const data = JSON.parse(raw);
|
|
52
|
-
let entry;
|
|
53
|
-
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
54
|
-
entry = data["xai-oauth"];
|
|
55
|
-
if (!entry?.access_token && data.access_token) {
|
|
56
|
-
entry = data;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (!entry?.access_token && Array.isArray(data)) {
|
|
60
|
-
entry = data.find((e) => e.provider === "xai-oauth" || !!e.access_token);
|
|
61
|
-
}
|
|
62
|
-
if (!entry?.access_token) {
|
|
63
|
-
process.stderr.write("[llm] readHermesOAuthToken: no access_token in auth file\n");
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
if (entry.expires_at && Date.now() / 1000 > entry.expires_at) {
|
|
67
|
-
frameworkLogger.log("llm-governance", "hermes-token-expired", "warning", { expiresAt: entry.expires_at });
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
return entry.access_token;
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
frameworkLogger.log("llm-governance", "hermes-auth-read-error", "warning", { error: String(err) });
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
44
|
+
function resolveHermesProvider() {
|
|
45
|
+
return process.env.HERMES_PROVIDER?.trim() || DEFAULT_HERMES_PROVIDER;
|
|
76
46
|
}
|
|
77
|
-
function
|
|
78
|
-
|
|
47
|
+
function resolveHermesModel() {
|
|
48
|
+
return process.env.HERMES_MODEL?.trim() || DEFAULT_HERMES_MODEL;
|
|
49
|
+
}
|
|
50
|
+
function resolveHermesTimeoutMs() {
|
|
51
|
+
const raw = process.env.HERMES_TIMEOUT_MS?.trim();
|
|
52
|
+
if (!raw)
|
|
53
|
+
return DEFAULT_HERMES_TIMEOUT_MS;
|
|
54
|
+
const parsed = Number(raw);
|
|
55
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_HERMES_TIMEOUT_MS;
|
|
56
|
+
}
|
|
57
|
+
export function resolveHermesBin() {
|
|
58
|
+
const explicit = process.env.HERMES_BIN?.trim();
|
|
59
|
+
if (explicit)
|
|
60
|
+
return explicit;
|
|
61
|
+
return "hermes";
|
|
62
|
+
}
|
|
63
|
+
function getDirectLlmConfig() {
|
|
64
|
+
const endpoint = process.env.XRAY_LLM_ENDPOINT ||
|
|
79
65
|
process.env.XRAY_GOVERNANCE_LLM_ENDPOINT ||
|
|
80
66
|
"";
|
|
81
|
-
const
|
|
67
|
+
const apiKey = process.env.XRAY_LLM_API_KEY ||
|
|
82
68
|
process.env.XRAY_GOVERNANCE_LLM_API_KEY ||
|
|
83
69
|
"";
|
|
84
|
-
const
|
|
70
|
+
const model = process.env.XRAY_LLM_MODEL ||
|
|
85
71
|
process.env.XRAY_GOVERNANCE_LLM_MODEL ||
|
|
86
72
|
"gpt-4o";
|
|
87
|
-
// First try: direct env-var config (highest priority)
|
|
88
73
|
const directEnabled = process.env.XRAY_GOVERNANCE_LLM_ENABLED === "true" ||
|
|
89
74
|
process.env.XRAY_LLM_ENABLED === "true";
|
|
90
|
-
if (directEnabled &&
|
|
91
|
-
return {
|
|
75
|
+
if (directEnabled && endpoint && apiKey) {
|
|
76
|
+
return { mode: "direct", endpoint, apiKey, model };
|
|
92
77
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (hermesToken) {
|
|
96
|
-
frameworkLogger.log("llm-governance", "using-hermes-oauth", "info", { provider: "xai-oauth", endpoint: XAI_ENDPOINT, model: XAI_MODEL });
|
|
97
|
-
return { endpoint: XAI_ENDPOINT, apiKey: hermesToken, model: XAI_MODEL };
|
|
78
|
+
if (endpoint && apiKey) {
|
|
79
|
+
return { mode: "direct", endpoint, apiKey, model };
|
|
98
80
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function getConfig() {
|
|
84
|
+
const direct = getDirectLlmConfig();
|
|
85
|
+
if (direct)
|
|
86
|
+
return direct;
|
|
87
|
+
if (hermesCliAvailable()) {
|
|
88
|
+
return {
|
|
89
|
+
mode: "hermes",
|
|
90
|
+
provider: resolveHermesProvider(),
|
|
91
|
+
model: resolveHermesModel(),
|
|
92
|
+
timeoutMs: resolveHermesTimeoutMs(),
|
|
93
|
+
};
|
|
105
94
|
}
|
|
106
95
|
return null;
|
|
107
96
|
}
|
|
97
|
+
export function hermesCliAvailable() {
|
|
98
|
+
try {
|
|
99
|
+
execFileSync(resolveHermesBin(), ["--version"], {
|
|
100
|
+
encoding: "utf8",
|
|
101
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
102
|
+
timeout: 5_000,
|
|
103
|
+
});
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function buildGovernancePrompt(role, proposalTitle, proposalDescription, evidence, proposalType) {
|
|
111
|
+
const evidenceBlock = evidence.length > 0
|
|
112
|
+
? `**Supporting Evidence:**\n${evidence.map((entry, index) => `${index + 1}. ${entry}`).join("\n")}\n\n`
|
|
113
|
+
: "";
|
|
114
|
+
return `${ROLE_PROMPTS[role]}
|
|
115
|
+
|
|
116
|
+
## Proposal
|
|
117
|
+
|
|
118
|
+
**Title:** ${proposalTitle}
|
|
119
|
+
|
|
120
|
+
**Description:** ${proposalDescription}
|
|
121
|
+
|
|
122
|
+
**Type:** ${proposalType}
|
|
123
|
+
|
|
124
|
+
${evidenceBlock}---
|
|
125
|
+
|
|
126
|
+
Please provide your structured vote.`;
|
|
127
|
+
}
|
|
128
|
+
function runHermesGovernanceInference(prompt, config) {
|
|
129
|
+
return execFileSync(resolveHermesBin(), ["-z", prompt, "--provider", config.provider, "--model", config.model], {
|
|
130
|
+
encoding: "utf8",
|
|
131
|
+
timeout: config.timeoutMs,
|
|
132
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
133
|
+
}).trim();
|
|
134
|
+
}
|
|
108
135
|
function parseVote(text) {
|
|
109
136
|
const decisionMatch = text.match(/DECISION:\s*(approve|reject|abstain)/i);
|
|
110
137
|
const confidenceMatch = text.match(/CONFIDENCE:\s*([0-9.]+)/i);
|
|
@@ -119,13 +146,7 @@ function parseVote(text) {
|
|
|
119
146
|
reasoning: reasoningMatch?.[1]?.trim() || "No reasoning provided",
|
|
120
147
|
};
|
|
121
148
|
}
|
|
122
|
-
|
|
123
|
-
const config = getConfig();
|
|
124
|
-
if (!config) {
|
|
125
|
-
process.stderr.write("[llm] no config for role=" + role + "\n");
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
process.stderr.write("[llm] using config endpoint=" + config.endpoint + " model=" + config.model + "\n");
|
|
149
|
+
async function callDirectLlm(role, config, proposalTitle, proposalDescription, evidence, proposalType) {
|
|
129
150
|
const systemPrompt = ROLE_PROMPTS[role];
|
|
130
151
|
const userMessage = `## Proposal
|
|
131
152
|
|
|
@@ -135,40 +156,61 @@ export async function tryLLMGovernance(role, proposalTitle, proposalDescription,
|
|
|
135
156
|
|
|
136
157
|
**Type:** ${proposalType}
|
|
137
158
|
|
|
138
|
-
${evidence.length > 0 ? `**Supporting Evidence:**\n${evidence.map((
|
|
159
|
+
${evidence.length > 0 ? `**Supporting Evidence:**\n${evidence.map((entry, index) => `${index + 1}. ${entry}`).join("\n")}` : ""}
|
|
139
160
|
|
|
140
161
|
---
|
|
141
162
|
|
|
142
163
|
Please provide your structured vote.`;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
const response = await fetch(config.endpoint, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: {
|
|
167
|
+
"Content-Type": "application/json",
|
|
168
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
169
|
+
},
|
|
170
|
+
body: JSON.stringify({
|
|
171
|
+
model: config.model,
|
|
172
|
+
messages: [
|
|
173
|
+
{ role: "system", content: systemPrompt },
|
|
174
|
+
{ role: "user", content: userMessage },
|
|
175
|
+
],
|
|
176
|
+
temperature: 0.3,
|
|
177
|
+
max_tokens: 500,
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
frameworkLogger.log("llm-governance", "api-call", "error", {
|
|
182
|
+
status: response.status,
|
|
183
|
+
statusText: response.statusText,
|
|
184
|
+
role,
|
|
159
185
|
});
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const data = (await response.json());
|
|
189
|
+
const choices = data?.choices;
|
|
190
|
+
const message = choices?.[0]?.message;
|
|
191
|
+
return message?.content ?? null;
|
|
192
|
+
}
|
|
193
|
+
export async function tryLLMGovernance(role, proposalTitle, proposalDescription, evidence, proposalType) {
|
|
194
|
+
const config = getConfig();
|
|
195
|
+
if (!config) {
|
|
196
|
+
process.stderr.write(`[llm] no config for role=${role}\n`);
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
if (config.mode === "hermes") {
|
|
200
|
+
process.stderr.write(`[llm] using hermes -z provider=${config.provider} model=${config.model}\n`);
|
|
201
|
+
frameworkLogger.log("llm-governance", "using-hermes-cli", "info", {
|
|
202
|
+
provider: config.provider,
|
|
203
|
+
model: config.model,
|
|
204
|
+
role,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
process.stderr.write(`[llm] using direct endpoint=${config.endpoint} model=${config.model}\n`);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const content = config.mode === "hermes"
|
|
212
|
+
? runHermesGovernanceInference(buildGovernancePrompt(role, proposalTitle, proposalDescription, evidence, proposalType), config)
|
|
213
|
+
: await callDirectLlm(role, config, proposalTitle, proposalDescription, evidence, proposalType);
|
|
172
214
|
if (!content) {
|
|
173
215
|
frameworkLogger.log("llm-governance", "parse", "warning", {
|
|
174
216
|
issue: "missing content in response",
|
|
@@ -201,23 +243,23 @@ Please provide your structured vote.`;
|
|
|
201
243
|
}
|
|
202
244
|
}
|
|
203
245
|
export function checkHermesOAuthStatus() {
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
246
|
+
const direct = getDirectLlmConfig();
|
|
247
|
+
if (direct) {
|
|
248
|
+
return {
|
|
249
|
+
configured: true,
|
|
250
|
+
endpoint: direct.endpoint,
|
|
251
|
+
model: direct.model,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (hermesCliAvailable()) {
|
|
213
255
|
return {
|
|
214
|
-
configured:
|
|
215
|
-
|
|
256
|
+
configured: true,
|
|
257
|
+
endpoint: "hermes-cli",
|
|
258
|
+
model: resolveHermesModel(),
|
|
216
259
|
};
|
|
217
260
|
}
|
|
218
261
|
return {
|
|
219
|
-
configured:
|
|
220
|
-
|
|
221
|
-
model: XAI_MODEL,
|
|
262
|
+
configured: false,
|
|
263
|
+
error: "Hermes CLI not found on PATH. Install Hermes and ensure xai-oauth is configured (same path as gateway/cron: hermes auth add xai-oauth).",
|
|
222
264
|
};
|
|
223
265
|
}
|
|
@@ -29,6 +29,7 @@ declare class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
29
29
|
connect(transport: Transport): Promise<void>;
|
|
30
30
|
/**
|
|
31
31
|
* Run as HTTP server using Streamable HTTP transport (for Grok CLI compatibility).
|
|
32
|
+
* Uses per-session transports so concurrent clients can each initialize independently.
|
|
32
33
|
*/
|
|
33
34
|
runHttp(port?: number): Promise<void>;
|
|
34
35
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
15
15
|
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
16
|
-
import { CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
+
import { CallToolRequestSchema, isInitializeRequest, } from "@modelcontextprotocol/sdk/types.js";
|
|
17
17
|
import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
|
|
18
18
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
19
19
|
import { featuresConfigLoader } from "../core/features-config.js";
|
|
@@ -351,6 +351,7 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
351
351
|
}
|
|
352
352
|
/**
|
|
353
353
|
* Run as HTTP server using Streamable HTTP transport (for Grok CLI compatibility).
|
|
354
|
+
* Uses per-session transports so concurrent clients can each initialize independently.
|
|
354
355
|
*/
|
|
355
356
|
async runHttp(port = parseInt(process.env.MCP_PORT ?? "3100", 10)) {
|
|
356
357
|
await this.initializeGovernance();
|
|
@@ -360,10 +361,7 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
360
361
|
host: mcpHost,
|
|
361
362
|
...(allowedHosts ? { allowedHosts } : {}),
|
|
362
363
|
});
|
|
363
|
-
const
|
|
364
|
-
sessionIdGenerator: () => randomUUID(),
|
|
365
|
-
});
|
|
366
|
-
await this.server.connect(transport);
|
|
364
|
+
const transports = {};
|
|
367
365
|
const apiKey = process.env.GOVERNANCE_API_KEY;
|
|
368
366
|
if (apiKey) {
|
|
369
367
|
frameworkLogger.log("governance-mcp", "api-key-auth-enabled", "info", {});
|
|
@@ -383,8 +381,45 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
383
381
|
}
|
|
384
382
|
next();
|
|
385
383
|
});
|
|
386
|
-
|
|
384
|
+
const mcpPostHandler = async (req, res) => {
|
|
387
385
|
try {
|
|
386
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
387
|
+
let transport;
|
|
388
|
+
if (sessionId && transports[sessionId]) {
|
|
389
|
+
transport = transports[sessionId];
|
|
390
|
+
}
|
|
391
|
+
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
392
|
+
transport = new StreamableHTTPServerTransport({
|
|
393
|
+
sessionIdGenerator: () => randomUUID(),
|
|
394
|
+
onsessioninitialized: (sid) => {
|
|
395
|
+
if (transport) {
|
|
396
|
+
transports[sid] = transport;
|
|
397
|
+
frameworkLogger.log("governance-mcp", "session-initialized", "info", {
|
|
398
|
+
sessionId: sid,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
transport.onclose = () => {
|
|
404
|
+
const sid = transport?.sessionId;
|
|
405
|
+
if (sid && transports[sid]) {
|
|
406
|
+
delete transports[sid];
|
|
407
|
+
frameworkLogger.log("governance-mcp", "session-closed", "info", { sessionId: sid });
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
const sessionServer = new GovernanceServer();
|
|
411
|
+
await sessionServer.connect(transport);
|
|
412
|
+
await transport.handleRequest(req, res, req.body);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
res.status(400).json({
|
|
417
|
+
jsonrpc: "2.0",
|
|
418
|
+
error: { code: -32000, message: "Bad Request: No valid session ID provided" },
|
|
419
|
+
id: null,
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
388
423
|
await transport.handleRequest(req, res, req.body);
|
|
389
424
|
}
|
|
390
425
|
catch (error) {
|
|
@@ -393,15 +428,51 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
|
|
|
393
428
|
res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal error" }, id: null });
|
|
394
429
|
}
|
|
395
430
|
}
|
|
431
|
+
};
|
|
432
|
+
app.post("/mcp", mcpPostHandler);
|
|
433
|
+
app.get("/mcp", async (req, res) => {
|
|
434
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
435
|
+
if (!sessionId || !transports[sessionId]) {
|
|
436
|
+
res.status(400).send("Invalid or missing session ID");
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
await transports[sessionId].handleRequest(req, res);
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
frameworkLogger.log("governance-mcp", "http-sse-error", "error", { error: String(error) });
|
|
444
|
+
if (!res.headersSent) {
|
|
445
|
+
res.status(500).send("Error processing SSE stream");
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
app.delete("/mcp", async (req, res) => {
|
|
450
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
451
|
+
if (!sessionId || !transports[sessionId]) {
|
|
452
|
+
res.status(400).send("Invalid or missing session ID");
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
await transports[sessionId].handleRequest(req, res);
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
frameworkLogger.log("governance-mcp", "http-delete-error", "error", { error: String(error) });
|
|
460
|
+
if (!res.headersSent) {
|
|
461
|
+
res.status(500).send("Error processing session termination");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
396
464
|
});
|
|
397
465
|
app.get("/health", (_req, res) => {
|
|
398
466
|
res.json({ status: "ok", server: "governance" });
|
|
399
467
|
});
|
|
468
|
+
const closeAllTransports = async () => {
|
|
469
|
+
await Promise.all(Object.values(transports).map((t) => t.close()));
|
|
470
|
+
};
|
|
400
471
|
app.listen(port, () => {
|
|
401
472
|
frameworkLogger.log("governance-mcp", "http-listening", "info", { port });
|
|
402
473
|
});
|
|
403
|
-
process.on("SIGINT", () => {
|
|
404
|
-
process.on("SIGTERM", () => {
|
|
474
|
+
process.on("SIGINT", () => { void closeAllTransports().then(() => process.exit(0)); });
|
|
475
|
+
process.on("SIGTERM", () => { void closeAllTransports().then(() => process.exit(0)); });
|
|
405
476
|
}
|
|
406
477
|
}
|
|
407
478
|
// Start the server if this file is run directly
|
|
@@ -321,7 +321,7 @@ class XrayCodeReviewServer extends XrayKnowledgeSkillBase {
|
|
|
321
321
|
content: [
|
|
322
322
|
{
|
|
323
323
|
type: "text",
|
|
324
|
-
text: "DECISION: abstain\nCONFIDENCE: 0.50\nREASONING: No LLM governance provider configured.
|
|
324
|
+
text: "DECISION: abstain\nCONFIDENCE: 0.50\nREASONING: No LLM governance provider configured. Install Hermes on PATH (hermes -z) with xai-oauth, or set XRAY_GOVERNANCE_LLM_ENABLED=true + XRAY_LLM_ENDPOINT.",
|
|
325
325
|
},
|
|
326
326
|
],
|
|
327
327
|
};
|
|
@@ -666,7 +666,7 @@ class XraySecurityAuditServer extends XrayKnowledgeSkillBase {
|
|
|
666
666
|
content: [
|
|
667
667
|
{
|
|
668
668
|
type: "text",
|
|
669
|
-
text: "DECISION: abstain\nCONFIDENCE: 0.50\nREASONING: No LLM governance provider configured.
|
|
669
|
+
text: "DECISION: abstain\nCONFIDENCE: 0.50\nREASONING: No LLM governance provider configured. Install Hermes on PATH (hermes -z) with xai-oauth, or set XRAY_GOVERNANCE_LLM_ENABLED=true + XRAY_LLM_ENDPOINT.",
|
|
670
670
|
},
|
|
671
671
|
],
|
|
672
672
|
};
|
|
@@ -34,8 +34,9 @@ export function toMemoryTask(task) {
|
|
|
34
34
|
};
|
|
35
35
|
if (task.priority)
|
|
36
36
|
result.priority = task.priority;
|
|
37
|
-
if (task.dependencies)
|
|
37
|
+
if (Array.isArray(task.dependencies) && task.dependencies.length > 0) {
|
|
38
38
|
result.dependencies = task.dependencies;
|
|
39
|
+
}
|
|
39
40
|
if (task.estimatedComplexity !== undefined)
|
|
40
41
|
result.estimatedComplexity = task.estimatedComplexity;
|
|
41
42
|
if (task.metadata)
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Creates optimized execution plans for orchestration tasks
|
|
5
5
|
*/
|
|
6
6
|
import type { OrchestrationTask, ExecutionPlan, ComplexityAnalysis, TaskValidation } from '../types.js';
|
|
7
|
+
/** Dependency count from task-ID list or numeric hint from analyze-complexity */
|
|
8
|
+
export declare function dependencyCount(deps: OrchestrationTask['dependencies'] | undefined): number;
|
|
7
9
|
/**
|
|
8
10
|
* Execution Planner
|
|
9
11
|
* Plans and optimizes task execution strategies
|