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.
Files changed (233) hide show
  1. package/.mcp.json +18 -14
  2. package/.opencode/skills/api-design/SKILL.md +1 -1
  3. package/.opencode/skills/architect-tools/SKILL.md +1 -1
  4. package/.opencode/skills/architecture-patterns/SKILL.md +1 -1
  5. package/.opencode/skills/auto-format/SKILL.md +1 -1
  6. package/.opencode/skills/boot-orchestrator/SKILL.md +2 -6
  7. package/.opencode/skills/bug-triage/SKILL.md +1 -1
  8. package/.opencode/skills/code-analyzer/SKILL.md +1 -1
  9. package/.opencode/skills/code-review/SKILL.md +1 -1
  10. package/.opencode/skills/enforcer/SKILL.md +1 -1
  11. package/.opencode/skills/framework-compliance-audit/SKILL.md +1 -1
  12. package/.opencode/skills/git-workflow/SKILL.md +1 -1
  13. package/.opencode/skills/hermes-agent/SKILL.md +36 -36
  14. package/.opencode/skills/inference-improve/SKILL.md +1 -1
  15. package/.opencode/skills/lint/SKILL.md +1 -1
  16. package/.opencode/skills/log-monitor/SKILL.md +1 -1
  17. package/.opencode/skills/model-health-check/SKILL.md +1 -1
  18. package/.opencode/skills/multimodal-looker/SKILL.md +1 -1
  19. package/.opencode/skills/orchestrator/SKILL.md +38 -14
  20. package/.opencode/skills/performance-analysis/SKILL.md +1 -1
  21. package/.opencode/skills/performance-optimization/SKILL.md +1 -1
  22. package/.opencode/skills/processor-pipeline/SKILL.md +1 -1
  23. package/.opencode/skills/project-analysis/SKILL.md +1 -1
  24. package/.opencode/skills/refactoring-strategies/SKILL.md +1 -1
  25. package/.opencode/skills/registry.json +65 -0
  26. package/.opencode/skills/researcher/SKILL.md +18 -5
  27. package/.opencode/skills/security-audit/SKILL.md +1 -1
  28. package/.opencode/skills/security-scan/SKILL.md +1 -1
  29. package/.opencode/skills/session-management/SKILL.md +1 -1
  30. package/.opencode/skills/state-manager/SKILL.md +1 -1
  31. package/.opencode/skills/storyteller/SKILL.md +1 -1
  32. package/.opencode/skills/strategist/SKILL.md +12 -0
  33. package/.opencode/skills/testing-best-practices/SKILL.md +37 -0
  34. package/.opencode/skills/testing-strategy/SKILL.md +14 -3
  35. package/.opencode/skills/ui-ux-design/SKILL.md +1 -1
  36. package/.opencode/skills/xray-orchestrator/index.ts +267 -0
  37. package/.xray/codex.json +12 -3
  38. package/.xray/features.json +28 -6
  39. package/.xray/features.schema.json +31 -0
  40. package/AGENTS-consumer.md +17 -7
  41. package/AGENTS.md +18 -10
  42. package/README.md +30 -27
  43. package/SKILLS.md +2 -2
  44. package/dist/AGENTS.md +18 -10
  45. package/dist/CHANGELOG.md +81 -11
  46. package/dist/README.md +30 -27
  47. package/dist/SKILLS.md +2 -2
  48. package/dist/cli/commands/hermes-install.js +3 -0
  49. package/dist/cli/commands/openclaw-install.js +14 -0
  50. package/dist/cli/index.js +10 -1
  51. package/dist/core/features-config.d.ts +15 -2
  52. package/dist/infrastructure/schemas/cloud-schemas.d.ts +1 -1
  53. package/dist/integrations/grok/grok-cli.d.ts +3 -0
  54. package/dist/integrations/grok/grok-cli.js +92 -4
  55. package/dist/integrations/grok/hooks/grok-hook-utils.js +53 -7
  56. package/dist/integrations/grok/hooks/post-tool-use.js +68 -46
  57. package/dist/integrations/grok/hooks/pre-tool-use.js +14 -31
  58. package/dist/integrations/grok/hooks/session-start.js +86 -4
  59. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  60. package/dist/integrations/hermes-agent/bridge.mjs +26 -1
  61. package/dist/integrations/hooks/delegation-gate-runtime.mjs +32 -3
  62. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +1 -1
  63. package/dist/integrations/hooks/station-hook-runtime.cjs +421 -0
  64. package/dist/integrations/hooks/station-hook-runtime.mjs +24 -0
  65. package/dist/integrations/hooks/station-memory-ingest.mjs +45 -0
  66. package/dist/integrations/hooks/station-memory-match.mjs +47 -0
  67. package/dist/integrations/openclaw/client.d.ts +5 -1
  68. package/dist/integrations/openclaw/client.js +17 -9
  69. package/dist/integrations/openclaw/config.js +0 -1
  70. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +102 -0
  71. package/dist/integrations/openclaw/index.js +17 -0
  72. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  73. package/dist/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  74. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +12 -0
  75. package/dist/integrations/openclaw/pre-tool-gate.d.ts +19 -0
  76. package/dist/integrations/openclaw/pre-tool-gate.js +39 -0
  77. package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
  78. package/dist/memory-routing/index.d.ts +1 -1
  79. package/dist/memory-routing/index.js +1 -1
  80. package/dist/memory-routing/provider-loader.d.ts +4 -0
  81. package/dist/memory-routing/provider-loader.js +44 -2
  82. package/dist/memory-routing/provider-registry.js +3 -2
  83. package/dist/nucleus/autonomy-kernel.d.ts +2 -2
  84. package/dist/nucleus/autonomy-kernel.js +17 -6
  85. package/dist/nucleus/confer.js +17 -3
  86. package/dist/nucleus/delegation-gate.d.ts +14 -8
  87. package/dist/nucleus/delegation-gate.js +151 -16
  88. package/dist/nucleus/suit-temperament.d.ts +40 -0
  89. package/dist/nucleus/suit-temperament.js +157 -0
  90. package/dist/nucleus/user-aside.js +1 -1
  91. package/dist/plugin/xray-codex-injection.js +14 -3
  92. package/dist/postprocessor/triggers/GitHookTrigger.js +11 -5
  93. package/opencode.json +80 -0
  94. package/package.json +15 -5
  95. package/scripts/hooks/install-hooks.cjs +18 -3
  96. package/scripts/hooks/run-hook.js +67 -3
  97. package/scripts/mjs/run-repertoire-mcp.mjs +39 -0
  98. package/scripts/mjs/verify-confer-core.mjs +1 -1
  99. package/scripts/mjs/verify-delegation-gate-core.mjs +29 -2
  100. package/scripts/mjs/verify-four-floor-compact.mjs +524 -0
  101. package/scripts/mjs/verify-pipeline-facets.mjs +215 -0
  102. package/scripts/mjs/verify-temperament-live.mjs +261 -0
  103. package/scripts/node/bridge-mcp-wiring.cjs +277 -29
  104. package/scripts/node/consumer-install-smoke.mjs +55 -2
  105. package/scripts/node/install-bridges.cjs +195 -33
  106. package/scripts/node/postinstall.cjs +50 -27
  107. package/scripts/node/reconcile-version.mjs +0 -4
  108. package/scripts/node/release-gate.mjs +1 -1
  109. package/scripts/node/release.mjs +2 -2
  110. package/scripts/node/universal-version-manager.js +1 -1
  111. package/scripts/node/validate-release-docs.mjs +20 -33
  112. package/scripts/node/version-manager.mjs +63 -18
  113. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  114. package/src/integrations/openclaw/README.md +134 -0
  115. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  116. package/src/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  117. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +12 -0
  118. package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
  119. package/vendor/@0xray/repertoire/AGENTS.md +163 -0
  120. package/vendor/@0xray/repertoire/LICENSE +21 -0
  121. package/vendor/@0xray/repertoire/README.md +120 -0
  122. package/vendor/@0xray/repertoire/data/curated_signals.json +208 -0
  123. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +78 -0
  124. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -0
  125. package/vendor/@0xray/repertoire/dist/RepertoireService.js +153 -0
  126. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -0
  127. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts +39 -0
  128. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +1 -0
  129. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js +60 -0
  130. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +1 -0
  131. package/vendor/@0xray/repertoire/dist/index.d.ts +14 -0
  132. package/vendor/@0xray/repertoire/dist/index.d.ts.map +1 -0
  133. package/vendor/@0xray/repertoire/dist/index.js +14 -0
  134. package/vendor/@0xray/repertoire/dist/index.js.map +1 -0
  135. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts +26 -0
  136. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +1 -0
  137. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js +109 -0
  138. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +1 -0
  139. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts +18 -0
  140. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +1 -0
  141. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js +94 -0
  142. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +1 -0
  143. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts +7 -0
  144. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +1 -0
  145. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js +18 -0
  146. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +1 -0
  147. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts +31 -0
  148. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -0
  149. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +83 -0
  150. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -0
  151. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts +6 -0
  152. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +1 -0
  153. package/vendor/@0xray/repertoire/dist/mcp/server.js +173 -0
  154. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +1 -0
  155. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts +27 -0
  156. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +1 -0
  157. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js +107 -0
  158. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +1 -0
  159. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts +11 -0
  160. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +1 -0
  161. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js +32 -0
  162. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +1 -0
  163. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts +9 -0
  164. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -0
  165. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +72 -0
  166. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -0
  167. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts +5 -0
  168. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +1 -0
  169. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js +18 -0
  170. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +1 -0
  171. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts +31 -0
  172. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -0
  173. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +218 -0
  174. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -0
  175. package/vendor/@0xray/repertoire/dist/paths.d.ts +24 -0
  176. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -0
  177. package/vendor/@0xray/repertoire/dist/paths.js +74 -0
  178. package/vendor/@0xray/repertoire/dist/paths.js.map +1 -0
  179. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +141 -0
  180. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -0
  181. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +210 -0
  182. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -0
  183. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +48 -0
  184. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -0
  185. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +221 -0
  186. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -0
  187. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts +11 -0
  188. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +1 -0
  189. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js +43 -0
  190. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +1 -0
  191. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts +15 -0
  192. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +1 -0
  193. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js +35 -0
  194. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +1 -0
  195. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts +3 -0
  196. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +1 -0
  197. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js +24 -0
  198. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +1 -0
  199. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts +24 -0
  200. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +1 -0
  201. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js +148 -0
  202. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +1 -0
  203. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts +4 -0
  204. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +1 -0
  205. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js +44 -0
  206. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +1 -0
  207. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts +22 -0
  208. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +1 -0
  209. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js +66 -0
  210. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +1 -0
  211. package/vendor/@0xray/repertoire/dist/types.d.ts +210 -0
  212. package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -0
  213. package/vendor/@0xray/repertoire/dist/types.js +2 -0
  214. package/vendor/@0xray/repertoire/dist/types.js.map +1 -0
  215. package/vendor/@0xray/repertoire/docs/MEMORY-ROUTING-PROVIDER.md +54 -0
  216. package/vendor/@0xray/repertoire/hermes-mcp.example.json +20 -0
  217. package/vendor/@0xray/repertoire/mcp-config.example.json +11 -0
  218. package/vendor/@0xray/repertoire/package.json +124 -0
  219. package/vendor/@0xray/repertoire/schemas/curated-signal.schema.json +68 -0
  220. package/vendor/@0xray/repertoire/schemas/inference-log.schema.json +82 -0
  221. package/vendor/@0xray/repertoire/scripts/confirm-suit-all.mjs +253 -0
  222. package/vendor/@0xray/repertoire/scripts/confirm-suit-harness.mjs +220 -0
  223. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +215 -0
  224. package/vendor/@0xray/repertoire/scripts/suit-bridge-shared.mjs +139 -0
  225. package/vendor/@0xray/repertoire/scripts/verify-grok-suit.mjs +304 -0
  226. package/vendor/@0xray/repertoire/scripts/verify-hermes-suit.mjs +163 -0
  227. package/vendor/@0xray/repertoire/scripts/verify-openclaw-suit.mjs +125 -0
  228. package/vendor/@0xray/repertoire/scripts/verify-opencode-suit.mjs +127 -0
  229. package/vendor/@0xray/repertoire/scripts/verify-synthesis-dogfood.mjs +207 -0
  230. package/vendor/@0xray/repertoire/skills/repertoire-trap-handling/SKILL.md +206 -0
  231. package/xray/codex.json +12 -3
  232. package/xray/features.json +28 -6
  233. 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 mandatoryConsults = cfg.auto_consult_major_work !== false ? [...MANDATORY_MAJOR_CONSULTS] : [];
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 mandatoryConsults = cfg.auto_consult_major_work !== false && requiresPhasedPlan
128
- ? [...MANDATORY_MAJOR_CONSULTS]
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
  {
@@ -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
- if (!fs.existsSync(featuresPath))
22
- return defaultConferConfig();
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 defaultConferConfig();
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
- * Grok-only codex/surface checks remain in pre-tool-use.js after the SSOT gate.
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 = 'grok' | 'hermes' | 'opencode' | 'openclaw' | 'generic';
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
- * Full pre-tool evaluation — synthesis gate first, slice recording, then
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
- * Grok-only codex/surface checks remain in pre-tool-use.js after the SSOT gate.
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 { lead_dev_mode: true, auto_chain_delegations: true };
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 { lead_dev_mode: true, auto_chain_delegations: true };
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
- return {
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
- return {
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
- return {
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
- const synthesisBlock = evaluateSynthesisGate(toolName, toolInput, ctx);
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 = evaluatePendingDelegationGate(toolName, toolInput, ctx);
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 {