0xray 3.5.4 → 4.0.0

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 (266) hide show
  1. package/.mcp.json +51 -7
  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 +32 -6
  39. package/.xray/features.schema.json +87 -0
  40. package/AGENTS-consumer.md +16 -6
  41. package/AGENTS.md +21 -3
  42. package/README.md +13 -10
  43. package/SKILLS.md +1 -1
  44. package/dist/AGENTS.md +21 -3
  45. package/dist/CHANGELOG.md +98 -0
  46. package/dist/README.md +13 -10
  47. package/dist/SKILLS.md +1 -1
  48. package/dist/cli/commands/hermes-install.js +6 -1
  49. package/dist/cli/commands/openclaw-install.js +14 -0
  50. package/dist/cli/index.js +0 -0
  51. package/dist/core/features-config.d.ts +30 -2
  52. package/dist/governance/governance-service.d.ts +1 -0
  53. package/dist/governance/governance-service.js +81 -6
  54. package/dist/infrastructure/schemas/cloud-schemas.d.ts +1 -1
  55. package/dist/integrations/enforcement-gate.js +15 -5
  56. package/dist/integrations/grok/grok-cli.d.ts +3 -0
  57. package/dist/integrations/grok/grok-cli.js +92 -13
  58. package/dist/integrations/grok/hooks/grok-hook-utils.js +99 -9
  59. package/dist/integrations/grok/hooks/post-tool-use.js +68 -25
  60. package/dist/integrations/grok/hooks/pre-tool-use.js +17 -14
  61. package/dist/integrations/grok/hooks/session-start.js +114 -4
  62. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  63. package/dist/integrations/hermes-agent/__init__.py +11 -1
  64. package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
  65. package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
  66. package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
  67. package/dist/integrations/hermes-agent/bridge.mjs +92 -11
  68. package/dist/integrations/hooks/confer-hook-runtime.mjs +49 -0
  69. package/dist/integrations/hooks/delegation-gate-runtime.mjs +32 -3
  70. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +211 -0
  71. package/dist/integrations/hooks/plan-hook-runtime.mjs +68 -0
  72. package/dist/integrations/hooks/station-hook-runtime.cjs +421 -0
  73. package/dist/integrations/hooks/station-hook-runtime.mjs +24 -0
  74. package/dist/integrations/hooks/station-memory-ingest.mjs +45 -0
  75. package/dist/integrations/hooks/station-memory-match.mjs +47 -0
  76. package/dist/integrations/hooks/user-aside-hook-runtime.mjs +49 -0
  77. package/dist/integrations/openclaw/client.d.ts +5 -1
  78. package/dist/integrations/openclaw/client.js +17 -9
  79. package/dist/integrations/openclaw/config.js +0 -1
  80. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +102 -0
  81. package/dist/integrations/openclaw/index.js +17 -0
  82. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  83. package/dist/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  84. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +10 -0
  85. package/dist/integrations/openclaw/pre-tool-gate.d.ts +19 -0
  86. package/dist/integrations/openclaw/pre-tool-gate.js +39 -0
  87. package/dist/mcps/architect-tools.server.js +6 -1
  88. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +7 -0
  89. package/dist/mcps/orchestrator/handlers/complexity-handler.js +107 -8
  90. package/dist/mcps/orchestrator/handlers/task-handler.d.ts +6 -0
  91. package/dist/mcps/orchestrator/handlers/task-handler.js +75 -0
  92. package/dist/mcps/orchestrator/server.js +54 -5
  93. package/dist/memory-routing/index.d.ts +1 -1
  94. package/dist/memory-routing/index.js +1 -1
  95. package/dist/memory-routing/provider-loader.d.ts +4 -0
  96. package/dist/memory-routing/provider-loader.js +74 -4
  97. package/dist/memory-routing/provider-registry.js +3 -2
  98. package/dist/nucleus/aside-worktree.d.ts +41 -0
  99. package/dist/nucleus/aside-worktree.js +164 -0
  100. package/dist/nucleus/autonomy-kernel.d.ts +2 -2
  101. package/dist/nucleus/autonomy-kernel.js +17 -6
  102. package/dist/nucleus/confer.d.ts +57 -0
  103. package/dist/nucleus/confer.js +322 -0
  104. package/dist/nucleus/delegation-gate.d.ts +18 -8
  105. package/dist/nucleus/delegation-gate.js +234 -20
  106. package/dist/nucleus/lead-dev-plan-persistence.d.ts +27 -3
  107. package/dist/nucleus/lead-dev-plan-persistence.js +201 -19
  108. package/dist/nucleus/spawn-plan-resolution.d.ts +20 -0
  109. package/dist/nucleus/spawn-plan-resolution.js +42 -0
  110. package/dist/nucleus/suit-temperament.d.ts +40 -0
  111. package/dist/nucleus/suit-temperament.js +157 -0
  112. package/dist/nucleus/synthesis-consult-receipt.js +18 -2
  113. package/dist/nucleus/synthesis-context.js +1 -1
  114. package/dist/nucleus/user-aside.d.ts +70 -0
  115. package/dist/nucleus/user-aside.js +393 -0
  116. package/dist/plugin/xray-codex-injection.js +14 -3
  117. package/dist/postprocessor/PostProcessor.d.ts +1 -0
  118. package/dist/postprocessor/PostProcessor.js +11 -0
  119. package/dist/postprocessor/triggers/GitHookTrigger.js +11 -5
  120. package/dist/processors/implementations/session-summary-processor.d.ts +1 -1
  121. package/dist/processors/implementations/session-summary-processor.js +9 -6
  122. package/opencode.json +88 -0
  123. package/package.json +25 -6
  124. package/scripts/hooks/install-hooks.cjs +21 -6
  125. package/scripts/hooks/run-hook.js +237 -88
  126. package/scripts/mjs/run-repertoire-mcp.mjs +39 -0
  127. package/scripts/mjs/verify-confer-core.mjs +103 -0
  128. package/scripts/mjs/verify-delegation-gate-core.mjs +76 -3
  129. package/scripts/mjs/verify-four-floor-compact.mjs +524 -0
  130. package/scripts/mjs/verify-hermes-session-start.mjs +137 -0
  131. package/scripts/mjs/verify-pipeline-facets.mjs +215 -0
  132. package/scripts/mjs/verify-pre-push-diff-core.mjs +87 -0
  133. package/scripts/mjs/verify-temperament-live.mjs +261 -0
  134. package/scripts/mjs/verify-user-aside-core.mjs +137 -0
  135. package/scripts/node/bridge-mcp-wiring.cjs +287 -29
  136. package/scripts/node/install-bridges.cjs +198 -44
  137. package/scripts/node/load-reflection-config.mjs +2 -2
  138. package/scripts/node/postinstall.cjs +50 -27
  139. package/scripts/node/release-gate.mjs +8 -4
  140. package/scripts/node/release.mjs +2 -2
  141. package/scripts/node/validate-release-docs.mjs +20 -33
  142. package/scripts/node/version-manager.mjs +25 -10
  143. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  144. package/src/integrations/openclaw/README.md +134 -0
  145. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  146. package/src/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  147. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +10 -0
  148. package/src/mcps/architect-tools.server.ts +6 -1
  149. package/src/mcps/orchestrator/handlers/complexity-handler.ts +180 -21
  150. package/src/mcps/orchestrator/handlers/task-handler.ts +99 -0
  151. package/src/mcps/orchestrator/server.ts +56 -5
  152. package/vendor/@0xray/repertoire/AGENTS.md +163 -0
  153. package/vendor/@0xray/repertoire/LICENSE +21 -0
  154. package/vendor/@0xray/repertoire/README.md +120 -0
  155. package/vendor/@0xray/repertoire/data/curated_signals.json +208 -0
  156. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +78 -0
  157. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -0
  158. package/vendor/@0xray/repertoire/dist/RepertoireService.js +153 -0
  159. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -0
  160. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts +39 -0
  161. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +1 -0
  162. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js +60 -0
  163. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +1 -0
  164. package/vendor/@0xray/repertoire/dist/index.d.ts +14 -0
  165. package/vendor/@0xray/repertoire/dist/index.d.ts.map +1 -0
  166. package/vendor/@0xray/repertoire/dist/index.js +14 -0
  167. package/vendor/@0xray/repertoire/dist/index.js.map +1 -0
  168. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts +26 -0
  169. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +1 -0
  170. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js +109 -0
  171. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +1 -0
  172. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts +18 -0
  173. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +1 -0
  174. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js +94 -0
  175. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +1 -0
  176. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts +7 -0
  177. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +1 -0
  178. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js +18 -0
  179. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +1 -0
  180. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts +31 -0
  181. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -0
  182. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +83 -0
  183. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -0
  184. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts +6 -0
  185. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +1 -0
  186. package/vendor/@0xray/repertoire/dist/mcp/server.js +173 -0
  187. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +1 -0
  188. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts +27 -0
  189. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +1 -0
  190. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js +107 -0
  191. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +1 -0
  192. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts +11 -0
  193. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +1 -0
  194. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js +32 -0
  195. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +1 -0
  196. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts +9 -0
  197. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -0
  198. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +72 -0
  199. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -0
  200. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts +5 -0
  201. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +1 -0
  202. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js +18 -0
  203. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +1 -0
  204. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts +31 -0
  205. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -0
  206. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +218 -0
  207. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -0
  208. package/vendor/@0xray/repertoire/dist/paths.d.ts +24 -0
  209. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -0
  210. package/vendor/@0xray/repertoire/dist/paths.js +74 -0
  211. package/vendor/@0xray/repertoire/dist/paths.js.map +1 -0
  212. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +141 -0
  213. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -0
  214. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +210 -0
  215. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -0
  216. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +48 -0
  217. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -0
  218. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +221 -0
  219. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -0
  220. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts +11 -0
  221. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +1 -0
  222. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js +43 -0
  223. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +1 -0
  224. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts +15 -0
  225. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +1 -0
  226. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js +35 -0
  227. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +1 -0
  228. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts +3 -0
  229. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +1 -0
  230. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js +24 -0
  231. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +1 -0
  232. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts +24 -0
  233. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +1 -0
  234. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js +148 -0
  235. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +1 -0
  236. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts +4 -0
  237. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +1 -0
  238. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js +44 -0
  239. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +1 -0
  240. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts +22 -0
  241. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +1 -0
  242. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js +66 -0
  243. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +1 -0
  244. package/vendor/@0xray/repertoire/dist/types.d.ts +210 -0
  245. package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -0
  246. package/vendor/@0xray/repertoire/dist/types.js +2 -0
  247. package/vendor/@0xray/repertoire/dist/types.js.map +1 -0
  248. package/vendor/@0xray/repertoire/docs/MEMORY-ROUTING-PROVIDER.md +54 -0
  249. package/vendor/@0xray/repertoire/hermes-mcp.example.json +20 -0
  250. package/vendor/@0xray/repertoire/mcp-config.example.json +11 -0
  251. package/vendor/@0xray/repertoire/package.json +124 -0
  252. package/vendor/@0xray/repertoire/schemas/curated-signal.schema.json +68 -0
  253. package/vendor/@0xray/repertoire/schemas/inference-log.schema.json +82 -0
  254. package/vendor/@0xray/repertoire/scripts/confirm-suit-all.mjs +253 -0
  255. package/vendor/@0xray/repertoire/scripts/confirm-suit-harness.mjs +220 -0
  256. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +215 -0
  257. package/vendor/@0xray/repertoire/scripts/suit-bridge-shared.mjs +139 -0
  258. package/vendor/@0xray/repertoire/scripts/verify-grok-suit.mjs +304 -0
  259. package/vendor/@0xray/repertoire/scripts/verify-hermes-suit.mjs +163 -0
  260. package/vendor/@0xray/repertoire/scripts/verify-openclaw-suit.mjs +125 -0
  261. package/vendor/@0xray/repertoire/scripts/verify-opencode-suit.mjs +127 -0
  262. package/vendor/@0xray/repertoire/scripts/verify-synthesis-dogfood.mjs +207 -0
  263. package/vendor/@0xray/repertoire/skills/repertoire-trap-handling/SKILL.md +206 -0
  264. package/xray/codex.json +12 -3
  265. package/xray/features.json +32 -6
  266. package/xray/features.schema.json +87 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * verify-user-aside-core — user aside SSOT + spawn routing.
4
+ */
5
+ import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
6
+ import { tmpdir } from 'node:os';
7
+ import { dirname, join } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { getActiveUserAsideBoot } from '../../dist/integrations/hooks/user-aside-hook-runtime.mjs';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const packageRoot = join(__dirname, '../..');
13
+
14
+ function dist(rel) {
15
+ return join(packageRoot, 'dist', rel);
16
+ }
17
+
18
+ const {
19
+ buildUserAsidePlan,
20
+ saveUserAside,
21
+ setActiveAsideId,
22
+ clearActiveAside,
23
+ getActiveAsideId,
24
+ isUserAsideTodoId,
25
+ assertSafeAsideId,
26
+ UserAsideValidationError,
27
+ getUserAsideBootHints,
28
+ } = await import(dist('nucleus/user-aside.js'));
29
+ const { resolveSpawnPlan } = await import(dist('nucleus/spawn-plan-resolution.js'));
30
+ const { validateSpawnMatchesTodo } = await import(dist('nucleus/lead-dev-plan-persistence.js'));
31
+ const { evaluatePreToolGate } = await import(dist('nucleus/delegation-gate.js'));
32
+
33
+ let passed = 0;
34
+ let failed = 0;
35
+ const pass = (label) => {
36
+ passed += 1;
37
+ process.stdout.write(`✅ ${label}\n`);
38
+ };
39
+ const fail = (label, detail) => {
40
+ failed += 1;
41
+ process.stderr.write(`❌ ${label}${detail ? ` — ${detail}` : ''}\n`);
42
+ };
43
+
44
+ const tmp = mkdtempSync(join(tmpdir(), 'xray-user-aside-verify-'));
45
+ mkdirSync(join(tmp, '.xray'), { recursive: true });
46
+ writeFileSync(
47
+ join(tmp, '.xray/features.json'),
48
+ JSON.stringify({
49
+ multi_agent_orchestration: {
50
+ enabled: true,
51
+ lead_dev_mode: true,
52
+ user_asides: { enabled: true },
53
+ },
54
+ }),
55
+ );
56
+
57
+ try {
58
+ const namespaced = 'suit-nft.a.1.1';
59
+ if (!isUserAsideTodoId(namespaced)) fail('step 1: namespaced todo pattern');
60
+ else pass('step 1: namespaced todo pattern');
61
+
62
+ try {
63
+ assertSafeAsideId('../bad');
64
+ fail('step 2: path rejection');
65
+ } catch (err) {
66
+ if (err instanceof UserAsideValidationError) pass('step 2: rejects unsafe aside id');
67
+ else fail('step 2: path rejection', String(err));
68
+ }
69
+
70
+ const aside = buildUserAsidePlan(
71
+ 'suit-nft',
72
+ 'Suit NFT',
73
+ 'Mint on Base',
74
+ [{ description: 'Groover proof', type: 'research' }],
75
+ 35,
76
+ tmp,
77
+ );
78
+ if (aside?.plan.phases[0]?.todos[0]?.id !== 'suit-nft.a.1.1') {
79
+ fail('step 3: build aside plan', aside?.plan.phases[0]?.todos[0]?.id);
80
+ } else pass('step 3: namespaced aside todos');
81
+
82
+ saveUserAside(aside, tmp);
83
+ setActiveAsideId('suit-nft', tmp, 'verify-session');
84
+ if (getActiveAsideId(tmp, 'verify-session') !== 'suit-nft') fail('step 4: active pointer');
85
+ else pass('step 4: active aside pointer with session');
86
+
87
+ const todo = aside.plan.phases[0].todos[0];
88
+ const resolved = resolveSpawnPlan({ planTodoId: todo.id }, tmp, 'verify-session');
89
+ if (resolved.source !== 'aside') fail('step 5: resolve spawn plan', JSON.stringify(resolved));
90
+ else pass('step 5: spawn plan resolves to aside');
91
+
92
+ const validation = validateSpawnMatchesTodo(
93
+ { planTodoId: todo.id, subagent_type: todo.subagent, prompt: todo.id },
94
+ tmp,
95
+ undefined,
96
+ 'verify-session',
97
+ );
98
+ if (!validation.valid) fail('step 6: spawn gate', validation.reason);
99
+ else pass('step 6: spawn gate accepts aside todo');
100
+
101
+ const boot = getUserAsideBootHints(tmp, 'verify-session');
102
+ if (!boot?.activeAside) fail('step 7: boot hints', JSON.stringify(boot));
103
+ else pass('step 7: session-boot hints from getUserAsideBootHints');
104
+
105
+ const hookBoot = getActiveUserAsideBoot(tmp, 'verify-session');
106
+ if (!hookBoot?.activeAside) fail('step 8: hook runtime', JSON.stringify(hookBoot));
107
+ else pass('step 8: user-aside-hook-runtime import');
108
+
109
+ clearActiveAside(tmp);
110
+ if (getActiveAsideId(tmp) !== null) fail('step 9: clear active');
111
+ else pass('step 9: clear active aside');
112
+
113
+ setActiveAsideId('suit-nft', tmp, 'verify-session');
114
+ const wtPath = join(tmp, 'aside-worktree');
115
+ aside.worktree = wtPath;
116
+ saveUserAside(aside, tmp);
117
+ const denySpawn = evaluatePreToolGate(
118
+ 'Task',
119
+ { planTodoId: todo.id, subagent_type: todo.subagent, prompt: todo.id },
120
+ {
121
+ projectRoot: tmp,
122
+ sessionId: 'verify-session',
123
+ features: { lead_dev_mode: true, auto_chain_delegations: true },
124
+ },
125
+ );
126
+ if (denySpawn.allow) fail('step 10: worktree cwd deny', JSON.stringify(denySpawn));
127
+ else if (denySpawn.gate !== 'aside-worktree-cwd-missing') {
128
+ fail('step 10: worktree cwd gate', denySpawn.gate);
129
+ } else pass('step 10: denies aside spawn without worktree cwd');
130
+ } finally {
131
+ rmSync(tmp, { recursive: true, force: true });
132
+ }
133
+
134
+ process.stdout.write(
135
+ `\n${failed === 0 ? `✅ verify-user-aside-core ${passed}/${passed}` : `⚠️ ${failed} user-aside check(s) failed`}\n`,
136
+ );
137
+ process.exit(failed > 0 ? 1 : 0);
@@ -6,7 +6,7 @@
6
6
  const fs = require("fs");
7
7
  const path = require("path");
8
8
  const os = require("os");
9
- const { execSync } = require("child_process");
9
+ const { execFileSync, execSync } = require("child_process");
10
10
 
11
11
  /** Canonical 7-server MCP surface */
12
12
  const XRAY_MCP_SERVERS = [
@@ -24,31 +24,118 @@ const HERMES_PLUGIN_DIR = path.join(os.homedir(), ".hermes", "plugins", "xray-he
24
24
  const OPENCLAW_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json");
25
25
  const OPENCLAW_STATE_DIR = path.join(os.homedir(), ".openclaw");
26
26
 
27
+ function isRepertoirePackageRoot(dir) {
28
+ try {
29
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
30
+ return pkg.name === "@0xray/repertoire" || pkg.name === "repertoire";
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ function resolveRepertoireMcp(targetDir) {
37
+ const selfRoot = targetDir;
38
+ const siblingRoot = path.join(targetDir, "..", "repertoire");
39
+ const candidates = [
40
+ path.join(targetDir, "node_modules", "@0xray", "repertoire", "dist", "mcp", "server.js"),
41
+ path.join(targetDir, "vendor", "@0xray", "repertoire", "dist", "mcp", "server.js"),
42
+ isRepertoirePackageRoot(selfRoot) ? path.join(selfRoot, "dist", "mcp", "server.js") : null,
43
+ isRepertoirePackageRoot(siblingRoot) ? path.join(siblingRoot, "dist", "mcp", "server.js") : null,
44
+ ];
45
+ return candidates.find((p) => p && fs.existsSync(p)) || null;
46
+ }
47
+
48
+ function resolveRepertoireProvider(targetDir) {
49
+ const siblingRoot = path.join(targetDir, "..", "repertoire");
50
+ const siblingProvider = path.join(siblingRoot, "dist", "provider", "memory-routing-provider.js");
51
+ const nmProvider = path.join(
52
+ targetDir,
53
+ "node_modules",
54
+ "@0xray",
55
+ "repertoire",
56
+ "dist",
57
+ "provider",
58
+ "memory-routing-provider.js",
59
+ );
60
+ const vendorProvider = path.join(
61
+ targetDir,
62
+ "vendor",
63
+ "@0xray",
64
+ "repertoire",
65
+ "dist",
66
+ "provider",
67
+ "memory-routing-provider.js",
68
+ );
69
+ if (fs.existsSync(nmProvider)) return nmProvider;
70
+ if (fs.existsSync(vendorProvider)) return vendorProvider;
71
+ if (isRepertoirePackageRoot(siblingRoot) && fs.existsSync(siblingProvider)) return siblingProvider;
72
+ return null;
73
+ }
74
+
75
+ function isDefaultMemoryRoutingOff(mr) {
76
+ if (!mr || typeof mr !== "object") return true;
77
+ if (mr.enabled === true) return false;
78
+ const provider = mr.provider == null || mr.provider === "" ? "null" : mr.provider;
79
+ return provider === "null";
80
+ }
81
+
82
+ function enableMemoryRoutingIfResolves(features, targetDir) {
83
+ const modulePath = resolveRepertoireProvider(targetDir);
84
+ if (!modulePath) return { features, changed: false };
85
+ const mr = (features && features.memory_routing) || {};
86
+ if (!isDefaultMemoryRoutingOff(mr)) return { features, changed: false };
87
+
88
+ const siblingRel = "../repertoire/dist/provider/memory-routing-provider.js";
89
+ const siblingAbs = path.join(targetDir, "..", "repertoire", "dist", "provider", "memory-routing-provider.js");
90
+ const useSibling = path.resolve(modulePath) === path.resolve(siblingAbs);
91
+ const repertoireRoot = path.resolve(modulePath, "..", "..", "..");
92
+ const signalsAbs = path.join(repertoireRoot, "data", "curated_signals.json");
93
+ const config = { ...(mr.config || {}) };
94
+ if (!config.signalsPath && fs.existsSync(signalsAbs)) {
95
+ config.signalsPath = useSibling ? "../repertoire/data/curated_signals.json" : signalsAbs;
96
+ }
97
+ if (!config.statePath) {
98
+ config.statePath = ".xray/state/repertoire/inference-state.json";
99
+ }
100
+ if (!config.feedbackDir) {
101
+ config.feedbackDir = ".xray/state/repertoire/feedback";
102
+ }
103
+
104
+ return {
105
+ features: {
106
+ ...features,
107
+ memory_routing: {
108
+ ...mr,
109
+ enabled: true,
110
+ provider: "repertoire",
111
+ module_path: useSibling ? siblingRel : modulePath,
112
+ config,
113
+ },
114
+ },
115
+ changed: true,
116
+ };
117
+ }
118
+
27
119
  function detectConsumerExtraMcpServers(targetDir) {
28
120
  const extras = { hermes: {}, opencode: {}, openclaw: {} };
29
121
  try {
30
- const pkgPath = path.join(targetDir, "package.json");
31
- if (!fs.existsSync(pkgPath)) return extras;
32
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
33
- const localRepertoireMcp = path.join(targetDir, "dist", "mcp", "server.js");
34
- if (pkg.name === "@0xray/repertoire" && fs.existsSync(localRepertoireMcp)) {
35
- extras.hermes.repertoire = {
36
- command: "node",
37
- args: [localRepertoireMcp],
38
- env: { XRAY_ROOT: targetDir },
39
- };
40
- extras.opencode.repertoire = {
41
- type: "local",
42
- command: ["node", "dist/mcp/server.js"],
43
- enabled: true,
44
- cwd: ".",
45
- };
46
- extras.openclaw.repertoire = {
47
- command: "node",
48
- args: [localRepertoireMcp],
49
- env: { XRAY_ROOT: targetDir },
50
- };
51
- }
122
+ const repertoireMcp = resolveRepertoireMcp(targetDir);
123
+ if (!repertoireMcp) return extras;
124
+ extras.hermes.repertoire = {
125
+ command: "node",
126
+ args: [repertoireMcp],
127
+ env: { XRAY_ROOT: targetDir },
128
+ };
129
+ extras.opencode.repertoire = {
130
+ type: "local",
131
+ command: ["node", repertoireMcp],
132
+ enabled: true,
133
+ };
134
+ extras.openclaw.repertoire = {
135
+ command: "node",
136
+ args: [repertoireMcp],
137
+ env: { XRAY_ROOT: targetDir },
138
+ };
52
139
  } catch {
53
140
  // best-effort
54
141
  }
@@ -121,13 +208,40 @@ function buildPortableProjectMcpJson() {
121
208
  return { mcpServers };
122
209
  }
123
210
 
211
+ function copyHermesFindProjectRootHelper(packageRoot, targetPluginDir) {
212
+ const helperSrc = path.join(packageRoot, "scripts", "helpers", "find-project-root.mjs");
213
+ if (!fs.existsSync(helperSrc)) return false;
214
+ const helperDst = path.join(targetPluginDir, "scripts", "helpers", "find-project-root.mjs");
215
+ fs.mkdirSync(path.dirname(helperDst), { recursive: true });
216
+ fs.copyFileSync(helperSrc, helperDst);
217
+ return true;
218
+ }
219
+
220
+ function isEphemeralInstallRoot(targetDir) {
221
+ const normalized = String(targetDir || "").replace(/\\/g, "/");
222
+ return /\/T\/|\/tmp\/|\/var\/folders\/|\/Temp\//i.test(normalized);
223
+ }
224
+
225
+ function copyHermesHookRuntimes(packageRoot) {
226
+ const hooksSrc = [
227
+ path.join(packageRoot, "dist", "integrations", "hooks"),
228
+ path.join(packageRoot, "src", "integrations", "hooks"),
229
+ ].find((p) => fs.existsSync(p));
230
+ if (!hooksSrc) return false;
231
+ const hooksDst = path.join(os.homedir(), ".hermes", "plugins", "hooks");
232
+ fs.mkdirSync(hooksDst, { recursive: true });
233
+ fs.cpSync(hooksSrc, hooksDst, { recursive: true, force: true });
234
+ return true;
235
+ }
236
+
124
237
  function writeHermesPluginArtifacts(targetDir) {
125
238
  if (!fs.existsSync(HERMES_PLUGIN_DIR)) return false;
126
- fs.writeFileSync(path.join(HERMES_PLUGIN_DIR, "xray-consumer-root.txt"), `${targetDir}\n`);
127
239
  fs.writeFileSync(
128
240
  path.join(HERMES_PLUGIN_DIR, ".mcp.json"),
129
241
  `${JSON.stringify(buildPluginMcpJson(targetDir), null, 2)}\n`
130
242
  );
243
+ if (isEphemeralInstallRoot(targetDir)) return true;
244
+ fs.writeFileSync(path.join(HERMES_PLUGIN_DIR, "xray-consumer-root.txt"), `${targetDir}\n`);
131
245
  return true;
132
246
  }
133
247
 
@@ -183,11 +297,13 @@ function deployPortableProjectMcpJson(targetDir) {
183
297
  existing = { mcpServers: {} };
184
298
  }
185
299
  }
300
+ const extras = detectConsumerExtraMcpServers(targetDir);
186
301
  const merged = {
187
302
  ...existing,
188
303
  mcpServers: {
189
304
  ...(existing.mcpServers || {}),
190
305
  ...portable.mcpServers,
306
+ ...(extras.openclaw || {}),
191
307
  },
192
308
  };
193
309
  fs.writeFileSync(destPath, `${JSON.stringify(merged, null, 2)}\n`);
@@ -195,10 +311,20 @@ function deployPortableProjectMcpJson(targetDir) {
195
311
 
196
312
  function enableHermesPluginBestEffort() {
197
313
  try {
198
- execSync("hermes plugins enable 0xray-hermes", { stdio: "pipe", encoding: "utf8" });
314
+ execSync("hermes plugins enable xray-hermes", { stdio: "pipe", encoding: "utf8" });
315
+ try {
316
+ execSync("hermes plugins disable 0xray-hermes", { stdio: "pipe", encoding: "utf8" });
317
+ } catch {
318
+ /* stale id may be absent */
319
+ }
199
320
  return true;
200
321
  } catch {
201
- return false;
322
+ try {
323
+ execSync("hermes plugins enable 0xray-hermes", { stdio: "pipe", encoding: "utf8" });
324
+ return true;
325
+ } catch {
326
+ return false;
327
+ }
202
328
  }
203
329
  }
204
330
 
@@ -260,10 +386,12 @@ function syncOpenClawMcpRegistry(targetDir) {
260
386
  }
261
387
 
262
388
  function writeOpenClawConsumerArtifacts(targetDir) {
263
- if (!fs.existsSync(OPENCLAW_STATE_DIR)) {
264
- fs.mkdirSync(OPENCLAW_STATE_DIR, { recursive: true });
389
+ if (!isEphemeralInstallRoot(targetDir)) {
390
+ if (!fs.existsSync(OPENCLAW_STATE_DIR)) {
391
+ fs.mkdirSync(OPENCLAW_STATE_DIR, { recursive: true });
392
+ }
393
+ fs.writeFileSync(path.join(OPENCLAW_STATE_DIR, "xray-consumer-root.txt"), `${targetDir}\n`);
265
394
  }
266
- fs.writeFileSync(path.join(OPENCLAW_STATE_DIR, "xray-consumer-root.txt"), `${targetDir}\n`);
267
395
 
268
396
  const consumerConfigPath = path.join(targetDir, ".xray", "config", "openclaw.json");
269
397
  if (fs.existsSync(consumerConfigPath)) {
@@ -278,6 +406,124 @@ function writeOpenClawConsumerArtifacts(targetDir) {
278
406
  return true;
279
407
  }
280
408
 
409
+ function resolveOpenClawPreToolHookSource(packageRoot) {
410
+ return [
411
+ path.join(packageRoot, "dist", "integrations", "openclaw", "hooks", "pre-tool-gate-runtime.mjs"),
412
+ path.join(packageRoot, "src", "integrations", "openclaw", "hooks", "pre-tool-gate-runtime.mjs"),
413
+ ].find((p) => fs.existsSync(p)) || null;
414
+ }
415
+
416
+ function resolveOpenClawPluginDir(packageRoot) {
417
+ return [
418
+ path.join(packageRoot, "dist", "integrations", "openclaw", "plugin", "xray-pre-tool"),
419
+ path.join(packageRoot, "src", "integrations", "openclaw", "plugin", "xray-pre-tool"),
420
+ ].find((p) => fs.existsSync(path.join(p, "index.js"))) || null;
421
+ }
422
+
423
+ function installOpenClawHostWear(packageRoot) {
424
+ const source = resolveOpenClawPreToolHookSource(packageRoot);
425
+ if (!source) return null;
426
+
427
+ const hookDir = path.join(os.homedir(), ".openclaw", "hooks");
428
+ fs.mkdirSync(hookDir, { recursive: true });
429
+ const dest = path.join(hookDir, "xray-pre-tool.mjs");
430
+ fs.copyFileSync(source, dest);
431
+ fs.writeFileSync(
432
+ path.join(hookDir, "xray-pre-tool.json"),
433
+ `${JSON.stringify(
434
+ {
435
+ name: "xray-pre-tool",
436
+ command: "node",
437
+ args: [dest],
438
+ stdin: "json",
439
+ blockExitCode: 2,
440
+ env: { XRAY_AI_PATH: packageRoot },
441
+ },
442
+ null,
443
+ 2,
444
+ )}\n`,
445
+ );
446
+
447
+ const pluginSrc = resolveOpenClawPluginDir(packageRoot);
448
+ if (pluginSrc) {
449
+ try {
450
+ execFileSync("openclaw", ["plugins", "install", "-l", pluginSrc], {
451
+ stdio: "pipe",
452
+ encoding: "utf8",
453
+ timeout: 60000,
454
+ });
455
+ } catch {
456
+ /* link is best-effort — stdin runtime is still installed */
457
+ }
458
+ try {
459
+ execFileSync("openclaw", ["config", "set", "plugins.entries.xray-pre-tool.enabled", "true"], {
460
+ stdio: "pipe",
461
+ encoding: "utf8",
462
+ timeout: 20000,
463
+ });
464
+ } catch {
465
+ /* enable is best-effort */
466
+ }
467
+ }
468
+ return dest;
469
+ }
470
+
471
+ function resolveOpencodeBin() {
472
+ try {
473
+ const found = execSync("command -v opencode", { encoding: "utf8" }).trim();
474
+ return found || null;
475
+ } catch {
476
+ return null;
477
+ }
478
+ }
479
+
480
+ function maybeWriteOpenClawCliBackend() {
481
+ const bin = resolveOpencodeBin();
482
+ if (!bin) return false;
483
+ const configPath = resolveOpenClawConfigPath();
484
+ if (!fs.existsSync(configPath)) return false;
485
+ let config;
486
+ try {
487
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
488
+ } catch {
489
+ return false;
490
+ }
491
+ if (!config.agents || typeof config.agents !== "object") config.agents = {};
492
+ if (!config.agents.defaults || typeof config.agents.defaults !== "object") {
493
+ config.agents.defaults = {};
494
+ }
495
+ const defaults = config.agents.defaults;
496
+ if (!defaults.cliBackends || typeof defaults.cliBackends !== "object") {
497
+ defaults.cliBackends = {};
498
+ }
499
+ defaults.cliBackends["opencode-cli"] = {
500
+ command: bin,
501
+ args: ["run", "--pure"],
502
+ output: "text",
503
+ input: "arg",
504
+ modelArg: "--model",
505
+ modelAliases: { "big-pickle": "opencode/big-pickle" },
506
+ sessionMode: "none",
507
+ };
508
+ if (!defaults.models || typeof defaults.models !== "object") defaults.models = {};
509
+ defaults.models["opencode-cli/big-pickle"] = {};
510
+ const model = defaults.model && typeof defaults.model === "object" ? defaults.model : {};
511
+ const primary = model.primary;
512
+ if (!primary || primary === "opencode/big-pickle") {
513
+ const fallbacks = Array.isArray(model.fallbacks) ? model.fallbacks.slice() : [];
514
+ if (primary === "opencode/big-pickle" && !fallbacks.includes("opencode/big-pickle")) {
515
+ fallbacks.unshift("opencode/big-pickle");
516
+ }
517
+ defaults.model = {
518
+ ...model,
519
+ primary: "opencode-cli/big-pickle",
520
+ fallbacks,
521
+ };
522
+ }
523
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
524
+ return true;
525
+ }
526
+
281
527
  function wireOpenClawBridge(targetDir) {
282
528
  writeOpenClawConsumerArtifacts(targetDir);
283
529
  return syncOpenClawMcpRegistry(targetDir);
@@ -295,6 +541,7 @@ module.exports = {
295
541
  buildStdioMcpServer,
296
542
  buildPluginMcpJson,
297
543
  buildPortableProjectMcpJson,
544
+ copyHermesFindProjectRootHelper,
298
545
  writeHermesPluginArtifacts,
299
546
  syncHermesMcpRegistry,
300
547
  mergeOpencodeMcpRegistry,
@@ -307,4 +554,15 @@ module.exports = {
307
554
  writeOpenClawConsumerArtifacts,
308
555
  resolveOpenClawConfigPath,
309
556
  detectConsumerExtraMcpServers,
557
+ isRepertoirePackageRoot,
558
+ resolveRepertoireMcp,
559
+ resolveRepertoireProvider,
560
+ isDefaultMemoryRoutingOff,
561
+ enableMemoryRoutingIfResolves,
562
+ isEphemeralInstallRoot,
563
+ copyHermesHookRuntimes,
564
+ resolveOpenClawPreToolHookSource,
565
+ resolveOpenClawPluginDir,
566
+ installOpenClawHostWear,
567
+ maybeWriteOpenClawCliBackend,
310
568
  };