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
@@ -18,8 +18,9 @@
18
18
  import { mcpClientManager } from '../mcps/mcp-client.js';
19
19
  import { callInProcessSkill } from '../mcps/in-process-skill-registry.js';
20
20
  import { getGovernanceIntegration, } from '../integrations/governance/index.js';
21
- import { mergeVotes } from './governance-core.js';
21
+ import { applyDecisionMatrix, calculateMetamorphosisScore, mergeVotes, } from './governance-core.js';
22
22
  import { frameworkLogger } from '../core/framework-logger.js';
23
+ import { featuresConfigLoader } from '../core/features-config.js';
23
24
  export class GovernanceService {
24
25
  constructor() {
25
26
  // We deliberately do *not* cache the integration here.
@@ -39,7 +40,7 @@ export class GovernanceService {
39
40
  */
40
41
  async govern(request) {
41
42
  const { proposals, context, options } = request;
42
- const requireExternal = options?.requireExternalDynamo ?? true;
43
+ const requireExternal = this.resolveRequireExternalDynamo(options?.requireExternalDynamo);
43
44
  const timeoutMs = options?.timeoutMs ?? 90000;
44
45
  const maxAbstentionThreshold = options?.maxAbstentionThreshold ?? 1.0;
45
46
  frameworkLogger.log('governance-service', 'govern-start', 'info', {
@@ -59,7 +60,7 @@ export class GovernanceService {
59
60
  }
60
61
  }
61
62
  // Run governance with end-to-end timeout
62
- const result = await this.runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs);
63
+ const result = await this.runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs, options?.metamorphosisThreshold);
63
64
  // Check abstention threshold
64
65
  if (result.results.length > 0 && maxAbstentionThreshold < 1.0) {
65
66
  const totalVotes = result.results.reduce((sum, r) => sum + r.votes.length, 0);
@@ -91,7 +92,26 @@ export class GovernanceService {
91
92
  },
92
93
  };
93
94
  }
94
- async runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs) {
95
+ resolveRequireExternalDynamo(explicit) {
96
+ if (explicit !== undefined)
97
+ return explicit;
98
+ if (process.env.XRAY_LOCAL_MODE === '1' || process.env.XRAY_LOCAL_MODE === 'true') {
99
+ return false;
100
+ }
101
+ try {
102
+ const config = featuresConfigLoader.loadConfig();
103
+ const ig = config.inference_governance;
104
+ if (ig?.require_external_dynamo === false)
105
+ return false;
106
+ if (ig?.local_mode === true)
107
+ return false;
108
+ }
109
+ catch {
110
+ /* default strict */
111
+ }
112
+ return true;
113
+ }
114
+ async runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs, metamorphosisThreshold = 0.7) {
95
115
  const controller = new AbortController();
96
116
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
97
117
  try {
@@ -112,12 +132,67 @@ export class GovernanceService {
112
132
  ...(externalVotes[index] || []),
113
133
  ];
114
134
  const merged = mergeVotes(votes);
135
+ const externalVote = votes.find((v) => v.server === 'external-dynamo');
136
+ let finalDecision = merged.finalDecision;
137
+ let averageConfidence = merged.averageConfidence;
138
+ let moralOverride = 'none';
139
+ if (externalVote?.moralTension ||
140
+ externalVote?.moralScore != null ||
141
+ externalVote?.moralFusion != null) {
142
+ const matrixInput = {
143
+ resonance: averageConfidence,
144
+ isotopicRatio: externalVote.moralFusion ?? 0.5,
145
+ };
146
+ if (externalVote.moralTension != null) {
147
+ matrixInput.moralTension = externalVote.moralTension;
148
+ }
149
+ if (externalVote.moralScore != null) {
150
+ matrixInput.moralScore = externalVote.moralScore;
151
+ }
152
+ if (externalVote.moralFusion != null) {
153
+ matrixInput.moralFusion = externalVote.moralFusion;
154
+ }
155
+ const matrix = applyDecisionMatrix(matrixInput);
156
+ moralOverride = matrix.moralOverride ?? 'none';
157
+ if (matrix.recommendation === 'REJECT') {
158
+ finalDecision = 'reject';
159
+ }
160
+ else if (matrix.recommendation === 'NEEDS_REVISION' && finalDecision === 'approve') {
161
+ finalDecision = 'needs_revision';
162
+ }
163
+ else if (matrix.recommendation === 'PASS' && finalDecision !== 'reject') {
164
+ finalDecision = 'approve';
165
+ }
166
+ averageConfidence = matrix.confidence;
167
+ }
168
+ let metamorphosisScore;
169
+ if (proposal.type === 'metamorphosis' || proposal.source === 'metamorphosis') {
170
+ const proposalType = proposal.type === 'compliance'
171
+ ? 'compliance'
172
+ : proposal.type === 'automate' || proposal.type === 'codify'
173
+ ? proposal.type
174
+ : 'fix';
175
+ const metamorphosisInput = {
176
+ averageConfidence,
177
+ proposalType,
178
+ resonanceScore: averageConfidence,
179
+ };
180
+ if (externalVote?.moralFusion != null) {
181
+ metamorphosisInput.historicalCoherence = externalVote.moralFusion;
182
+ }
183
+ metamorphosisScore = calculateMetamorphosisScore(metamorphosisInput);
184
+ if (metamorphosisScore < metamorphosisThreshold) {
185
+ finalDecision = 'needs_revision';
186
+ }
187
+ }
115
188
  return {
116
189
  proposalId: proposal.id,
117
- finalDecision: merged.finalDecision,
118
- averageConfidence: merged.averageConfidence,
190
+ finalDecision,
191
+ averageConfidence,
119
192
  votes,
120
193
  reasoningSummary: merged.reasoningSummary,
194
+ moralOverride,
195
+ ...(metamorphosisScore != null ? { metamorphosisScore } : {}),
121
196
  };
122
197
  });
123
198
  return { results };
@@ -50,8 +50,8 @@ export declare const AWSLoadBalancerConfigSchema: z.ZodObject<{
50
50
  name: z.ZodString;
51
51
  priority: z.ZodNumber;
52
52
  action: z.ZodEnum<{
53
- allow: "allow";
54
53
  deny: "deny";
54
+ allow: "allow";
55
55
  count: "count";
56
56
  }>;
57
57
  conditions: z.ZodArray<z.ZodObject<{
@@ -252,12 +252,22 @@ export async function afterToolHook(tool, args, _result, _error, _context) {
252
252
  const result = _result;
253
253
  if (result && typeof result === "object" && "title" in result && "description" in result) {
254
254
  const { handleGovernRequest } = await import("../nucleus/index.js");
255
+ const localMode = process.env.XRAY_LOCAL_MODE === "1" || process.env.XRAY_LOCAL_MODE === "true";
255
256
  const governanceResult = await handleGovernRequest({
256
- proposalId: `gate-${Date.now()}`,
257
- title: String(result.title || ""),
258
- description: String(result.description || ""),
259
- type: String(result.type || "fix"),
260
- content: result,
257
+ proposals: [
258
+ {
259
+ id: `gate-${Date.now()}`,
260
+ type: String(result.type || "fix"),
261
+ title: String(result.title || ""),
262
+ description: String(result.description || ""),
263
+ evidence: [],
264
+ source: "manual",
265
+ metadata: result,
266
+ },
267
+ ],
268
+ options: {
269
+ requireExternalDynamo: localMode ? false : undefined,
270
+ },
261
271
  });
262
272
  governanceTriggered = governanceResult?.overallDecision === 'approve';
263
273
  if (governanceTriggered) {
@@ -19,7 +19,10 @@ export interface GrokInstallOptions {
19
19
  dryRun?: boolean;
20
20
  }
21
21
  export declare function installForGrokCLI(options?: GrokInstallOptions): Promise<void>;
22
+ /** Grok TUI reads <project>/.grok/config.toml, not the package copy. */
23
+ export declare function writeProjectRepertoireMcp(projectRoot: string): string | null;
22
24
  declare const _default: {
23
25
  installForGrokCLI: typeof installForGrokCLI;
26
+ writeProjectRepertoireMcp: typeof writeProjectRepertoireMcp;
24
27
  };
25
28
  export default _default;
@@ -24,17 +24,8 @@ import { syncBuiltinSkills } from '../../cli/commands/skill-install.js';
24
24
  const __dirname = path.dirname(new URL(import.meta.url).pathname);
25
25
  const packageRoot = path.resolve(__dirname, '..', '..', '..');
26
26
  const requireCjs = createRequire(import.meta.url);
27
- const { resolveConsumerTargetDir } = requireCjs(path.join(packageRoot, 'scripts/node/install-bridges.cjs'));
28
- /** Canonical 7-server MCP surface — matches install-bridges.cjs and package .mcp.json */
29
- const XRAY_MCP_SERVERS = [
30
- { name: 'xray-governance', mcpCmd: 'governance', env: { XRAY_FORCE_MCP_GOVERNANCE: 'true' } },
31
- { name: 'xray-skills', mcpCmd: 'skills', env: {} },
32
- { name: 'xray-orchestrator', mcpCmd: 'orchestrator', env: {} },
33
- { name: 'xray-enforcer', mcpCmd: 'enforcer', env: {} },
34
- { name: 'xray-researcher', mcpCmd: 'researcher', env: {} },
35
- { name: 'xray-code-review', mcpCmd: 'code-review', env: {} },
36
- { name: 'xray-architect-tools', mcpCmd: 'architect-tools', env: {} },
37
- ];
27
+ const { resolveConsumerTargetDir, patchGrokHooks } = requireCjs(path.join(packageRoot, 'scripts/node/install-bridges.cjs'));
28
+ const { XRAY_MCP_SERVERS, resolveRepertoireMcp } = requireCjs(path.join(packageRoot, 'scripts/node/bridge-mcp-wiring.cjs'));
38
29
  function registerGrokMcpServers(targetDir) {
39
30
  try {
40
31
  execSync('which grok', { stdio: 'ignore' });
@@ -99,6 +90,17 @@ export async function installForGrokCLI(options = {}) {
99
90
  if (globalCopied > 0) {
100
91
  console.log(`\x1b[32m✓ Synced ${globalCopied} builtin skills to ~/.grok/skills/\x1b[0m`);
101
92
  }
93
+ pinGrokPluginToInstalledDist(targetPluginDir, packageRoot, targetDir);
94
+ wearGrokHookCommands(targetPluginDir, packageRoot, targetDir);
95
+ const projectPluginDir = path.join(targetDir, '.grok', 'plugins', '0xray');
96
+ if (projectPluginDir !== targetPluginDir) {
97
+ if (!fs.existsSync(projectPluginDir) || options.force) {
98
+ fs.cpSync(sourceDir, projectPluginDir, { recursive: true, force: true });
99
+ }
100
+ pinGrokPluginToInstalledDist(projectPluginDir, packageRoot, targetDir);
101
+ wearGrokHookCommands(projectPluginDir, packageRoot, targetDir);
102
+ }
103
+ writeProjectRepertoireMcp(targetDir);
102
104
  // Attempt auto-trust (best effort)
103
105
  try {
104
106
  execSync(`grok plugins trust "${targetPluginDir}"`, { stdio: 'ignore' });
@@ -115,11 +117,88 @@ export async function installForGrokCLI(options = {}) {
115
117
  console.log('Restart Grok or run `grok` to load the new hooks and MCP servers.');
116
118
  }
117
119
  catch (err) {
118
- frameworkLogger.log('grok-integration', 'install-error', 'error', { error: err.message });
119
- console.error('Failed to install Grok plugin:', err.message);
120
+ const message = err instanceof Error ? err.message : String(err);
121
+ frameworkLogger.log('grok-integration', 'install-error', 'error', { error: message });
122
+ console.error('Failed to install Grok plugin:', message);
120
123
  }
121
124
  frameworkLogger.log('grok-integration', 'install-complete', 'info', {});
122
125
  }
126
+ function wearGrokHookCommands(pluginDir, xrayRoot, targetDir) {
127
+ patchGrokHooks(pluginDir, xrayRoot, targetDir, (label, action, status, details) => {
128
+ const level = status === 'success' ||
129
+ status === 'error' ||
130
+ status === 'info' ||
131
+ status === 'debug' ||
132
+ status === 'warning'
133
+ ? status
134
+ : 'info';
135
+ frameworkLogger.log(label, action, level, details ?? {});
136
+ }, 'grok-install');
137
+ }
138
+ function pinGrokPluginToInstalledDist(pluginDir, xrayRoot, targetDir) {
139
+ const hookJs = path.join(xrayRoot, 'dist/integrations/grok/hooks/pre-tool-use.js');
140
+ const cliJs = path.join(xrayRoot, 'dist/cli/index.js');
141
+ if (!fs.existsSync(hookJs))
142
+ return;
143
+ const hooksPath = path.join(pluginDir, 'hooks', 'hooks.json');
144
+ if (fs.existsSync(hooksPath)) {
145
+ const text = fs.readFileSync(hooksPath, 'utf8');
146
+ fs.writeFileSync(hooksPath, text.split('${XRAY_AI_PATH:-node_modules/0xray}').join(xrayRoot));
147
+ }
148
+ const mcpPath = path.join(pluginDir, '.mcp.json');
149
+ if (fs.existsSync(mcpPath) && fs.existsSync(cliJs)) {
150
+ const mcp = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
151
+ for (const server of Object.values(mcp.mcpServers ?? {})) {
152
+ if (server.command === 'npx' && Array.isArray(server.args) && server.args.includes('mcp')) {
153
+ const mcpIdx = server.args.indexOf('mcp');
154
+ const mcpCmd = server.args[mcpIdx + 1] ?? 'governance';
155
+ server.command = 'node';
156
+ server.args = [cliJs, 'mcp', mcpCmd];
157
+ }
158
+ }
159
+ const repertoireMcp = resolveRepertoireMcp(targetDir);
160
+ if (mcp.mcpServers) {
161
+ if (repertoireMcp) {
162
+ mcp.mcpServers.repertoire = {
163
+ command: 'node',
164
+ args: [repertoireMcp],
165
+ };
166
+ }
167
+ else {
168
+ delete mcp.mcpServers.repertoire;
169
+ }
170
+ }
171
+ fs.writeFileSync(mcpPath, `${JSON.stringify(mcp, null, 2)}\n`);
172
+ }
173
+ }
174
+ /** Grok TUI reads <project>/.grok/config.toml, not the package copy. */
175
+ export function writeProjectRepertoireMcp(projectRoot) {
176
+ const repertoireMcp = resolveRepertoireMcp(projectRoot);
177
+ if (!repertoireMcp)
178
+ return null;
179
+ const grokDir = path.join(projectRoot, '.grok');
180
+ fs.mkdirSync(grokDir, { recursive: true });
181
+ const tomlPath = path.join(grokDir, 'config.toml');
182
+ const block = `[mcp_servers.repertoire]
183
+ command = "node"
184
+ args = [${JSON.stringify(repertoireMcp)}]
185
+ enabled = true
186
+ `;
187
+ let existing = '';
188
+ if (fs.existsSync(tomlPath)) {
189
+ existing = fs.readFileSync(tomlPath, 'utf8');
190
+ }
191
+ if (/\[mcp_servers\.repertoire\]/.test(existing)) {
192
+ existing = existing.replace(/\[mcp_servers\.repertoire\][\s\S]*?(?=\n\[|$)/, block.trim());
193
+ fs.writeFileSync(tomlPath, existing.endsWith('\n') ? existing : `${existing}\n`);
194
+ }
195
+ else {
196
+ const prefix = existing.trim() ? `${existing.trim()}\n\n` : '';
197
+ fs.writeFileSync(tomlPath, `${prefix}${block}`);
198
+ }
199
+ return tomlPath;
200
+ }
123
201
  export default {
124
202
  installForGrokCLI,
203
+ writeProjectRepertoireMcp,
125
204
  };
@@ -17,7 +17,16 @@ import {
17
17
  getActivePendingDelegations,
18
18
  validateSpawnMatchesTodo,
19
19
  updatePlanTodoStatusInPlace,
20
+ loadDelegationGateFeatures,
20
21
  } from '../../hooks/delegation-gate-runtime.mjs';
22
+ import { isConferPendingForSession } from '../../hooks/confer-hook-runtime.mjs';
23
+ import { getActiveUserAsideBoot } from '../../hooks/user-aside-hook-runtime.mjs';
24
+ import {
25
+ applyStationHeat,
26
+ buildRepertoireResume as stationRepertoireResume,
27
+ readExistingBoot,
28
+ writeStationMarkdown,
29
+ } from '../../hooks/station-hook-runtime.mjs';
21
30
 
22
31
  export {
23
32
  checkPendingDelegationGate,
@@ -105,9 +114,44 @@ export function resolveCodexPath(root = workspaceRoot()) {
105
114
  return null;
106
115
  }
107
116
 
117
+ export function resolveSiblingWorkspaceRoots(root = workspaceRoot()) {
118
+ const featuresPath = resolveFeaturesPath(root);
119
+ if (!featuresPath) return [];
120
+ try {
121
+ const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
122
+ const siblings = data.multi_agent_orchestration?.sibling_repos ?? [];
123
+ if (!Array.isArray(siblings)) return [];
124
+ const resolved = [];
125
+ for (const entry of siblings) {
126
+ const rel = typeof entry === 'string' ? entry : entry?.path;
127
+ if (!rel || typeof rel !== 'string') continue;
128
+ const abs = path.isAbsolute(rel) ? rel : path.resolve(root, rel);
129
+ if (!fs.existsSync(abs)) continue;
130
+ resolved.push({
131
+ path: abs,
132
+ label: typeof entry === 'object' && entry.label ? entry.label : path.basename(abs),
133
+ });
134
+ }
135
+ return resolved;
136
+ } catch {
137
+ return [];
138
+ }
139
+ }
140
+
141
+ export function buildRepertoireResume(root = workspaceRoot()) {
142
+ return stationRepertoireResume(root);
143
+ }
144
+
108
145
  export function loadFeatures(root = workspaceRoot()) {
109
146
  const featuresPath = resolveFeaturesPath(root);
110
- if (!featuresPath) return { lead_dev_mode: true, no_new_surface: true, per_suite_triage: true };
147
+ if (!featuresPath) {
148
+ return {
149
+ lead_dev_mode: true,
150
+ no_new_surface: true,
151
+ per_suite_triage: true,
152
+ grok_postprocessor_light: false,
153
+ };
154
+ }
111
155
  try {
112
156
  const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
113
157
  const orch = data.multi_agent_orchestration ?? {};
@@ -116,6 +160,8 @@ export function loadFeatures(root = workspaceRoot()) {
116
160
  no_new_surface: orch.no_new_surface !== false,
117
161
  per_suite_test_triage: orch.per_suite_test_triage !== false,
118
162
  auto_chain_delegations: orch.auto_chain_delegations !== false,
163
+ grok_postprocessor_light: data.grok_postprocessor_light === true,
164
+ sibling_repos: resolveSiblingWorkspaceRoots(root),
119
165
  };
120
166
  } catch {
121
167
  return {
@@ -123,6 +169,8 @@ export function loadFeatures(root = workspaceRoot()) {
123
169
  no_new_surface: true,
124
170
  per_suite_triage: true,
125
171
  auto_chain_delegations: true,
172
+ grok_postprocessor_light: false,
173
+ sibling_repos: [],
126
174
  };
127
175
  }
128
176
  }
@@ -160,9 +208,11 @@ export function extractToolContext(event) {
160
208
  return { toolName, toolInput, paths, content, cmd: String(toolInput.command || '') };
161
209
  }
162
210
 
163
- export function checkCodexPatterns(content) {
211
+ export function checkCodexPatterns(content, options = {}) {
164
212
  if (!content) return null;
165
- for (const { message, pattern } of CODEX_BLOCK_PATTERNS) {
213
+ const only = Array.isArray(options.terms) ? options.terms : null;
214
+ for (const { message, pattern, terms } of CODEX_BLOCK_PATTERNS) {
215
+ if (only && !terms.some((n) => only.includes(n))) continue;
166
216
  if (pattern.test(content)) return message;
167
217
  pattern.lastIndex = 0;
168
218
  }
@@ -215,23 +265,51 @@ export function sessionBootPath(root = workspaceRoot()) {
215
265
  return path.join(root, '.xray', 'state', 'session-boot.json');
216
266
  }
217
267
 
268
+ export function loadConferPending(root = workspaceRoot(), sessionId = null) {
269
+ try {
270
+ return isConferPendingForSession(root, sessionId);
271
+ } catch {
272
+ return false;
273
+ }
274
+ }
275
+
218
276
  export function buildSessionBootPayload(root, source = '0xray/grok-session-start', extra = {}) {
219
- const features = loadFeatures();
277
+ const features = loadFeatures(root);
220
278
  const blockingTerms = loadBlockingCodexTerms();
279
+ const siblingRoots = features.sibling_repos ?? resolveSiblingWorkspaceRoots(root);
280
+ const sessionId =
281
+ extra.sessionId || process.env.GROK_SESSION_ID || process.env.GROK_SESSION || null;
282
+ const conferPending = loadConferPending(root, sessionId);
283
+ const userAsideBoot = getActiveUserAsideBoot(root, sessionId);
284
+ const gateFeatures = loadDelegationGateFeatures(root, 'grok');
285
+ const frontier = gateFeatures.ceremony === 'lite';
286
+ const existing = readExistingBoot(root);
287
+ const heat = applyStationHeat(root, extra.host || 'grok', extra, existing);
221
288
  return {
222
289
  hook: source,
223
290
  lead_dev_mode: features.lead_dev_mode,
224
291
  no_new_surface: features.no_new_surface,
292
+ host: extra.host || 'grok',
293
+ suit_profile: gateFeatures.suit_profile ?? 'guided',
294
+ ceremony: gateFeatures.ceremony ?? 'full',
295
+ spawn_plan_mode: gateFeatures.spawn_plan_mode ?? 'deny',
225
296
  codexBlockingTermCount: blockingTerms.length,
226
297
  codexTerms: [59, 67, 68, 69],
227
298
  rules: features.lead_dev_mode ? LEAD_DEV_RULES : [],
228
- mcpIntake: 'xray-orchestrator → analyze-complexity (required before spawn_subagent)',
229
- enforcement: 'PreToolUse hook — codex patterns + surface area + spawn gate',
299
+ mcpIntake: frontier
300
+ ? 'xray-orchestrator analyze-complexity optional on frontier (spawn warns, does not deny)'
301
+ : 'xray-orchestrator → analyze-complexity (required before spawn_subagent)',
302
+ enforcement: 'PreToolUse hook — Codex constitution always on; ceremony scales by suit_temperament',
230
303
  workspaceRoot: root,
231
- sessionId: process.env.GROK_SESSION_ID || null,
304
+ ...heat,
305
+ ...(siblingRoots.length > 0 ? { siblingWorkspaceRoots: siblingRoots } : {}),
306
+ ...(conferPending ? { conferPending: true, conferTrigger: 'analyze-complexity at synthesis checkpoint' } : {}),
307
+ ...(userAsideBoot ?? {}),
308
+ sessionId,
232
309
  timestamp: new Date().toISOString(),
233
310
  source,
234
311
  ...extra,
312
+ ...heat,
235
313
  };
236
314
  }
237
315
 
@@ -240,19 +318,31 @@ export function writeSessionBoot(root, payload) {
240
318
  const stateDir = path.join(root, '.xray', 'state');
241
319
  fs.mkdirSync(stateDir, { recursive: true });
242
320
  fs.writeFileSync(sessionBootPath(root), JSON.stringify(payload, null, 2));
321
+ writeStationMarkdown(root, payload);
243
322
  return sessionBootPath(root);
244
323
  } catch {
245
324
  return null;
246
325
  }
247
326
  }
248
327
 
249
- /** Boot on SessionStart, UserPromptSubmit, or first PreToolUse if missing. */
328
+ export function sessionBootNeedsRefresh(existing, root) {
329
+ if (!existing || typeof existing !== 'object') return true;
330
+ if (existing.lead_dev_mode === undefined) return true;
331
+ if (existing.host !== 'grok') return true;
332
+ if (!existing.suit_profile) return true;
333
+ if (existing.workspaceRoot && existing.workspaceRoot !== root) return true;
334
+ if (!existing.repertoireResume) return true;
335
+ if (!existing.stationLine) return true;
336
+ return false;
337
+ }
338
+
339
+ /** Boot on SessionStart, UserPromptSubmit, or first PreToolUse if missing/stale. */
250
340
  export function ensureSessionBoot(root = workspaceRoot(), source = '0xray/grok-boot') {
251
341
  const bootPath = sessionBootPath(root);
252
342
  if (fs.existsSync(bootPath)) {
253
343
  try {
254
344
  const existing = JSON.parse(fs.readFileSync(bootPath, 'utf8'));
255
- if (existing.lead_dev_mode !== undefined) return bootPath;
345
+ if (!sessionBootNeedsRefresh(existing, root)) return bootPath;
256
346
  } catch {
257
347
  /* rewrite corrupt boot */
258
348
  }
@@ -8,6 +8,8 @@ import {
8
8
  extractToolContext,
9
9
  isOrchestrateToolEvent,
10
10
  isSubagentTool,
11
+ isWriteTool,
12
+ loadFeatures,
11
13
  readStdinJson,
12
14
  resolveSessionId,
13
15
  workspaceRoot,
@@ -17,6 +19,7 @@ import {
17
19
  evaluatePostToolSpawn,
18
20
  isOrchestrateToolEvent as gateIsOrchestrateToolEvent,
19
21
  } from '../../hooks/delegation-gate-runtime.mjs';
22
+ import { recordRoutingOutcome, runGrokPostprocessorLight } from '../../hooks/pipeline-hook-runtime.mjs';
20
23
 
21
24
  function extractToolOutput(event) {
22
25
  return (
@@ -29,40 +32,77 @@ function extractToolOutput(event) {
29
32
  );
30
33
  }
31
34
 
32
- async function main() {
33
- const root = workspaceRoot();
34
- try {
35
- const event = await readStdinJson();
36
- const eventRoot = event.workspaceRoot || event.cwd || root;
37
- const sessionId = resolveSessionId(event);
38
- const { toolName, toolInput } = extractToolContext(event);
35
+ export function handlePostToolUse(event, root = workspaceRoot()) {
36
+ const eventRoot = event.workspaceRoot || event.cwd || root;
37
+ const sessionId = resolveSessionId(event);
38
+ const { toolName, toolInput } = extractToolContext(event);
39
39
 
40
- if (isOrchestrateToolEvent(toolName, toolInput) || gateIsOrchestrateToolEvent(toolName, toolInput)) {
41
- appendHookActivity(eventRoot, 'grok-post-tool-use', 'auto-chain-pending', 'info', {
40
+ if (isOrchestrateToolEvent(toolName, toolInput) || gateIsOrchestrateToolEvent(toolName, toolInput)) {
41
+ appendHookActivity(eventRoot, 'grok-post-tool-use', 'auto-chain-pending', 'info', {
42
+ tool: toolName,
43
+ sessionId,
44
+ note: 'orchestrate-task completed — pending-delegations.json written by task-handler',
45
+ });
46
+ try {
47
+ recordRoutingOutcome(eventRoot, {
42
48
  tool: toolName,
49
+ agent: toolInput?.subagent_type ?? toolInput?.agent ?? 'orchestrate-task',
50
+ planTodoId: toolInput?.planTodoId ?? null,
43
51
  sessionId,
44
- note: 'orchestrate-task completed — pending-delegations.json written by task-handler',
45
52
  });
53
+ } catch {
54
+ /* non-blocking */
46
55
  }
56
+ }
57
+
58
+ const features = loadFeatures(eventRoot);
59
+ if (features.grok_postprocessor_light === true && isWriteTool(toolName)) {
60
+ try {
61
+ const paths = [
62
+ toolInput?.path,
63
+ toolInput?.filePath,
64
+ toolInput?.file_path,
65
+ ].filter(Boolean);
66
+ runGrokPostprocessorLight(eventRoot, { tool: toolName, paths });
67
+ } catch {
68
+ /* non-blocking */
69
+ }
70
+ }
71
+
72
+ if (isSubagentTool(toolName)) {
73
+ const spawnResult = evaluatePostToolSpawn(toolName, toolInput, eventRoot, {
74
+ toolOutput: extractToolOutput(event),
75
+ sessionId,
76
+ });
47
77
 
48
- if (isSubagentTool(toolName)) {
49
- const spawnResult = evaluatePostToolSpawn(toolName, toolInput, eventRoot, {
50
- toolOutput: extractToolOutput(event),
78
+ if (spawnResult.satisfied.length > 0 || spawnResult.expectedTodoId) {
79
+ appendHookActivity(eventRoot, 'grok-post-tool-use', 'auto-chain-cleared', 'success', {
80
+ tool: toolName,
81
+ satisfied: spawnResult.satisfied.map((d) => d.id),
82
+ clearedAll: spawnResult.clearedAll,
83
+ planTodoId: spawnResult.expectedTodoId ?? null,
84
+ receiptRecorded: spawnResult.receiptRecorded ?? false,
85
+ todoCompleted: spawnResult.todoCompleted ?? false,
86
+ });
87
+ }
88
+ try {
89
+ recordRoutingOutcome(eventRoot, {
90
+ tool: toolName,
91
+ agent: toolInput?.subagent_type ?? toolInput?.agent ?? 'subagent',
92
+ planTodoId: toolInput?.planTodoId ?? spawnResult.expectedTodoId ?? null,
51
93
  sessionId,
52
94
  });
53
-
54
- if (spawnResult.satisfied.length > 0 || spawnResult.expectedTodoId) {
55
- appendHookActivity(eventRoot, 'grok-post-tool-use', 'auto-chain-cleared', 'success', {
56
- tool: toolName,
57
- satisfied: spawnResult.satisfied.map((d) => d.id),
58
- clearedAll: spawnResult.clearedAll,
59
- planTodoId: spawnResult.expectedTodoId ?? null,
60
- receiptRecorded: spawnResult.receiptRecorded ?? false,
61
- todoCompleted: spawnResult.todoCompleted ?? false,
62
- });
63
- }
95
+ } catch {
96
+ /* non-blocking */
64
97
  }
98
+ }
99
+ }
65
100
 
101
+ async function main() {
102
+ const root = workspaceRoot();
103
+ try {
104
+ const event = await readStdinJson();
105
+ handlePostToolUse(event, root);
66
106
  process.exit(0);
67
107
  } catch (err) {
68
108
  appendHookActivity(root, 'grok-post-tool-use', 'hook-error', 'error', {
@@ -72,4 +112,7 @@ async function main() {
72
112
  }
73
113
  }
74
114
 
75
- main();
115
+ const launchedAsCli = Boolean(process.argv[1] && process.argv[1].endsWith('post-tool-use.js'));
116
+ if (launchedAsCli) {
117
+ main();
118
+ }