0xray 3.5.5 → 4.0.1
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/.mcp.json +18 -14
- package/.opencode/skills/api-design/SKILL.md +1 -1
- package/.opencode/skills/architect-tools/SKILL.md +1 -1
- package/.opencode/skills/architecture-patterns/SKILL.md +1 -1
- package/.opencode/skills/auto-format/SKILL.md +1 -1
- package/.opencode/skills/boot-orchestrator/SKILL.md +2 -6
- package/.opencode/skills/bug-triage/SKILL.md +1 -1
- package/.opencode/skills/code-analyzer/SKILL.md +1 -1
- package/.opencode/skills/code-review/SKILL.md +1 -1
- package/.opencode/skills/enforcer/SKILL.md +1 -1
- package/.opencode/skills/framework-compliance-audit/SKILL.md +1 -1
- package/.opencode/skills/git-workflow/SKILL.md +1 -1
- package/.opencode/skills/hermes-agent/SKILL.md +36 -36
- package/.opencode/skills/inference-improve/SKILL.md +1 -1
- package/.opencode/skills/lint/SKILL.md +1 -1
- package/.opencode/skills/log-monitor/SKILL.md +1 -1
- package/.opencode/skills/model-health-check/SKILL.md +1 -1
- package/.opencode/skills/multimodal-looker/SKILL.md +1 -1
- package/.opencode/skills/orchestrator/SKILL.md +38 -14
- package/.opencode/skills/performance-analysis/SKILL.md +1 -1
- package/.opencode/skills/performance-optimization/SKILL.md +1 -1
- package/.opencode/skills/processor-pipeline/SKILL.md +1 -1
- package/.opencode/skills/project-analysis/SKILL.md +1 -1
- package/.opencode/skills/refactoring-strategies/SKILL.md +1 -1
- package/.opencode/skills/registry.json +65 -0
- package/.opencode/skills/researcher/SKILL.md +18 -5
- package/.opencode/skills/security-audit/SKILL.md +1 -1
- package/.opencode/skills/security-scan/SKILL.md +1 -1
- package/.opencode/skills/session-management/SKILL.md +1 -1
- package/.opencode/skills/state-manager/SKILL.md +1 -1
- package/.opencode/skills/storyteller/SKILL.md +1 -1
- package/.opencode/skills/strategist/SKILL.md +12 -0
- package/.opencode/skills/testing-best-practices/SKILL.md +37 -0
- package/.opencode/skills/testing-strategy/SKILL.md +14 -3
- package/.opencode/skills/ui-ux-design/SKILL.md +1 -1
- package/.opencode/skills/xray-orchestrator/index.ts +267 -0
- package/.xray/codex.json +12 -3
- package/.xray/features.json +28 -6
- package/.xray/features.schema.json +31 -0
- package/AGENTS-consumer.md +17 -7
- package/AGENTS.md +18 -10
- package/README.md +30 -27
- package/SKILLS.md +2 -2
- package/dist/AGENTS.md +18 -10
- package/dist/CHANGELOG.md +81 -11
- package/dist/README.md +30 -27
- package/dist/SKILLS.md +2 -2
- package/dist/cli/commands/hermes-install.js +3 -0
- package/dist/cli/commands/openclaw-install.js +14 -0
- package/dist/cli/index.js +10 -1
- package/dist/core/features-config.d.ts +15 -2
- package/dist/infrastructure/schemas/cloud-schemas.d.ts +1 -1
- package/dist/integrations/grok/grok-cli.d.ts +3 -0
- package/dist/integrations/grok/grok-cli.js +92 -4
- package/dist/integrations/grok/hooks/grok-hook-utils.js +53 -7
- package/dist/integrations/grok/hooks/post-tool-use.js +68 -46
- package/dist/integrations/grok/hooks/pre-tool-use.js +14 -31
- package/dist/integrations/grok/hooks/session-start.js +86 -4
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
- package/dist/integrations/hermes-agent/bridge.mjs +26 -1
- package/dist/integrations/hooks/delegation-gate-runtime.mjs +32 -3
- package/dist/integrations/hooks/pipeline-hook-runtime.mjs +1 -1
- package/dist/integrations/hooks/station-hook-runtime.cjs +421 -0
- package/dist/integrations/hooks/station-hook-runtime.mjs +24 -0
- package/dist/integrations/hooks/station-memory-ingest.mjs +45 -0
- package/dist/integrations/hooks/station-memory-match.mjs +47 -0
- package/dist/integrations/openclaw/client.d.ts +5 -1
- package/dist/integrations/openclaw/client.js +17 -9
- package/dist/integrations/openclaw/config.js +0 -1
- package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +102 -0
- package/dist/integrations/openclaw/index.js +17 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +12 -0
- package/dist/integrations/openclaw/pre-tool-gate.d.ts +19 -0
- package/dist/integrations/openclaw/pre-tool-gate.js +39 -0
- package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
- package/dist/memory-routing/index.d.ts +1 -1
- package/dist/memory-routing/index.js +1 -1
- package/dist/memory-routing/provider-loader.d.ts +4 -0
- package/dist/memory-routing/provider-loader.js +44 -2
- package/dist/memory-routing/provider-registry.js +3 -2
- package/dist/nucleus/autonomy-kernel.d.ts +2 -2
- package/dist/nucleus/autonomy-kernel.js +17 -6
- package/dist/nucleus/confer.js +17 -3
- package/dist/nucleus/delegation-gate.d.ts +14 -8
- package/dist/nucleus/delegation-gate.js +151 -16
- package/dist/nucleus/suit-temperament.d.ts +40 -0
- package/dist/nucleus/suit-temperament.js +157 -0
- package/dist/nucleus/user-aside.js +1 -1
- package/dist/plugin/xray-codex-injection.js +14 -3
- package/dist/postprocessor/triggers/GitHookTrigger.js +11 -5
- package/opencode.json +80 -0
- package/package.json +15 -5
- package/scripts/hooks/install-hooks.cjs +18 -3
- package/scripts/hooks/run-hook.js +67 -3
- package/scripts/mjs/run-repertoire-mcp.mjs +39 -0
- package/scripts/mjs/verify-confer-core.mjs +1 -1
- package/scripts/mjs/verify-delegation-gate-core.mjs +29 -2
- package/scripts/mjs/verify-four-floor-compact.mjs +524 -0
- package/scripts/mjs/verify-pipeline-facets.mjs +215 -0
- package/scripts/mjs/verify-temperament-live.mjs +261 -0
- package/scripts/node/bridge-mcp-wiring.cjs +277 -29
- package/scripts/node/consumer-install-smoke.mjs +55 -2
- package/scripts/node/install-bridges.cjs +195 -33
- package/scripts/node/postinstall.cjs +50 -27
- package/scripts/node/reconcile-version.mjs +0 -4
- package/scripts/node/release-gate.mjs +1 -1
- package/scripts/node/release.mjs +2 -2
- package/scripts/node/universal-version-manager.js +1 -1
- package/scripts/node/validate-release-docs.mjs +20 -33
- package/scripts/node/version-manager.mjs +63 -18
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
- package/src/integrations/openclaw/README.md +134 -0
- package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
- package/src/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +12 -0
- package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
- package/vendor/@0xray/repertoire/AGENTS.md +163 -0
- package/vendor/@0xray/repertoire/LICENSE +21 -0
- package/vendor/@0xray/repertoire/README.md +120 -0
- package/vendor/@0xray/repertoire/data/curated_signals.json +208 -0
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +78 -0
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/RepertoireService.js +153 -0
- package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts +39 -0
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js +60 -0
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/index.d.ts +14 -0
- package/vendor/@0xray/repertoire/dist/index.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/index.js +14 -0
- package/vendor/@0xray/repertoire/dist/index.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts +26 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js +109 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts +18 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js +94 -0
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts +7 -0
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js +18 -0
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts +31 -0
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +83 -0
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/mcp/server.d.ts +6 -0
- package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/mcp/server.js +173 -0
- package/vendor/@0xray/repertoire/dist/mcp/server.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts +27 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js +107 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts +11 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js +32 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts +9 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +72 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts +5 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js +18 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts +31 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +218 -0
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/paths.d.ts +24 -0
- package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/paths.js +74 -0
- package/vendor/@0xray/repertoire/dist/paths.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +141 -0
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +210 -0
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +48 -0
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +221 -0
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts +11 -0
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js +43 -0
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts +15 -0
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.js +35 -0
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts +3 -0
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js +24 -0
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts +24 -0
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js +148 -0
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts +4 -0
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js +44 -0
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts +22 -0
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js +66 -0
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +1 -0
- package/vendor/@0xray/repertoire/dist/types.d.ts +210 -0
- package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -0
- package/vendor/@0xray/repertoire/dist/types.js +2 -0
- package/vendor/@0xray/repertoire/dist/types.js.map +1 -0
- package/vendor/@0xray/repertoire/docs/MEMORY-ROUTING-PROVIDER.md +54 -0
- package/vendor/@0xray/repertoire/hermes-mcp.example.json +20 -0
- package/vendor/@0xray/repertoire/mcp-config.example.json +11 -0
- package/vendor/@0xray/repertoire/package.json +124 -0
- package/vendor/@0xray/repertoire/schemas/curated-signal.schema.json +68 -0
- package/vendor/@0xray/repertoire/schemas/inference-log.schema.json +82 -0
- package/vendor/@0xray/repertoire/scripts/confirm-suit-all.mjs +253 -0
- package/vendor/@0xray/repertoire/scripts/confirm-suit-harness.mjs +220 -0
- package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +215 -0
- package/vendor/@0xray/repertoire/scripts/suit-bridge-shared.mjs +139 -0
- package/vendor/@0xray/repertoire/scripts/verify-grok-suit.mjs +304 -0
- package/vendor/@0xray/repertoire/scripts/verify-hermes-suit.mjs +163 -0
- package/vendor/@0xray/repertoire/scripts/verify-openclaw-suit.mjs +125 -0
- package/vendor/@0xray/repertoire/scripts/verify-opencode-suit.mjs +127 -0
- package/vendor/@0xray/repertoire/scripts/verify-synthesis-dogfood.mjs +207 -0
- package/vendor/@0xray/repertoire/skills/repertoire-trap-handling/SKILL.md +206 -0
- package/xray/codex.json +12 -3
- package/xray/features.json +28 -6
- package/xray/features.schema.json +31 -0
|
@@ -6,6 +6,7 @@ import * as fs from 'fs';
|
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { featuresConfigLoader } from '../core/features-config.js';
|
|
8
8
|
import { scoreComplexity } from './thin-dispatch.js';
|
|
9
|
+
import { conferDefaultForProfile, resolveRuntimeSuitProfile } from './suit-temperament.js';
|
|
9
10
|
export const LEAD_DEV_RULES = [
|
|
10
11
|
'Phased plan + detailed todos; assign best subagent; monitor output; iterate fully',
|
|
11
12
|
'Lead dev takes helm — loop test fix until complete; no permission pings',
|
|
@@ -75,11 +76,17 @@ function buildImplementationTodos(description, taskTypes, taskInputs, idPrefix)
|
|
|
75
76
|
}));
|
|
76
77
|
}
|
|
77
78
|
/** Mandatory-consult plan for synthesis checkpoint realignment (no implementation phase). */
|
|
78
|
-
export function buildSynthesisCheckpointPlan(dueReason) {
|
|
79
|
+
export function buildSynthesisCheckpointPlan(dueReason, projectRoot = process.cwd()) {
|
|
79
80
|
if (!isLeadDevModeActive())
|
|
80
81
|
return null;
|
|
81
82
|
const cfg = orchestrationConfig();
|
|
82
|
-
const
|
|
83
|
+
const profile = resolveRuntimeSuitProfile(projectRoot);
|
|
84
|
+
const autoConsult = profile === 'frontier'
|
|
85
|
+
? cfg.confer?.enabled === true || cfg.confer_on_synthesis === true
|
|
86
|
+
: profile === 'strict'
|
|
87
|
+
? true
|
|
88
|
+
: cfg.auto_consult_major_work !== false;
|
|
89
|
+
const mandatoryConsults = autoConsult ? [...MANDATORY_MAJOR_CONSULTS] : [];
|
|
83
90
|
if (mandatoryConsults.length === 0)
|
|
84
91
|
return null;
|
|
85
92
|
const reasonSuffix = dueReason ? ` (${dueReason})` : '';
|
|
@@ -113,10 +120,11 @@ export function buildSynthesisCheckpointPlan(dueReason) {
|
|
|
113
120
|
},
|
|
114
121
|
};
|
|
115
122
|
}
|
|
116
|
-
export function buildLeadDevPlan(description, taskTypes = ['implement'], taskInputs = [], mcpOverallComplexity) {
|
|
123
|
+
export function buildLeadDevPlan(description, taskTypes = ['implement'], taskInputs = [], mcpOverallComplexity, projectRoot = process.cwd()) {
|
|
117
124
|
if (!isLeadDevModeActive())
|
|
118
125
|
return null;
|
|
119
126
|
const cfg = orchestrationConfig();
|
|
127
|
+
const profile = resolveRuntimeSuitProfile(projectRoot);
|
|
120
128
|
const threshold = cfg.phased_plan_threshold ?? 25;
|
|
121
129
|
const score = scoreComplexity(description, { taskTypes });
|
|
122
130
|
const mcpScore = mcpOverallComplexity !== undefined && Number.isFinite(mcpOverallComplexity)
|
|
@@ -124,9 +132,12 @@ export function buildLeadDevPlan(description, taskTypes = ['implement'], taskInp
|
|
|
124
132
|
: 0;
|
|
125
133
|
const complexity = Math.max(score.score, mcpScore);
|
|
126
134
|
const requiresPhasedPlan = complexity > threshold || taskInputs.length > 1;
|
|
127
|
-
const
|
|
128
|
-
?
|
|
129
|
-
:
|
|
135
|
+
const autoConsult = profile === 'frontier'
|
|
136
|
+
? cfg.confer?.enabled === true || cfg.confer_on_synthesis === true
|
|
137
|
+
: profile === 'strict' || conferDefaultForProfile(profile)
|
|
138
|
+
? cfg.auto_consult_major_work !== false
|
|
139
|
+
: false;
|
|
140
|
+
const mandatoryConsults = autoConsult && requiresPhasedPlan ? [...MANDATORY_MAJOR_CONSULTS] : [];
|
|
130
141
|
const phases = requiresPhasedPlan
|
|
131
142
|
? [
|
|
132
143
|
{
|
package/dist/nucleus/confer.js
CHANGED
|
@@ -8,6 +8,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
9
|
import { buildReceiptFromConsultOutput, tryRecordSynthesisConsultReceipt, parseConsultVerdictFromText, } from './synthesis-consult-receipt.js';
|
|
10
10
|
import { isSynthesisCheckpointDue } from './synthesis.js';
|
|
11
|
+
import { conferDefaultForProfile, resolveRuntimeSuitProfile } from './suit-temperament.js';
|
|
11
12
|
export const CONFER_AGENTS = [...MANDATORY_MAJOR_CONSULTS];
|
|
12
13
|
const STATE_VERSION = 1;
|
|
13
14
|
export function conferCheckpointPath(projectRoot = process.cwd()) {
|
|
@@ -18,19 +19,32 @@ export function defaultConferConfig() {
|
|
|
18
19
|
}
|
|
19
20
|
export function loadConferConfig(projectRoot = process.cwd()) {
|
|
20
21
|
const featuresPath = path.join(projectRoot, '.xray', 'features.json');
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const profile = resolveRuntimeSuitProfile(projectRoot);
|
|
23
|
+
if (profile === 'strict') {
|
|
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
|
+
}
|
|
23
31
|
try {
|
|
24
32
|
const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
|
|
25
33
|
const orch = data.multi_agent_orchestration ?? {};
|
|
26
34
|
const raw = orch.confer ?? {};
|
|
35
|
+
if (profile === 'frontier') {
|
|
36
|
+
const optedIn = raw.enabled === true || orch.confer_on_synthesis === true;
|
|
37
|
+
return { enabled: optedIn, on_synthesis: optedIn };
|
|
38
|
+
}
|
|
27
39
|
return {
|
|
28
40
|
enabled: raw.enabled !== false && orch.confer_on_synthesis !== false,
|
|
29
41
|
on_synthesis: raw.on_synthesis !== false && orch.confer_on_synthesis !== false,
|
|
30
42
|
};
|
|
31
43
|
}
|
|
32
44
|
catch {
|
|
33
|
-
return
|
|
45
|
+
return conferDefaultForProfile(profile)
|
|
46
|
+
? defaultConferConfig()
|
|
47
|
+
: { enabled: false, on_synthesis: false };
|
|
34
48
|
}
|
|
35
49
|
}
|
|
36
50
|
export function isConferEnabled(projectRoot = process.cwd()) {
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-host delegation gate SSOT — pending-delegations + spawn todo enforcement.
|
|
3
3
|
* Grok / Hermes / OpenCode adapters call evaluatePreToolGate via delegation-gate-runtime.
|
|
4
|
-
*
|
|
4
|
+
* Constitution (11/29/69 + destructive shell) lives here. Grok may still extra-block Codex 2/7.
|
|
5
5
|
*/
|
|
6
6
|
import { type PendingDelegation } from './pending-delegations.js';
|
|
7
7
|
export { getActivePendingDelegations } from './pending-delegations.js';
|
|
8
8
|
import { updatePlanTodoStatus, type SpawnToolInput } from './lead-dev-plan-persistence.js';
|
|
9
|
+
import { writeSuitSessionBoot, maybeHeatHostStation, type CeremonyLevel, type SpawnPlanMode, type SuitHost, type SuitProfile } from './suit-temperament.js';
|
|
10
|
+
export { writeSuitSessionBoot, maybeHeatHostStation };
|
|
9
11
|
export { extractSpawnCwd, validateAsideWorktreeCwd, provisionGitWorktree } from './aside-worktree.js';
|
|
10
|
-
export { validateSpawnMatchesTodo, updatePlanTodoStatus, savePersistedLeadDevPlan, } from './lead-dev-plan-persistence.js';
|
|
12
|
+
export { validateSpawnMatchesTodo, updatePlanTodoStatus, savePersistedLeadDevPlan, archiveStaleLeadDevPlan, findRecentStalePlanArchive, } from './lead-dev-plan-persistence.js';
|
|
11
13
|
export { buildReceiptFromConsultOutput, hasValidSynthesisConsultReceipt, isSynthesisConsultTodoId, loadSynthesisConsultReceipt, parseConsultVerdictFromText, tryRecordSynthesisConsultReceipt, writeSynthesisConsultReceipt, type SynthesisConsultReceipt, type SynthesisConsultVerdict, } from './synthesis-consult-receipt.js';
|
|
12
14
|
export declare const updatePlanTodoStatusInPlace: typeof updatePlanTodoStatus;
|
|
13
|
-
export type DelegationGateHost =
|
|
15
|
+
export type DelegationGateHost = SuitHost;
|
|
14
16
|
export interface DelegationGateFeatures {
|
|
15
17
|
lead_dev_mode: boolean;
|
|
16
18
|
auto_chain_delegations: boolean;
|
|
19
|
+
/** v3 — deny (guided/strict), warn (frontier), off */
|
|
20
|
+
spawn_plan_mode?: SpawnPlanMode;
|
|
21
|
+
ceremony?: CeremonyLevel;
|
|
22
|
+
suit_profile?: SuitProfile;
|
|
23
|
+
no_new_surface?: boolean;
|
|
17
24
|
}
|
|
18
25
|
export interface ToolGateInput {
|
|
19
26
|
prompt?: string;
|
|
@@ -42,6 +49,7 @@ export interface PreToolGateContext {
|
|
|
42
49
|
sessionId: string | null;
|
|
43
50
|
features: DelegationGateFeatures;
|
|
44
51
|
host?: DelegationGateHost;
|
|
52
|
+
hookEvent?: string;
|
|
45
53
|
}
|
|
46
54
|
export interface PreToolGateDeny {
|
|
47
55
|
allow: false;
|
|
@@ -69,7 +77,7 @@ export interface PostToolSpawnOptions {
|
|
|
69
77
|
toolOutput?: unknown;
|
|
70
78
|
sessionId?: string | null;
|
|
71
79
|
}
|
|
72
|
-
export declare function loadDelegationGateFeatures(projectRoot?: string): DelegationGateFeatures;
|
|
80
|
+
export declare function loadDelegationGateFeatures(projectRoot?: string, host?: DelegationGateHost): DelegationGateFeatures;
|
|
73
81
|
export declare function normalizeHostToolInput(toolInput: ToolGateInput): SpawnToolInput & {
|
|
74
82
|
command?: string;
|
|
75
83
|
};
|
|
@@ -83,10 +91,8 @@ export declare function evaluateSpawnPlanGate(toolName: string, toolInput: ToolG
|
|
|
83
91
|
export declare function evaluateSynthesisGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
84
92
|
/** Block writes / unrelated work while pending delegations exist. */
|
|
85
93
|
export declare function evaluatePendingDelegationGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
* pending gate, then spawn todo gate for subagent tools.
|
|
89
|
-
*/
|
|
94
|
+
/** Codex 11 / 29 / 69 + destructive shell — always on, all hosts that call this SSOT. */
|
|
95
|
+
export declare function evaluateConstitutionGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
90
96
|
export declare function evaluatePreToolGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
91
97
|
export declare function evaluatePostToolSpawn(toolName: string, toolInput: ToolGateInput, projectRoot: string, options?: PostToolSpawnOptions): PostToolSpawnResult;
|
|
92
98
|
export declare function isOrchestrateToolEvent(toolName: string, toolInput?: ToolGateInput): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-host delegation gate SSOT — pending-delegations + spawn todo enforcement.
|
|
3
3
|
* Grok / Hermes / OpenCode adapters call evaluatePreToolGate via delegation-gate-runtime.
|
|
4
|
-
*
|
|
4
|
+
* Constitution (11/29/69 + destructive shell) lives here. Grok may still extra-block Codex 2/7.
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
@@ -13,6 +13,8 @@ import { isSynthesisConsultTodoId, tryRecordSynthesisConsultReceipt, } from './s
|
|
|
13
13
|
import { resolveSpawnPlan, hasValidSpawnPlanContext } from './spawn-plan-resolution.js';
|
|
14
14
|
import { isUserAsidesEnabled, isUserAsideTodoId, loadActiveAsidePointer, loadActiveUserAside, } from './user-aside.js';
|
|
15
15
|
import { validateAsideWorktreeCwd, } from './aside-worktree.js';
|
|
16
|
+
import { ceremonyForProfile, resolveSuitProfile, spawnPlanModeForProfile, writeSuitSessionBoot, maybeHeatHostStation, } from './suit-temperament.js';
|
|
17
|
+
export { writeSuitSessionBoot, maybeHeatHostStation };
|
|
16
18
|
export { extractSpawnCwd, validateAsideWorktreeCwd, provisionGitWorktree } from './aside-worktree.js';
|
|
17
19
|
function denyAsideWorktreeCwd(cwdCheck) {
|
|
18
20
|
const base = {
|
|
@@ -30,7 +32,7 @@ function asideWorktreeCwdDenyIfNeeded(activeAside, toolInput, projectRoot) {
|
|
|
30
32
|
return denyAsideWorktreeCwd(cwdCheck);
|
|
31
33
|
return undefined;
|
|
32
34
|
}
|
|
33
|
-
export { validateSpawnMatchesTodo, updatePlanTodoStatus, savePersistedLeadDevPlan, } from './lead-dev-plan-persistence.js';
|
|
35
|
+
export { validateSpawnMatchesTodo, updatePlanTodoStatus, savePersistedLeadDevPlan, archiveStaleLeadDevPlan, findRecentStalePlanArchive, } from './lead-dev-plan-persistence.js';
|
|
34
36
|
export { buildReceiptFromConsultOutput, hasValidSynthesisConsultReceipt, isSynthesisConsultTodoId, loadSynthesisConsultReceipt, parseConsultVerdictFromText, tryRecordSynthesisConsultReceipt, writeSynthesisConsultReceipt, } from './synthesis-consult-receipt.js';
|
|
35
37
|
export const updatePlanTodoStatusInPlace = updatePlanTodoStatus;
|
|
36
38
|
const READ_TOOLS = new Set([
|
|
@@ -69,21 +71,34 @@ const SYNTHESIS_ALLOWED_CONSULT_TOOLS = new Set([
|
|
|
69
71
|
]);
|
|
70
72
|
const HERMES_WRITE_TOOLS = new Set(['write_file', 'patch', 'write', 'edit']);
|
|
71
73
|
const OPENCODE_WRITE_TOOLS = new Set(['write', 'edit', 'multiedit']);
|
|
72
|
-
export function loadDelegationGateFeatures(projectRoot = process.cwd()) {
|
|
74
|
+
export function loadDelegationGateFeatures(projectRoot = process.cwd(), host = 'generic') {
|
|
75
|
+
const fallback = (profile) => ({
|
|
76
|
+
lead_dev_mode: true,
|
|
77
|
+
auto_chain_delegations: true,
|
|
78
|
+
spawn_plan_mode: spawnPlanModeForProfile(profile),
|
|
79
|
+
ceremony: ceremonyForProfile(profile),
|
|
80
|
+
suit_profile: profile,
|
|
81
|
+
no_new_surface: true,
|
|
82
|
+
});
|
|
73
83
|
const featuresPath = path.join(projectRoot, '.xray', 'features.json');
|
|
74
84
|
if (!fs.existsSync(featuresPath)) {
|
|
75
|
-
return
|
|
85
|
+
return fallback(resolveSuitProfile(undefined, host));
|
|
76
86
|
}
|
|
77
87
|
try {
|
|
78
88
|
const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
|
|
79
89
|
const orch = data.multi_agent_orchestration ?? {};
|
|
90
|
+
const profile = resolveSuitProfile(data.suit_temperament, host);
|
|
80
91
|
return {
|
|
81
92
|
lead_dev_mode: orch.enabled !== false && orch.lead_dev_mode !== false,
|
|
82
93
|
auto_chain_delegations: orch.auto_chain_delegations !== false,
|
|
94
|
+
spawn_plan_mode: spawnPlanModeForProfile(profile),
|
|
95
|
+
ceremony: ceremonyForProfile(profile),
|
|
96
|
+
suit_profile: profile,
|
|
97
|
+
no_new_surface: profile === 'strict' ? true : orch.no_new_surface !== false,
|
|
83
98
|
};
|
|
84
99
|
}
|
|
85
100
|
catch {
|
|
86
|
-
return
|
|
101
|
+
return fallback(resolveSuitProfile(undefined, host));
|
|
87
102
|
}
|
|
88
103
|
}
|
|
89
104
|
export function normalizeHostToolInput(toolInput) {
|
|
@@ -197,40 +212,53 @@ function denyFromPending(primary, pendingCount) {
|
|
|
197
212
|
delegationId: primary.id,
|
|
198
213
|
};
|
|
199
214
|
}
|
|
215
|
+
function spawnPlanSoftAllow(gate, reason) {
|
|
216
|
+
return { allow: true, reason, gate };
|
|
217
|
+
}
|
|
200
218
|
/** Spawn plan missing — applies before auto_chain opt-out for subagent tools. */
|
|
201
219
|
export function evaluateSpawnPlanGate(toolName, toolInput, ctx) {
|
|
202
220
|
if (!ctx.features.lead_dev_mode || !isSubagentTool(toolName)) {
|
|
203
221
|
return { allow: true };
|
|
204
222
|
}
|
|
223
|
+
const spawnMode = ctx.features.spawn_plan_mode ?? 'deny';
|
|
224
|
+
if (spawnMode === 'off') {
|
|
225
|
+
return { allow: true };
|
|
226
|
+
}
|
|
205
227
|
const activeAside = isUserAsidesEnabled(ctx.projectRoot)
|
|
206
228
|
? loadActiveUserAside(ctx.projectRoot, ctx.sessionId)
|
|
207
229
|
: null;
|
|
208
230
|
const plan = loadPersistedLeadDevPlan(ctx.projectRoot);
|
|
209
231
|
if (!activeAside && plan && isLeadDevPlanStale(plan, ctx.projectRoot)) {
|
|
210
|
-
|
|
211
|
-
allow: false,
|
|
232
|
+
const stale = {
|
|
212
233
|
reason: 'Lead-dev plan is stale (unstarted todos exceeded TTL) — ' +
|
|
213
234
|
're-run xray-orchestrator analyze-complexity to refresh the plan',
|
|
214
235
|
gate: 'spawn-plan-stale',
|
|
215
236
|
hint: { tool: 'analyze-complexity', mcp: 'xray-orchestrator' },
|
|
216
237
|
};
|
|
238
|
+
if (spawnMode === 'warn')
|
|
239
|
+
return spawnPlanSoftAllow(stale.gate, stale.reason);
|
|
240
|
+
return { allow: false, ...stale };
|
|
217
241
|
}
|
|
218
242
|
if (!hasValidSpawnPlanContext(ctx.projectRoot, ctx.sessionId)) {
|
|
219
243
|
if (findRecentStalePlanArchive(ctx.projectRoot)) {
|
|
220
|
-
|
|
221
|
-
allow: false,
|
|
244
|
+
const archived = {
|
|
222
245
|
reason: 'Lead-dev plan was stale and archived — ' +
|
|
223
246
|
're-run xray-orchestrator analyze-complexity to refresh the plan',
|
|
224
247
|
gate: 'spawn-plan-stale',
|
|
225
248
|
hint: { tool: 'analyze-complexity', mcp: 'xray-orchestrator' },
|
|
226
249
|
};
|
|
250
|
+
if (spawnMode === 'warn')
|
|
251
|
+
return spawnPlanSoftAllow(archived.gate, archived.reason);
|
|
252
|
+
return { allow: false, ...archived };
|
|
227
253
|
}
|
|
228
|
-
|
|
229
|
-
allow: false,
|
|
254
|
+
const missing = {
|
|
230
255
|
reason: 'Codex 59/67: call xray-orchestrator analyze-complexity first — ' +
|
|
231
256
|
'writes .xray/state/lead-dev-plan.json required before spawn_subagent',
|
|
232
257
|
gate: 'spawn-plan-missing',
|
|
233
258
|
};
|
|
259
|
+
if (spawnMode === 'warn')
|
|
260
|
+
return spawnPlanSoftAllow(missing.gate, missing.reason);
|
|
261
|
+
return { allow: false, ...missing };
|
|
234
262
|
}
|
|
235
263
|
if (ctx.features.auto_chain_delegations === false) {
|
|
236
264
|
const cwdDeny = asideWorktreeCwdDenyIfNeeded(activeAside, toolInput, ctx.projectRoot);
|
|
@@ -246,13 +274,16 @@ export function evaluateSpawnPlanGate(toolName, toolInput, ctx) {
|
|
|
246
274
|
const expectedTodo = pending[0]?.planTodoId && resolved.plan
|
|
247
275
|
? allPlanTodos(resolved.plan).find((t) => t.id === pending[0].planTodoId) ?? null
|
|
248
276
|
: null;
|
|
277
|
+
const cwdDeny = asideWorktreeCwdDenyIfNeeded(activeAside, toolInput, ctx.projectRoot);
|
|
278
|
+
if (cwdDeny)
|
|
279
|
+
return cwdDeny;
|
|
249
280
|
const validation = validateSpawnMatchesTodo(normalized, ctx.projectRoot, expectedTodo, ctx.sessionId);
|
|
250
281
|
if (!validation.valid) {
|
|
282
|
+
if (spawnMode === 'warn') {
|
|
283
|
+
return spawnPlanSoftAllow(validation.gate ?? 'spawn-todo-persistence', validation.reason ?? 'Spawn did not match lead-dev plan todo (frontier: warn, not deny)');
|
|
284
|
+
}
|
|
251
285
|
return denyFromSpawnValidation(validation);
|
|
252
286
|
}
|
|
253
|
-
const cwdDeny = asideWorktreeCwdDenyIfNeeded(activeAside, toolInput, ctx.projectRoot);
|
|
254
|
-
if (cwdDeny)
|
|
255
|
-
return cwdDeny;
|
|
256
287
|
return { allow: true };
|
|
257
288
|
}
|
|
258
289
|
/** Block writes / spawns / shell while synthesis checkpoint is due. */
|
|
@@ -346,8 +377,110 @@ export function evaluatePendingDelegationGate(toolName, toolInput, ctx) {
|
|
|
346
377
|
* Full pre-tool evaluation — synthesis gate first, slice recording, then
|
|
347
378
|
* pending gate, then spawn todo gate for subagent tools.
|
|
348
379
|
*/
|
|
380
|
+
const CONSTITUTION_ANY = /:\s*any\b|as\s+any\b/;
|
|
381
|
+
const CONSTITUTION_TS_IGNORE = /@ts-ignore|@ts-expect-error/;
|
|
382
|
+
const CONSTITUTION_EVAL = /\beval\s*\(/;
|
|
383
|
+
const CONSTITUTION_DESTRUCTIVE = /\brm\s+-rf\s+\/(?:\s|$)|\bmkfs\b|\bdd\s+if=|:()\s*\{\s*:\|&\s*\}\s*;:/i;
|
|
384
|
+
const SURFACE_DENY = [
|
|
385
|
+
/(?:^|\/)src\/mcps\/[^/]+\.server\.(ts|js)$/i,
|
|
386
|
+
/(?:^|\/)src\/skills\/[^/]+\/SKILL\.md$/i,
|
|
387
|
+
/(?:^|\/)src\/mcps\/orchestrator\/handlers\/[^/]+-handler\.(ts|js)$/i,
|
|
388
|
+
/(?:^|\/)src\/nucleus\/autonomy-kernel\.(ts|js)$/i,
|
|
389
|
+
/(?:^|\/)src\/mcps\/[^/]+\/handlers\/autonomy-handler\.(ts|js)$/i,
|
|
390
|
+
];
|
|
391
|
+
function collectWriteContent(toolInput) {
|
|
392
|
+
return [
|
|
393
|
+
toolInput.new_string,
|
|
394
|
+
toolInput.contents,
|
|
395
|
+
toolInput.content,
|
|
396
|
+
toolInput.command,
|
|
397
|
+
toolInput.prompt,
|
|
398
|
+
]
|
|
399
|
+
.filter((v) => v != null)
|
|
400
|
+
.map(String)
|
|
401
|
+
.join('\n');
|
|
402
|
+
}
|
|
403
|
+
function collectWritePaths(toolInput) {
|
|
404
|
+
const paths = [];
|
|
405
|
+
for (const key of ['path', 'file_path', 'filePath', 'target_notebook']) {
|
|
406
|
+
const v = toolInput[key];
|
|
407
|
+
if (typeof v === 'string' && v)
|
|
408
|
+
paths.push(v);
|
|
409
|
+
}
|
|
410
|
+
const extra = toolInput.paths;
|
|
411
|
+
if (Array.isArray(extra)) {
|
|
412
|
+
for (const p of extra) {
|
|
413
|
+
if (typeof p === 'string' && p)
|
|
414
|
+
paths.push(p);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return paths;
|
|
418
|
+
}
|
|
419
|
+
/** Codex 11 / 29 / 69 + destructive shell — always on, all hosts that call this SSOT. */
|
|
420
|
+
export function evaluateConstitutionGate(toolName, toolInput, ctx) {
|
|
421
|
+
const content = collectWriteContent(toolInput);
|
|
422
|
+
const paths = collectWritePaths(toolInput);
|
|
423
|
+
const writing = isWriteTool(toolName, ctx.host ?? 'generic');
|
|
424
|
+
if (writing && ctx.features.no_new_surface !== false) {
|
|
425
|
+
for (const p of paths) {
|
|
426
|
+
const normalized = p.replace(/\\/g, '/');
|
|
427
|
+
const abs = path.isAbsolute(p) ? p : path.join(ctx.projectRoot, p);
|
|
428
|
+
if (fs.existsSync(abs))
|
|
429
|
+
continue;
|
|
430
|
+
if (SURFACE_DENY.some((re) => re.test(normalized))) {
|
|
431
|
+
return {
|
|
432
|
+
allow: false,
|
|
433
|
+
reason: 'Codex 69: new MCP/skill/handler surface — rewire existing',
|
|
434
|
+
gate: 'no-new-surface',
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (writing && content) {
|
|
440
|
+
if (CONSTITUTION_ANY.test(content) || CONSTITUTION_TS_IGNORE.test(content)) {
|
|
441
|
+
return {
|
|
442
|
+
allow: false,
|
|
443
|
+
reason: 'Codex 11 Type Safety: no `any` / @ts-ignore / @ts-expect-error',
|
|
444
|
+
gate: 'codex-11',
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
if (CONSTITUTION_EVAL.test(content)) {
|
|
448
|
+
return {
|
|
449
|
+
allow: false,
|
|
450
|
+
reason: 'Codex 29 Security: eval() prohibited',
|
|
451
|
+
gate: 'codex-29',
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (isShellTool(toolName) && CONSTITUTION_DESTRUCTIVE.test(String(toolInput.command ?? ''))) {
|
|
456
|
+
return {
|
|
457
|
+
allow: false,
|
|
458
|
+
reason: 'Blocked destructive shell command',
|
|
459
|
+
gate: 'destructive-shell',
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
return { allow: true };
|
|
463
|
+
}
|
|
349
464
|
export function evaluatePreToolGate(toolName, toolInput, ctx) {
|
|
350
|
-
|
|
465
|
+
if (ctx.host === 'openclaw') {
|
|
466
|
+
try {
|
|
467
|
+
maybeHeatHostStation(ctx.projectRoot, 'openclaw', {
|
|
468
|
+
source: '0xray/openclaw-pre-tool',
|
|
469
|
+
sessionId: ctx.sessionId || 'openclaw',
|
|
470
|
+
hookEvent: ctx.hookEvent || 'session_start',
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
/* station heat is best-effort — constitution still runs */
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const constitution = evaluateConstitutionGate(toolName, toolInput, ctx);
|
|
478
|
+
if (!constitution.allow)
|
|
479
|
+
return constitution;
|
|
480
|
+
const lite = ctx.features.ceremony === 'lite';
|
|
481
|
+
const synthesisBlock = lite
|
|
482
|
+
? { allow: true }
|
|
483
|
+
: evaluateSynthesisGate(toolName, toolInput, ctx);
|
|
351
484
|
if (!synthesisBlock.allow)
|
|
352
485
|
return synthesisBlock;
|
|
353
486
|
if (!isSynthesisCheckpointDue(ctx.projectRoot, ctx.sessionId)) {
|
|
@@ -356,7 +489,9 @@ export function evaluatePreToolGate(toolName, toolInput, ctx) {
|
|
|
356
489
|
sessionId: ctx.sessionId,
|
|
357
490
|
});
|
|
358
491
|
}
|
|
359
|
-
const pendingBlock =
|
|
492
|
+
const pendingBlock = lite
|
|
493
|
+
? { allow: true }
|
|
494
|
+
: evaluatePendingDelegationGate(toolName, toolInput, ctx);
|
|
360
495
|
if (!pendingBlock.allow)
|
|
361
496
|
return pendingBlock;
|
|
362
497
|
const spawnBlock = evaluateSpawnPlanGate(toolName, toolInput, ctx);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0xRay 4.0 suit temperament — how loudly the Autonomous Engine insists.
|
|
3
|
+
*
|
|
4
|
+
* v2 three-subsystem OS stays: Inference · External Governance · Autonomous Engine.
|
|
5
|
+
* This module does not replace thinDispatch or Codex. It only scales *ceremony*
|
|
6
|
+
* (spawn-plan deny, synthesis, confer) by host strength.
|
|
7
|
+
*
|
|
8
|
+
* Frontier (Grok 4.6 / Grok Build): engine available, spawn warns if no plan.
|
|
9
|
+
* Guided (OpenCode / Hermes / OpenClaw, including free models): full v2 ceremony.
|
|
10
|
+
* Missing config → guided (existing consumers unchanged).
|
|
11
|
+
*/
|
|
12
|
+
export type SuitHost = 'grok' | 'hermes' | 'opencode' | 'openclaw' | 'generic';
|
|
13
|
+
export type SuitProfile = 'frontier' | 'guided' | 'strict';
|
|
14
|
+
export type SuitProfileOrAuto = 'auto' | SuitProfile;
|
|
15
|
+
export type CeremonyLevel = 'full' | 'lite';
|
|
16
|
+
export type SpawnPlanMode = 'deny' | 'warn' | 'off';
|
|
17
|
+
export interface SuitTemperamentConfig {
|
|
18
|
+
/** auto = pick from host_defaults */
|
|
19
|
+
profile?: SuitProfileOrAuto;
|
|
20
|
+
host_defaults?: Partial<Record<SuitHost, SuitProfile>>;
|
|
21
|
+
}
|
|
22
|
+
export declare const DEFAULT_HOST_PROFILES: Record<SuitHost, SuitProfile>;
|
|
23
|
+
export declare function loadSuitTemperamentRaw(projectRoot: string): SuitTemperamentConfig | undefined;
|
|
24
|
+
export declare function resolveSuitProfile(raw: SuitTemperamentConfig | undefined, host: SuitHost): SuitProfile;
|
|
25
|
+
/**
|
|
26
|
+
* Host-aware when known. Kernel/MCP (`generic`) uses last session-boot profile.
|
|
27
|
+
* A Hermes/OpenCode host ignores a leftover Grok boot (and vice versa).
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveRuntimeSuitProfile(projectRoot: string, host?: SuitHost): SuitProfile;
|
|
30
|
+
/**
|
|
31
|
+
* Write station heat when this host/session has no live card yet.
|
|
32
|
+
* OpenClaw has no SessionStart — PreToolUse is the session boundary.
|
|
33
|
+
* Install/init cards without a session id are not a live session.
|
|
34
|
+
*/
|
|
35
|
+
export declare function maybeHeatHostStation(projectRoot: string, host: SuitHost, extra?: Record<string, unknown>): string | null;
|
|
36
|
+
/** Merge temperament fields into session-boot.json for the host that just started. */
|
|
37
|
+
export declare function writeSuitSessionBoot(projectRoot: string, host: SuitHost, extra?: Record<string, unknown>): string;
|
|
38
|
+
export declare function ceremonyForProfile(profile: SuitProfile): CeremonyLevel;
|
|
39
|
+
export declare function spawnPlanModeForProfile(profile: SuitProfile): SpawnPlanMode;
|
|
40
|
+
export declare function conferDefaultForProfile(profile: SuitProfile): boolean;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0xRay 4.0 suit temperament — how loudly the Autonomous Engine insists.
|
|
3
|
+
*
|
|
4
|
+
* v2 three-subsystem OS stays: Inference · External Governance · Autonomous Engine.
|
|
5
|
+
* This module does not replace thinDispatch or Codex. It only scales *ceremony*
|
|
6
|
+
* (spawn-plan deny, synthesis, confer) by host strength.
|
|
7
|
+
*
|
|
8
|
+
* Frontier (Grok 4.6 / Grok Build): engine available, spawn warns if no plan.
|
|
9
|
+
* Guided (OpenCode / Hermes / OpenClaw, including free models): full v2 ceremony.
|
|
10
|
+
* Missing config → guided (existing consumers unchanged).
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
15
|
+
const requireCjs = createRequire(import.meta.url);
|
|
16
|
+
const stationHeat = requireCjs('../integrations/hooks/station-hook-runtime.cjs');
|
|
17
|
+
export const DEFAULT_HOST_PROFILES = {
|
|
18
|
+
grok: 'frontier',
|
|
19
|
+
hermes: 'guided',
|
|
20
|
+
opencode: 'guided',
|
|
21
|
+
openclaw: 'guided',
|
|
22
|
+
generic: 'guided',
|
|
23
|
+
};
|
|
24
|
+
function isSuitProfile(value) {
|
|
25
|
+
return value === 'frontier' || value === 'guided' || value === 'strict';
|
|
26
|
+
}
|
|
27
|
+
export function loadSuitTemperamentRaw(projectRoot) {
|
|
28
|
+
const featuresPath = path.join(projectRoot, '.xray', 'features.json');
|
|
29
|
+
if (!fs.existsSync(featuresPath))
|
|
30
|
+
return undefined;
|
|
31
|
+
try {
|
|
32
|
+
const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
|
|
33
|
+
return data.suit_temperament;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function isSuitHost(value) {
|
|
40
|
+
return (value === 'grok' ||
|
|
41
|
+
value === 'hermes' ||
|
|
42
|
+
value === 'opencode' ||
|
|
43
|
+
value === 'openclaw' ||
|
|
44
|
+
value === 'generic');
|
|
45
|
+
}
|
|
46
|
+
function readSessionBoot(projectRoot) {
|
|
47
|
+
try {
|
|
48
|
+
const bootPath = path.join(projectRoot, '.xray', 'state', 'session-boot.json');
|
|
49
|
+
if (!fs.existsSync(bootPath))
|
|
50
|
+
return null;
|
|
51
|
+
const boot = JSON.parse(fs.readFileSync(bootPath, 'utf8'));
|
|
52
|
+
return {
|
|
53
|
+
host: isSuitHost(boot.host) ? boot.host : null,
|
|
54
|
+
suit_profile: isSuitProfile(boot.suit_profile) ? boot.suit_profile : null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function resolveSuitProfile(raw, host) {
|
|
62
|
+
// No key / empty object without profile → guided (existing consumers).
|
|
63
|
+
if (!raw || !raw.profile)
|
|
64
|
+
return 'guided';
|
|
65
|
+
if (raw.profile !== 'auto') {
|
|
66
|
+
return raw.profile;
|
|
67
|
+
}
|
|
68
|
+
const defaults = {
|
|
69
|
+
...DEFAULT_HOST_PROFILES,
|
|
70
|
+
...(raw.host_defaults ?? {}),
|
|
71
|
+
};
|
|
72
|
+
return defaults[host] ?? 'guided';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Host-aware when known. Kernel/MCP (`generic`) uses last session-boot profile.
|
|
76
|
+
* A Hermes/OpenCode host ignores a leftover Grok boot (and vice versa).
|
|
77
|
+
*/
|
|
78
|
+
export function resolveRuntimeSuitProfile(projectRoot, host = 'generic') {
|
|
79
|
+
const raw = loadSuitTemperamentRaw(projectRoot);
|
|
80
|
+
const boot = readSessionBoot(projectRoot);
|
|
81
|
+
if (raw?.profile === 'auto' && boot?.suit_profile) {
|
|
82
|
+
if (host === 'generic' || boot.host == null || boot.host === host) {
|
|
83
|
+
return boot.suit_profile;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return resolveSuitProfile(raw, host);
|
|
87
|
+
}
|
|
88
|
+
function concreteSessionId(value) {
|
|
89
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Write station heat when this host/session has no live card yet.
|
|
93
|
+
* OpenClaw has no SessionStart — PreToolUse is the session boundary.
|
|
94
|
+
* Install/init cards without a session id are not a live session.
|
|
95
|
+
*/
|
|
96
|
+
export function maybeHeatHostStation(projectRoot, host, extra = {}) {
|
|
97
|
+
const bootPath = path.join(projectRoot, '.xray', 'state', 'session-boot.json');
|
|
98
|
+
const cardPath = path.join(projectRoot, '.xray', 'state', 'STATION.md');
|
|
99
|
+
let existing = {};
|
|
100
|
+
if (fs.existsSync(bootPath)) {
|
|
101
|
+
try {
|
|
102
|
+
existing = JSON.parse(fs.readFileSync(bootPath, 'utf8'));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
existing = {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const sessionId = concreteSessionId(extra.sessionId);
|
|
109
|
+
const existingSessionId = concreteSessionId(existing.sessionId);
|
|
110
|
+
const sameHost = existing.host === host;
|
|
111
|
+
const sameSession = existingSessionId !== null && sessionId !== null && existingSessionId === sessionId;
|
|
112
|
+
if (sameHost && sameSession && fs.existsSync(cardPath)) {
|
|
113
|
+
return bootPath;
|
|
114
|
+
}
|
|
115
|
+
return writeSuitSessionBoot(projectRoot, host, extra);
|
|
116
|
+
}
|
|
117
|
+
/** Merge temperament fields into session-boot.json for the host that just started. */
|
|
118
|
+
export function writeSuitSessionBoot(projectRoot, host, extra = {}) {
|
|
119
|
+
const profile = resolveSuitProfile(loadSuitTemperamentRaw(projectRoot), host);
|
|
120
|
+
const stateDir = path.join(projectRoot, '.xray', 'state');
|
|
121
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
122
|
+
const bootPath = path.join(stateDir, 'session-boot.json');
|
|
123
|
+
let existing = {};
|
|
124
|
+
if (fs.existsSync(bootPath)) {
|
|
125
|
+
try {
|
|
126
|
+
existing = JSON.parse(fs.readFileSync(bootPath, 'utf8'));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
existing = {};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const heat = stationHeat.applyStationHeat(projectRoot, host, extra, existing);
|
|
133
|
+
const payload = {
|
|
134
|
+
...existing,
|
|
135
|
+
...extra,
|
|
136
|
+
...heat,
|
|
137
|
+
host,
|
|
138
|
+
suit_profile: profile,
|
|
139
|
+
ceremony: ceremonyForProfile(profile),
|
|
140
|
+
spawn_plan_mode: spawnPlanModeForProfile(profile),
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
};
|
|
143
|
+
fs.writeFileSync(bootPath, JSON.stringify(payload, null, 2));
|
|
144
|
+
stationHeat.writeStationMarkdown(projectRoot, payload);
|
|
145
|
+
return bootPath;
|
|
146
|
+
}
|
|
147
|
+
export function ceremonyForProfile(profile) {
|
|
148
|
+
return profile === 'frontier' ? 'lite' : 'full';
|
|
149
|
+
}
|
|
150
|
+
export function spawnPlanModeForProfile(profile) {
|
|
151
|
+
if (profile === 'frontier')
|
|
152
|
+
return 'warn';
|
|
153
|
+
return 'deny';
|
|
154
|
+
}
|
|
155
|
+
export function conferDefaultForProfile(profile) {
|
|
156
|
+
return profile !== 'frontier';
|
|
157
|
+
}
|
|
@@ -119,7 +119,7 @@ export function buildUserAsidePlan(asideId, title, description, taskInputs = [],
|
|
|
119
119
|
return null;
|
|
120
120
|
const safeId = assertSafeAsideId(asideId);
|
|
121
121
|
const taskTypes = taskInputs.map((t) => t.type ?? 'implement');
|
|
122
|
-
const basePlan = buildLeadDevPlan(description, taskTypes, taskInputs, mcpOverallComplexity);
|
|
122
|
+
const basePlan = buildLeadDevPlan(description, taskTypes, taskInputs, mcpOverallComplexity, projectRoot);
|
|
123
123
|
if (!basePlan)
|
|
124
124
|
return null;
|
|
125
125
|
return {
|