0xray 4.0.41 → 4.1.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 (148) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.opencode/skills/orchestrator/SKILL.md +12 -0
  3. package/.plugin/plugin.json +1 -1
  4. package/.xray/features.json +5 -5
  5. package/.xray/features.schema.json +1 -1
  6. package/AGENTS-consumer.md +10 -6
  7. package/AGENTS.md +19 -8
  8. package/LICENSE +1 -1
  9. package/README.md +65 -19
  10. package/SKILLS.md +5 -1
  11. package/dist/AGENTS.md +19 -8
  12. package/dist/CHANGELOG.md +85 -0
  13. package/dist/LICENSE +1 -1
  14. package/dist/README.md +65 -19
  15. package/dist/SKILLS.md +5 -1
  16. package/dist/cli/commands/openclaw-install.js +1 -1
  17. package/dist/cli/commands/plugin-commands.d.ts +4 -0
  18. package/dist/cli/commands/plugin-commands.js +84 -41
  19. package/dist/cli/index.js +12 -0
  20. package/dist/core/config-loader.js +4 -4
  21. package/dist/core/features-config.d.ts +1 -1
  22. package/dist/core/features-config.js +3 -3
  23. package/dist/enforcement/index.d.ts +4 -0
  24. package/dist/enforcement/index.js +4 -0
  25. package/dist/enforcement/validators/code-quality-validators.js +6 -4
  26. package/dist/governance/llm-governance-provider.d.ts +7 -1
  27. package/dist/governance/llm-governance-provider.js +72 -14
  28. package/dist/governance/local-confer.d.ts +4 -1
  29. package/dist/governance/local-confer.js +22 -1
  30. package/dist/integrations/cursor/hooks/hooks.json +6 -3
  31. package/dist/integrations/cursor/hooks/pre-tool-use.js +100 -37
  32. package/dist/integrations/cursor/hooks/xray-cloud-hook.sh +9 -1
  33. package/dist/integrations/grok/grok-cli.js +10 -37
  34. package/dist/integrations/grok/hooks/grok-hook-utils.js +8 -1
  35. package/dist/integrations/grok/hooks/pre-tool-use.js +2 -58
  36. package/dist/integrations/hermes-agent/bridge.mjs +14 -1
  37. package/dist/integrations/hooks/confer-hook-runtime.mjs +23 -1
  38. package/dist/integrations/hooks/delegation-gate-runtime.mjs +3 -2
  39. package/dist/integrations/hooks/goggles-mcp.mjs +35 -17
  40. package/dist/integrations/hooks/goggles-pipeline.mjs +46 -1
  41. package/dist/integrations/hooks/goggles-planes.json +11 -7
  42. package/dist/integrations/hooks/pre-tool-speak.mjs +76 -0
  43. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +21 -4
  44. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +17 -2
  45. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  46. package/dist/mcps/architect-tools.server.d.ts +6 -0
  47. package/dist/mcps/architect-tools.server.js +19 -2
  48. package/dist/mcps/config/server-config-registry.js +8 -6
  49. package/dist/mcps/governance.server.d.ts +1 -0
  50. package/dist/mcps/governance.server.js +46 -0
  51. package/dist/mcps/knowledge-skills/code-review.server.js +7 -0
  52. package/dist/mcps/knowledge-skills/security-audit.server.js +5 -3
  53. package/dist/mcps/mcp-client.js +1 -1
  54. package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
  55. package/dist/mcps/researcher.server.js +12 -3
  56. package/dist/monitoring/{nudge-watchdog.d.ts → nudge-monitor.d.ts} +14 -5
  57. package/dist/monitoring/{nudge-watchdog.js → nudge-monitor.js} +42 -17
  58. package/dist/nucleus/autonomy-kernel.d.ts +2 -0
  59. package/dist/nucleus/autonomy-kernel.js +17 -13
  60. package/dist/nucleus/confer-host.d.ts +35 -0
  61. package/dist/nucleus/confer-host.js +55 -0
  62. package/dist/nucleus/confer.d.ts +19 -3
  63. package/dist/nucleus/confer.js +111 -55
  64. package/dist/nucleus/lead-dev-plan-persistence.js +4 -2
  65. package/dist/nucleus/suit-temperament.d.ts +1 -1
  66. package/dist/nucleus/suit-temperament.js +2 -2
  67. package/dist/nucleus/synthesis-consult-receipt.d.ts +7 -1
  68. package/dist/nucleus/synthesis-consult-receipt.js +86 -35
  69. package/dist/plugin/xray-codex-injection.js +18 -0
  70. package/dist/processors/implementations/nudge-processor.d.ts +1 -1
  71. package/dist/processors/implementations/nudge-processor.js +4 -4
  72. package/dist/reporting/framework-reporting-system.js +2 -5
  73. package/dist/reporting/log-parser.d.ts +2 -0
  74. package/dist/reporting/log-parser.js +7 -6
  75. package/dist/skills/orchestrator/SKILL.md +12 -0
  76. package/docs-site/docs/plates/activity-log.md +1 -1
  77. package/docs-site/docs/plates/bifrost.md +48 -0
  78. package/docs-site/docs/plates/boot.md +2 -2
  79. package/docs-site/docs/plates/centrifuge.md +1 -1
  80. package/docs-site/docs/plates/cursor-compact.md +1 -1
  81. package/docs-site/docs/plates/glossary.md +7 -3
  82. package/docs-site/docs/plates/goggles.md +13 -2
  83. package/docs-site/docs/plates/governance.md +1 -1
  84. package/docs-site/docs/plates/grok-compact.md +1 -1
  85. package/docs-site/docs/plates/grokbot.md +3 -1
  86. package/docs-site/docs/plates/host-pack.md +1 -1
  87. package/docs-site/docs/plates/house.md +1 -1
  88. package/docs-site/docs/plates/index.md +4 -2
  89. package/docs-site/docs/plates/inference-files.md +1 -1
  90. package/docs-site/docs/plates/kept-line.md +1 -1
  91. package/docs-site/docs/plates/kits.md +1 -1
  92. package/docs-site/docs/plates/lens-gate.md +3 -1
  93. package/docs-site/docs/plates/lens-page.md +1 -1
  94. package/docs-site/docs/plates/memory-recall.md +1 -1
  95. package/docs-site/docs/plates/notes-page.md +2 -2
  96. package/docs-site/docs/plates/orchestration.md +1 -1
  97. package/docs-site/docs/plates/package-face.md +1 -1
  98. package/docs-site/docs/plates/payload-heat.md +1 -1
  99. package/docs-site/docs/plates/pickup-stamp.md +1 -1
  100. package/docs-site/docs/plates/pre-tool.md +1 -1
  101. package/docs-site/docs/plates/processor.md +1 -1
  102. package/docs-site/docs/plates/record-map.md +1 -1
  103. package/docs-site/docs/plates/reflection-page.md +1 -1
  104. package/docs-site/docs/plates/reporting.md +1 -1
  105. package/docs-site/docs/plates/review.md +1 -1
  106. package/docs-site/docs/plates/routing.md +1 -1
  107. package/docs-site/docs/plates/session-capture.md +1 -1
  108. package/docs-site/docs/plates/site-manual.md +1 -1
  109. package/docs-site/docs/plates/stamp-plate.md +1 -1
  110. package/docs-site/docs/plates/station-card.md +1 -1
  111. package/docs-site/docs/plates/station-heat.md +1 -1
  112. package/docs-site/docs/plates/suit-organs.md +1 -1
  113. package/docs-site/docs/plates/suit-settings.md +1 -1
  114. package/docs-site/docs/plates/suit-wear.md +1 -1
  115. package/docs-site/docs/plates/suit.md +3 -3
  116. package/docs-site/docs/plates/trail-state.md +1 -1
  117. package/docs-site/docs/plates/work-fresh.md +1 -1
  118. package/docs-site/docs/plates/write-home.md +1 -1
  119. package/llms.txt +16 -6
  120. package/package.json +4 -4
  121. package/scripts/foundry/README.md +2 -2
  122. package/scripts/foundry/release-gate.mjs +111 -15
  123. package/scripts/foundry/release.mjs +8 -0
  124. package/scripts/mjs/verify-confer-core.mjs +153 -29
  125. package/scripts/mjs/verify-pipeline-facets.mjs +1 -1
  126. package/scripts/mjs/verify-user-aside-core.mjs +3 -4
  127. package/scripts/node/bridge-mcp-wiring.cjs +236 -5
  128. package/scripts/node/confer-switch.cjs +66 -0
  129. package/scripts/node/install-bridges.cjs +44 -53
  130. package/scripts/node/pack-tmp-suit-proof.mjs +14 -6
  131. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +17 -2
  132. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  133. package/src/mcps/architect-tools.server.ts +26 -2
  134. package/src/mcps/config/__tests__/server-config-registry.test.ts +15 -6
  135. package/src/mcps/config/server-config-registry.ts +9 -6
  136. package/src/mcps/governance.server.ts +49 -0
  137. package/src/mcps/knowledge-skills/code-review.server.ts +8 -0
  138. package/src/mcps/knowledge-skills/security-audit.server.ts +11 -12
  139. package/src/mcps/mcp-client.ts +1 -1
  140. package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
  141. package/src/mcps/researcher.server.ts +19 -10
  142. package/src/skills/orchestrator/SKILL.md +12 -0
  143. package/vendor/@0xray/repertoire/README.md +1 -1
  144. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +5 -51
  145. package/xray/agents_template.md +2 -2
  146. package/xray/features.json +5 -5
  147. package/xray/features.schema.json +1 -1
  148. package/docs-site/docs/plates/live-mesh.md +0 -41
package/dist/README.md CHANGED
@@ -1,37 +1,45 @@
1
- # xray — exo for coding agents
1
+ # xray — a suit for coding agents
2
2
 
3
- **4.0** — a suit that survives the context window
3
+ ![0xRay — A Power Suit for AI Agents. survive compaction. build foundries. monitor the swarm.](docs-site/static/img/readme-hero.jpg)
4
4
 
5
- ![0xRay v4 exo — CONSTITUTION ON](docs-site/static/img/exo-skeleton-v4.jpg)
5
+ **4.1** — a suit that survives the context window
6
6
 
7
- Not a catalog of agents. The product is the **skeleton you wear**.
7
+ Bifrost is the live board. Goggles reads one named plane.
8
+
9
+ 0xRay is a three-subsystem autonomous engine. The suit is the wear. Inference remembers. External Governance judges. The Autonomous Engine runs the work. The host you already run wears it: Grok, OpenCode, Hermes, OpenClaw, or Cursor. Memory, judgement, and payments come on with the suit. When the window closes, the next session wakes with the job, the page, and the laws.
10
+
11
+ Bifrost turns each pull request, issue, and comment into one card and a line in the right seat's inbox, then wakes that seat. Confer still completes only on a real yes. A foundry is built from mills and hangars. The mill builds and ships. The hangar is the shop.
12
+
13
+ The product is the **skeleton you wear**.
8
14
 
9
15
  ## Your AI Power Suit
10
16
 
11
17
  **Your AI Power ⚡️ Suit 🦾** — an exoskeleton that blocks AI slop from becoming real damage.
12
18
 
13
- - **Bone** — three-subsystem OS: Inference · External Governance (Dynamo / Codex) · Autonomous Engine (thinDispatch)
19
+ - **Bone** — three-subsystem engine: Inference · External Governance (Dynamo / Codex) · Autonomous Engine (thinDispatch)
14
20
  - **Always on** — Codex 11 / 29 / 69, destructive shell, no new MCP/skill/handler surface
15
21
  - **Temperament** — `frontier` | `guided` | `strict` | `auto` — how loud the engine is, not whether governance exists
16
22
  - **Wear** — Grok, OpenCode, Hermes, OpenClaw, plus Cursor project hooks. One SSOT gate
17
- - **Muscle** — **Repertoire is preferred.** Vendored 0.2.9 ships **on**. Dest is named laws, not hangar `repo-*` or git slugs. Station is the compact-survival ticket — not a substitute for Repertoire.
23
+ - **Muscle** — **Repertoire is preferred.** Vendored 0.2.9 ships **on**. Dest is named laws, not hangar `repo-*` or git slugs. Station is the wake ticket — not a substitute for Repertoire.
18
24
 
19
25
  ```bash
20
26
  npm install 0xray
27
+ npx 0xray wear
21
28
  ```
22
29
 
23
- Consumer `npm install 0xray` auto-wires four chat bridges, Cursor project hooks, and seven MCP servers. **Repertoire is preferred** — vendored **0.2.9** ships on: factory + stack laws, in-process routing, extra host MCP `repertoire`. Not an eighth `xray-*` server. Dest is a judgment index. Explicit opt-out only: `"enabled": false, "provider": "repertoire"`. Do not pin 0.1.8.
30
+ `npm install 0xray` links vendored `@0xray/repertoire` and prints `npx 0xray wear`. It does not write `.mcp.json` or the chat bridges. Wear writes the consumer suit: managed `AGENTS.md`, `.xray` config, seven MCP servers, and the OpenCode, Grok, Hermes, and OpenClaw bridges (`install-bridges.cjs`). Inside a git checkout, wear also fastens Cursor project hooks. **Repertoire is preferred** — vendored **0.2.9** ships on: factory + stack laws, in-process routing, extra host MCP `repertoire`. Not an eighth `xray-*` server. Dest is a judgment index. Explicit opt-out only: `"enabled": false, "provider": "repertoire"`. Do not pin 0.1.8.
24
31
 
25
32
  ## Quick Start
26
33
 
27
34
  ```bash
28
- npm install 0xray # postinstall: mill+inspect plant + 4 bridges + 7 MCPs + AGENTS.md + .mcp.json
29
- npx 0xray status # verify install
30
- npx 0xray setup # optional: symlinks, hook extras
35
+ npm install 0xray
36
+ npx 0xray wear
37
+ npx 0xray status
38
+ npx 0xray setup --git-hooks # optional: git hooks only
31
39
  npx @0xray/foundry inspect --skip-live # mill receipt (chat is not a receipt)
32
40
  ```
33
41
 
34
- Manual per-platform install (idempotent, same result as postinstall):
42
+ Manual per-platform install (idempotent, same bridges wear writes):
35
43
 
36
44
  ```bash
37
45
  npx 0xray opencode install
@@ -51,12 +59,50 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
51
59
 
52
60
  | Version | Highlights |
53
61
  |---------|------------|
54
- | **Unreleased** | Fasten is **foundry-plant/0**: builtin plant is mill+inspect only. `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill declares (looker / vibe / mixer live in the mill). Nested mill `@0xray/foundry@0.1.12`. CLI shims `foundry blip\|sound` remain (0.1.11+). Shop plant first-class (`shop-extract`, `shop-witness`, `shop-pin` + `foundry.json` `shopPlant`). |
62
+ | **4.1.0** | Bifrost is the collector board. A cloud agent is a board row. A stuck card's L2 nudge goes to the CoS role. `live-inbox` requires `LIVE_URL`, `SEAT_KEYS_DIR`, and `LIVE_CURSORS`. Goggles reads one named plane. |
63
+ | **4.0.43** | Confer completes only on a real PASS. Cursor gate events fail closed, except EACCES when a cloud wrapper cannot write dest. Gate C cannot be skipped. |
64
+ | **4.0.42** | Confer votes. Burst runs the swarm. |
65
+ | **4.0.41** | Wake cascade stays on the station module. A fresh wear fastens mill and inspect. Goggles records the worn package path. The live page plays the mesh in the browser. |
66
+ | **4.0.40** | Live mesh runs the full loop: feed watch, render, direction, deploy, health, and the ledger both ways. |
67
+ | **4.0.39** | The seven stack laws stay worn. Repertoire in the suit is 0.2.9. |
68
+ | **4.0.38** | Codex term 70 requires the current source and the worn npm package. Dynamo's recommendation is honored. |
69
+ | **4.0.37** | A naked checkout wakes with repertoire on. |
70
+ | **4.0.36** | Goggles holds the stop through search, read, and a dist wipe. |
71
+ | **4.0.35** | Wear rewrites a checkout `dist/cli` launch to `node_modules/0xray`. |
72
+ | **4.0.34** | The lens stays on the worn build when the source is not packed. |
73
+ | **4.0.33** | Version bump. |
74
+ | **4.0.32** | `0xray look` opens one plane. Digest and triage return the card. Calling and confer stay off. |
75
+ | **4.0.31** | Goggles looks at one pipeline. Kind 0 stays quiet when the worn list matches the map. Calling and confer stay off. |
55
76
  | **4.0.30** | Fixes 4.0.29: `npx 0xray wear` and `setup` work outside a git checkout again. They write the 4.0.28 project setup, skip only Cursor hooks, and print one warning. Inside a git checkout they restore the 4.0.28 project setup, and `wear` adds Cursor hooks plus the unwear snapshot. Companion `@0xray/grok-bot` 0.1.8. Released 2026-09-28. |
56
77
  | **4.0.29** | `npm install` only creates the relative `node_modules/@0xray/repertoire` link to the vendored repertoire and prints `npx 0xray wear`. Run `npx 0xray wear` after install. Wear writes only the consumer project's `.cursor/hooks.json` (installed dist hooks, beside user entries); `npx 0xray unwear` restores the snapshot. Companion `@0xray/grok-bot` 0.1.8. Released 2026-09-28. |
57
78
  | **4.0.28** | MCP servers start through `scripts/node/mcp-launch.cjs` and only see PATH, HOME, XRAY_ROOT, plus names on their keep list. Tokens such as NPM_TOKEN are not inherited. User env values stay out of argv. Postinstall keeps user edits to AGENTS.md, .mcp.json, and opencode.json. `house init` does not copy EXAMPLE.md; doctor FAILs while `house/EXAMPLE.md` exists. Inspect adds a machinePlugin check. The Cursor hook defaults XRAY_ROOT to the suit. Companion `@0xray/grok-bot` 0.1.7. Released 2026-09-27. |
58
79
  | **4.0.27** | Host compact proof needs a generation id and a numeric usage percent, and the same preCompact receipt is mirrored to nested consumer wear roots. Memory scoring: [docs/memory-scoring.md](docs/memory-scoring.md) (orchestrator links it). Companion `@0xray/grok-bot` 0.1.6: general page vs `house/` overlay, `templates/house/`, `grok-bot house init`, doctor `GROK_BOT_HOUSE` or walk-up. Released 2026-09-26. |
80
+ | **4.0.26** | A lesson is stored when the task matches no signal. A Cursor compact cut is proved with a session log and a per-session receipt. |
81
+ | **4.0.25** | Plate schematics ship in the package. A worn suit resolves them from `dist/integrations/hooks/plates.cjs`. |
82
+ | **4.0.24** | Compaction and a host change are the same cut. The station card is the handoff. Repertoire in that suit is 0.2.8. |
83
+ | **4.0.23** | The Dynamo client starts when external governance is required. Proposals score locally when that governance is off. |
84
+ | **4.0.22** | The stamper strips present-tense patch pins from shipped prose. The cursor gate follows the suit mill. |
85
+ | **4.0.21** | Dest is named laws, not hangar slugs. Cleanup heat grows the project copy when Repertoire is on. |
86
+ | **4.0.20** | Heat grows the project copy. Session boot rewrites when HEAD moves. |
87
+ | **4.0.19** | Vendored Repertoire 0.2.5. Wear replaces older leftovers. |
88
+ | **4.0.18** | Wake heat joins bookmark, index, and mind. |
89
+ | **4.0.17** | Cursor heat writes session files when HEAD moves. |
90
+ | **4.0.16** | `npx 0xray validate` is a wear check. |
91
+ | **4.0.15** | Fasten is **foundry-plant/0**: builtin plant is mill+inspect only. `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill declares (looker / vibe / mixer live in the mill). Nested mill `@0xray/foundry@0.1.12`. CLI shims `foundry blip\|sound` remain (0.1.11+). Shop plant first-class (`shop-extract`, `shop-witness`, `shop-pin` + `foundry.json` `shopPlant`). |
92
+ | **4.0.14** | Factory-blip beds stamp the hook cell and keep six tellable bodies. |
93
+ | **4.0.13** | Station PreCompact merge keeps custom STATION.md keys. |
94
+ | **4.0.12** | Project-scoped Grok plugin dest so multi-seat wear does not clobber the machine plugin. |
95
+ | **4.0.11** | The npm pack includes `.grok-plugin/` so the Grok marketplace manifest ships. |
96
+ | **4.0.10** | Version bump. |
59
97
  | **4.0.9** | Mill target: postinstall does not mill npm global prefix or `_npx` as a consumer. Isolated HOME skips machine `~/.grok`. |
98
+ | **4.0.8** | Confer abstains locally instead of rubber-stamping a proposal. |
99
+ | **4.0.7** | Nested mill 0.1.9. Inventory writes `dna`. The mill does not mint on chain. |
100
+ | **4.0.6** | Nested mill 0.1.8. Isolated HOME skips machine Hermes and OpenClaw. |
101
+ | **4.0.5** | Inspect is a mill command. A costume dump fails mint. Mill plant stays mill + inspect. |
102
+ | **4.0.4** | Mill fastens an inspect suit. Costume is opt-in with `foundry.json` `"costume": true`. |
103
+ | **4.0.3** | Their plant is the suit. The mill plant is the factory discipline on the hanger. |
104
+ | **4.0.2** | The nested mill ships in the 0xray tarball. `@0xray/foundry` stays the standalone mill CLI. |
105
+ | **4.0.1** | CLI `--version` no longer dumps a Commander stack. The release gate smokes the organ and does not run another publish from a lifecycle script. |
60
106
  | **4.0.0** | Exo + temperament. Constitution always on; ceremony by host. Repertoire organ on (vendored 0.2). Station card survives compact/host-swap. **On npm.** [vision](docs-site/docs/architecture/v4-vision.md) · [now](docs-site/docs/architecture/v4-now.md) |
61
107
  | **3.4.1** | Unified `install-bridges.cjs` on postinstall — OpenCode, Grok, Hermes, OpenClaw in one pass. All 7 MCPs servers via `npx -y 0xray mcp <cmd>` (no `dist/` paths). Canonical `release.mjs` pipeline. |
62
108
  | **3.3.1** | Orchestrator confidence gate wired into execution planning. |
@@ -102,9 +148,9 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
102
148
  - **External Governance** — Dynamo Solar SSOT; CodexPolicyService; weighted PHI/TAU deliberation.
103
149
  - **Autonomous Engine** — thinDispatch 7-flow routing; orchestrator confidence gate (v3.3.1).
104
150
 
105
- ## Consumer Install (postinstall)
151
+ ## Consumer Install (wear)
106
152
 
107
- On `npm install 0xray` in a consumer project, postinstall automatically:
153
+ `npm install 0xray` links the vendored repertoire and prints `npx 0xray wear`. Wear then:
108
154
 
109
155
  1. Copies **`AGENTS-consumer.md` → `AGENTS.md`**. Does **not** write consumer-root **`SKILLS.md`**.
110
156
  2. Fastens **mill plant** (`mill` + `inspect`). Fasten is **foundry-plant/0**: builtin plant is mill+inspect only. `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill package declares — organs (looker, vibe, mixer) live in the mill, not an xray catalog. Nested mill `@0xray/foundry@0.1.12`. CLI shims `foundry blip|sound` still exist on foundry 0.1.11+; fasten is the new cut. Not a 45-skill / 42-agent costume dump. `foundry.json` `"costume": true` is the opt-in dump.
@@ -113,7 +159,7 @@ On `npm install 0xray` in a consumer project, postinstall automatically:
113
159
  5. Writes project **`.mcp.json`** with 7 MCPs servers (`npx -y 0xray mcp …`)
114
160
  6. Installs **4 chat bridges**: OpenCode, Grok, Hermes, OpenClaw, plus **Cursor** `.cursor/hooks.json` (PPE + wiring; mill plant, not costume)
115
161
  7. Factory shop plant (`shop-extract`, `shop-witness`, `shop-pin` from groover-hangar) may coexist when worn. Extra shops: `foundry.json` `shopPlant`. Not costume.
116
- 8. Installs git pre-commit hook (non-blocking if not a git repo)
162
+ 8. Inside a git checkout, fastens Cursor `.cursor/hooks.json` and the hook scripts, and keeps an unwear snapshot. Outside a git checkout, skips those hooks and prints one warning. `npx 0xray setup --git-hooks` is what installs git hooks.
117
163
 
118
164
  See [llms.txt](llms.txt) for the agent map. Catalog of 45 skills lives in [SKILLS.md](SKILLS.md) — that is the exo catalog, not default consumer wear.
119
165
 
@@ -186,13 +232,13 @@ Docs: [memory routing](docs-site/docs/guides/memory-routing.md) · [Repertoire](
186
232
 
187
233
  ## Integrations
188
234
 
189
- | Platform | Install | Postinstall behavior |
235
+ | Platform | Install | What that command writes |
190
236
  |----------|---------|----------------------|
191
237
  | **OpenCode** | `npx 0xray opencode install` | Merges `opencode.json`, copies agent YML surfaces |
192
238
  | **Grok CLI / Build** | `npx 0xray grok install` | Project `.grok/plugins/0xray` (shared HOME does not clobber machine plugin), 7 MCPs servers |
193
239
  | **Hermes Agent** | `npx 0xray hermes install` | `~/.hermes/plugins/xray-hermes`, consumer root marker |
194
240
  | **OpenClaw** | `npx 0xray openclaw install` | `.xray/config/openclaw.json`, skill sync |
195
- | **Cursor** | postinstall (`installCursorBridge`) | Fastens `.cursor/hooks.json` + relative `.cursor/hooks/*.sh`. Rewrites leftover env-assignment one-liners. Not a fifth chat TUI. |
241
+ | **Cursor** | `npx 0xray wear` inside a git checkout | Fastens `.cursor/hooks.json` + relative `.cursor/hooks/*.sh`. Rewrites leftover env-assignment one-liners. Not a fifth chat TUI. |
196
242
 
197
243
  ## Governance & Codex
198
244
 
@@ -219,7 +265,7 @@ npm run release:npm # gate + prepare + npm publish --access public
219
265
 
220
266
  ## Release
221
267
 
222
- The **4.0** line is on npm. Do **not** run `release:major` to ship a 4.x fix (that becomes 5.0.0). Do **not** put a `scripts.publish` lifecycle that re-runs the gate after the registry PUT. Patch number lives in `package.json` + CHANGELOG.
268
+ The **4.1** line is on npm. Do **not** run `release:major` to ship a 4.x fix (that becomes 5.0.0). Do **not** put a `scripts.publish` lifecycle that re-runs the gate after the registry PUT. Patch number lives in `package.json` + CHANGELOG.
223
269
 
224
270
  ```bash
225
271
  npm run release:gate
@@ -235,4 +281,4 @@ MIT — see [LICENSE](LICENSE)
235
281
 
236
282
  ---
237
283
 
238
- *xray — MCP-centric, governed, autonomous. Pure v2 three-subsystem.*
284
+ *xray — a three-subsystem autonomous engine. The suit is the wear.*
package/dist/SKILLS.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # 0xray Skills
2
2
 
3
- **4.0** — a suit that survives the context window
3
+ **4.1** — a suit that survives the context window
4
+
5
+ Confer votes. Burst runs the swarm.
6
+
7
+ 0xRay is a three-subsystem autonomous engine. The suit is the wear. This file is the catalog.
4
8
 
5
9
  **45 skills** in this exo catalog. Default consumer plant is **`mill` + `inspect`**, not this dump. `npm install 0xray` does **not** sync 45 skills unless `foundry.json` `"costume": true`. Factory shop plant (`shop-extract`, `shop-witness`, `shop-pin`) coexists with mill plant when a hangar wears those shops. Extra shops: `foundry.json` `shopPlant`. Fasten is **foundry-plant/0**: builtin plant is mill+inspect only. `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill package declares — organs (looker, vibe, mixer) live in the mill, not this catalog. Nested mill `@0xray/foundry@0.1.12`. CLI shims `foundry blip|sound` still exist on foundry 0.1.11+. Agent map: [llms.txt](llms.txt).
6
10
 
@@ -69,7 +69,7 @@ async function installForOpenClaw(options = {}) {
69
69
  /* session-boot is best-effort */
70
70
  }
71
71
  const packageRoot = path.resolve(__dirname, '..', '..', '..');
72
- const hookInstalled = isolated ? null : wiring.installOpenClawHostWear(packageRoot);
72
+ const hookInstalled = isolated ? null : wiring.installOpenClawHostWear(packageRoot, targetDir);
73
73
  const cliBackend = isolated ? false : wiring.maybeWriteOpenClawCliBackend();
74
74
  frameworkLogger.log('openclaw-integration', 'cli-backend', 'info', { written: cliBackend });
75
75
  if (hookInstalled) {
@@ -16,3 +16,7 @@ export declare function pluginEnableCommand(pluginName: string): Promise<void>;
16
16
  export declare function pluginDisableCommand(pluginName: string): Promise<void>;
17
17
  export declare function pluginStatusCommand(pluginName: string): Promise<void>;
18
18
  export declare function pluginUninstallCommand(pluginName: string): Promise<void>;
19
+ type YamlMap = Record<string, unknown>;
20
+ /** Subset of YAML used by plugin.yaml: maps, scalar lists, and lists of maps. */
21
+ export declare function parseYaml(content: string): YamlMap;
22
+ export {};
@@ -152,24 +152,25 @@ export async function pluginStatusCommand(pluginName) {
152
152
  console.log(` Type: ${m.type}`);
153
153
  console.log(` Description: ${m.description}`);
154
154
  console.log(` License: ${m.license || "N/A"}`);
155
- if (m.capabilities) {
155
+ if (Array.isArray(m.capabilities)) {
156
156
  console.log("\n Capabilities:");
157
157
  for (const cap of m.capabilities) {
158
- console.log(` - ${cap}`);
158
+ console.log(` - ${String(cap)}`);
159
159
  }
160
160
  }
161
- if (m.tools) {
161
+ if (Array.isArray(m.tools)) {
162
162
  console.log("\n Tools:");
163
163
  for (const tool of m.tools) {
164
- console.log(` - ${tool.name}: ${tool.description}`);
164
+ const row = tool;
165
+ console.log(` - ${String(row.name)}: ${String(row.description)}`);
165
166
  }
166
167
  }
167
- if (m.runtime) {
168
+ if (m.runtime && typeof m.runtime === "object" && !Array.isArray(m.runtime)) {
168
169
  const runtime = m.runtime;
169
170
  console.log("\n Runtime:");
170
- console.log(` Command: ${runtime.command}`);
171
- if (runtime.args) {
172
- console.log(` Args: ${runtime.args.join(" ")}`);
171
+ console.log(` Command: ${String(runtime.command)}`);
172
+ if (Array.isArray(runtime.args)) {
173
+ console.log(` Args: ${runtime.args.map((arg) => String(arg)).join(" ")}`);
173
174
  }
174
175
  }
175
176
  console.log("\n" + "═".repeat(50) + "\n");
@@ -195,51 +196,93 @@ function validateManifest(manifest) {
195
196
  }
196
197
  }
197
198
  }
198
- function parseYaml(content) {
199
- const lines = content.split("\n");
200
- const result = {};
201
- let currentKey = "";
202
- const stack = [
203
- { indent: -1, obj: result },
204
- ];
205
- for (const line of lines) {
199
+ function parseScalar(raw) {
200
+ const value = raw.trim();
201
+ if ((value.startsWith('"') && value.endsWith('"')) ||
202
+ (value.startsWith("'") && value.endsWith("'"))) {
203
+ return value.slice(1, -1);
204
+ }
205
+ if (value.startsWith("[") && value.endsWith("]")) {
206
+ const inner = value.slice(1, -1).trim();
207
+ if (!inner)
208
+ return [];
209
+ return inner.split(",").map((part) => parseScalar(part));
210
+ }
211
+ if (value === "true")
212
+ return true;
213
+ if (value === "false")
214
+ return false;
215
+ if (/^-?\d+$/.test(value))
216
+ return Number(value);
217
+ return value;
218
+ }
219
+ /** Subset of YAML used by plugin.yaml: maps, scalar lists, and lists of maps. */
220
+ export function parseYaml(content) {
221
+ const root = {};
222
+ const stack = [{ indent: -1, container: root }];
223
+ for (const line of content.split("\n")) {
206
224
  const trimmed = line.trim();
207
225
  if (!trimmed || trimmed.startsWith("#"))
208
226
  continue;
209
227
  const indent = line.search(/\S/);
210
- const isListItem = trimmed.startsWith("- ");
211
- if (isListItem) {
212
- const value = trimmed.slice(2).trim();
228
+ while (stack.length > 1) {
213
229
  const top = stack[stack.length - 1];
214
- if (top && currentKey && !Array.isArray(top.obj[currentKey])) {
215
- top.obj[currentKey] = [];
216
- }
217
- if (top && currentKey) {
218
- top.obj[currentKey].push(value);
219
- }
220
- continue;
230
+ if (!top || indent > top.indent)
231
+ break;
232
+ stack.pop();
221
233
  }
222
- const colonIndex = trimmed.indexOf(":");
223
- if (colonIndex > 0) {
224
- const key = trimmed.slice(0, colonIndex).trim();
225
- const value = trimmed.slice(colonIndex + 1).trim();
226
- const top = stack[stack.length - 1];
227
- while (top && stack.length > 1 && indent <= top.indent) {
234
+ if (trimmed.startsWith("- ")) {
235
+ let owner = stack[stack.length - 1];
236
+ if (owner &&
237
+ !Array.isArray(owner.container) &&
238
+ Object.keys(owner.container).length === 0 &&
239
+ stack.length > 1) {
240
+ const holder = stack[stack.length - 2];
241
+ const emptyIndent = owner.indent;
228
242
  stack.pop();
243
+ if (holder && !Array.isArray(holder.container) && holder.key) {
244
+ const list = [];
245
+ holder.container[holder.key] = list;
246
+ stack.push({ indent: emptyIndent, container: list });
247
+ }
248
+ owner = stack[stack.length - 1];
229
249
  }
230
- const current = stack[stack.length - 1];
231
- if (!current)
250
+ if (!owner || !Array.isArray(owner.container))
232
251
  continue;
233
- const parent = current.obj;
234
- if (value) {
235
- parent[key] = value;
252
+ const rest = trimmed.slice(2).trim();
253
+ const colon = rest.indexOf(":");
254
+ const quoted = rest.startsWith('"') || rest.startsWith("'");
255
+ if (!quoted && colon > 0) {
256
+ const item = {};
257
+ const key = rest.slice(0, colon).trim();
258
+ const value = rest.slice(colon + 1).trim();
259
+ item[key] = value ? parseScalar(value) : {};
260
+ owner.container.push(item);
261
+ stack.push({ indent, container: item, key });
236
262
  }
237
263
  else {
238
- parent[key] = {};
239
- stack.push({ indent, obj: parent[key] });
264
+ owner.container.push(parseScalar(rest));
240
265
  }
241
- currentKey = key;
266
+ continue;
267
+ }
268
+ const colonIndex = trimmed.indexOf(":");
269
+ if (colonIndex <= 0)
270
+ continue;
271
+ const key = trimmed.slice(0, colonIndex).trim();
272
+ const value = trimmed.slice(colonIndex + 1).trim();
273
+ const current = stack[stack.length - 1];
274
+ if (!current || Array.isArray(current.container))
275
+ continue;
276
+ if (value) {
277
+ current.container[key] = parseScalar(value);
278
+ current.key = key;
279
+ }
280
+ else {
281
+ const child = {};
282
+ current.container[key] = child;
283
+ current.key = key;
284
+ stack.push({ indent, container: child });
242
285
  }
243
286
  }
244
- return result;
287
+ return root;
245
288
  }
package/dist/cli/index.js CHANGED
@@ -116,6 +116,18 @@ program
116
116
  .action((args) => {
117
117
  runGoggles(args);
118
118
  });
119
+ program
120
+ .command("confer")
121
+ .argument("<state>", "on or off")
122
+ .description("Turn confer on or off in .xray/features.json. A missing key stays off until this runs.")
123
+ .action((state) => {
124
+ try {
125
+ runNodeScript("confer-switch.cjs", [state]);
126
+ }
127
+ catch {
128
+ process.exit(1);
129
+ }
130
+ });
119
131
  program
120
132
  .command("wear")
121
133
  .description("Wear the 0xray suit in this git checkout")
@@ -93,9 +93,9 @@ export class XrayConfigLoader {
93
93
  }
94
94
  const cfg = config;
95
95
  return {
96
- enabled: typeof cfg.enabled === 'boolean' ? cfg.enabled : true,
96
+ enabled: typeof cfg.enabled === 'boolean' ? cfg.enabled : false,
97
97
  interval_minutes: Math.max(5, Math.min(1440, typeof cfg.interval_minutes === 'number' ? cfg.interval_minutes : 60)),
98
- auto_schedule: typeof cfg.auto_schedule === 'boolean' ? cfg.auto_schedule : true,
98
+ auto_schedule: typeof cfg.auto_schedule === 'boolean' ? cfg.auto_schedule : false,
99
99
  include_health_assessment: typeof cfg.include_health_assessment === 'boolean' ? cfg.include_health_assessment : true,
100
100
  include_agent_activities: typeof cfg.include_agent_activities === 'boolean' ? cfg.include_agent_activities : true,
101
101
  include_pipeline_operations: typeof cfg.include_pipeline_operations === 'boolean' ? cfg.include_pipeline_operations : true,
@@ -123,9 +123,9 @@ export class XrayConfigLoader {
123
123
  session_persistence: true,
124
124
  },
125
125
  autonomous_reporting: {
126
- enabled: true,
126
+ enabled: false,
127
127
  interval_minutes: 60,
128
- auto_schedule: true,
128
+ auto_schedule: false,
129
129
  include_health_assessment: true,
130
130
  include_agent_activities: true,
131
131
  include_pipeline_operations: true,
@@ -406,7 +406,7 @@ export interface FeaturesConfig {
406
406
  enabled?: boolean;
407
407
  };
408
408
  memory_routing?: MemoryRoutingConfig;
409
- /** P2.2 — lightweight Grok write-tool postprocessor marker */
409
+ /** Marker is opt-in and the shipped default is off. */
410
410
  grok_postprocessor_light?: boolean;
411
411
  /** P2 — git post-commit session inference capture */
412
412
  inference_session_capture?: {
@@ -342,9 +342,9 @@ export class FeaturesConfigLoader {
342
342
  no_new_surface: true,
343
343
  },
344
344
  autonomous_reporting: {
345
- enabled: true,
345
+ enabled: false,
346
346
  interval_minutes: 60,
347
- auto_schedule: true,
347
+ auto_schedule: false,
348
348
  include_health_assessment: true,
349
349
  include_agent_activities: true,
350
350
  include_pipeline_operations: true,
@@ -410,7 +410,7 @@ export class FeaturesConfigLoader {
410
410
  max_cache_size_mb: 50,
411
411
  },
412
412
  inference: {
413
- description: "Self-improvement cycle that grades a new named session into repertoire",
413
+ description: "The cycle runs from the CLI command inference:run and the session wake does not start it.",
414
414
  enabled: true,
415
415
  workflow_dir: ".xray/inference",
416
416
  reports_dir: ".xray/reports",
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Enforcement entry. The Hermes bridge loads ValidatorRegistry from this file.
3
+ */
4
+ export { ValidatorRegistry, globalValidatorRegistry, } from "./validators/index.js";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Enforcement entry. The Hermes bridge loads ValidatorRegistry from this file.
3
+ */
4
+ export { ValidatorRegistry, globalValidatorRegistry, } from "./validators/index.js";
@@ -322,13 +322,15 @@ export class ConsoleLogUsageValidator extends BaseValidator {
322
322
  severity = "error";
323
323
  async validate(context) {
324
324
  const { newCode } = context;
325
- // Skip validation if no code to check
326
325
  if (!newCode) {
327
326
  return this.createSuccessResult("No code to validate for console.log usage");
328
327
  }
329
- // Check for console.log usage
330
- if (newCode.includes("await frameworkLogger.log('rule-enforcer', '-return-passed-false-message-console-log-', 'info', { message: ")) {
331
- return this.createFailureResult("await frameworkLogger.log('rule-enforcer', '-', 'info', { message: } }); detected - use frameworkLogger for production logs or remove for debugging");
328
+ const codeWithoutComments = newCode
329
+ .replace(/\/\/.*$/gm, "")
330
+ .replace(/\/\*[\s\S]*?\*\//g, "");
331
+ const consoleCall = /console\.(?:log|debug|info|warn|error|trace)\s*\(/;
332
+ if (consoleCall.test(codeWithoutComments)) {
333
+ return this.createFailureResult("console method detected — use frameworkLogger", ["Use frameworkLogger instead of console methods"]);
332
334
  }
333
335
  return this.createSuccessResult("Console log usage follows proper guidelines");
334
336
  }
@@ -1,4 +1,9 @@
1
- export type GovernanceRole = "code-review" | "security-audit" | "researcher";
1
+ export type GovernanceRole = "code-review" | "security-audit" | "researcher" | "architect";
2
+ export type GovernanceMiss = "not-configured" | "error" | "timeout" | "empty" | "unparseable";
3
+ export interface GovernanceAttempt {
4
+ vote: GovernanceVote | null;
5
+ miss: GovernanceMiss | null;
6
+ }
2
7
  export interface GovernanceVote {
3
8
  decision: "approve" | "reject" | "abstain";
4
9
  confidence: number;
@@ -7,6 +12,7 @@ export interface GovernanceVote {
7
12
  export declare function resolveHermesBin(): string;
8
13
  export declare function isGovernanceLlmConfigured(): boolean;
9
14
  export declare function hermesCliAvailable(): boolean;
15
+ export declare function attemptLLMGovernance(role: GovernanceRole, proposalTitle: string, proposalDescription: string, evidence: string[], proposalType: string): Promise<GovernanceAttempt>;
10
16
  export declare function tryLLMGovernance(role: GovernanceRole, proposalTitle: string, proposalDescription: string, evidence: string[], proposalType: string): Promise<GovernanceVote | null>;
11
17
  export declare function checkHermesOAuthStatus(): {
12
18
  configured: boolean;
@@ -41,6 +41,19 @@ Respond with a structured vote in exactly this format:
41
41
  DECISION: approve|reject|abstain
42
42
  CONFIDENCE: <0.0-1.0>
43
43
  REASONING: <2-4 sentence analysis of the proposal's broader project impact>`,
44
+ architect: `You are a senior architect on a governance committee for a software project. Analyze the following proposal and architecture assessment.
45
+
46
+ Consider:
47
+ - Does the change fit the existing architecture?
48
+ - Does it raise coupling, complexity, or scaling risk?
49
+ - Should the work stop until a structural problem is fixed?
50
+
51
+ Respond with a structured vote in exactly this format:
52
+ DECISION: approve|reject|abstain
53
+ CONFIDENCE: <0.0-1.0>
54
+ REASONING: <2-4 sentence analysis>
55
+
56
+ reject means the architecture should not proceed.`,
44
57
  };
45
58
  function resolveHermesProvider() {
46
59
  return process.env.HERMES_PROVIDER?.trim() || DEFAULT_HERMES_PROVIDER;
@@ -151,18 +164,43 @@ function runHermesGovernanceInference(prompt, config) {
151
164
  maxBuffer: 10 * 1024 * 1024,
152
165
  }).trim();
153
166
  }
167
+ function sanitizeVoteReasoning(reasoning) {
168
+ return reasoning
169
+ .split("\n")
170
+ .map((line) => /^\s*(?:[-*•]\s*)?(?:Verdict:|DECISION:)/i.test(line) ? `> ${line.trim()}` : line)
171
+ .join("\n")
172
+ .trim();
173
+ }
154
174
  function parseVote(text) {
155
- const decisionMatch = text.match(/DECISION:\s*(approve|reject|abstain)/i);
156
- const confidenceMatch = text.match(/CONFIDENCE:\s*([0-9.]+)/i);
157
- const reasoningMatch = text.match(/REASONING:\s*(.+)/is);
158
- if (!decisionMatch || !decisionMatch[1])
175
+ // First exact token before REASONING. The prompt menu and anything the model
176
+ // writes inside the reasoning body are not a second vote.
177
+ const lines = text.split("\n");
178
+ const reasoningIdx = lines.findIndex((line) => /^REASONING:/i.test(line.trim()));
179
+ const headerLines = reasoningIdx < 0 ? lines : lines.slice(0, reasoningIdx);
180
+ let decisionWord;
181
+ for (const line of headerLines) {
182
+ const match = line.trim().match(/^DECISION:\s*(approve|reject|abstain)\s*$/i);
183
+ if (match?.[1]) {
184
+ decisionWord = match[1];
185
+ break;
186
+ }
187
+ }
188
+ if (!decisionWord)
159
189
  return null;
190
+ const header = headerLines.join("\n");
191
+ const confidenceMatch = header.match(/CONFIDENCE:\s*([0-9.]+)/i);
160
192
  const rawConfidence = confidenceMatch?.[1];
161
193
  const confidence = rawConfidence ? parseFloat(rawConfidence) : 0.5;
194
+ const reasoningBody = reasoningIdx < 0
195
+ ? ""
196
+ : lines
197
+ .slice(reasoningIdx)
198
+ .join("\n")
199
+ .replace(/^REASONING:\s*/i, "");
162
200
  return {
163
- decision: decisionMatch[1].toLowerCase(),
164
- confidence: isNaN(confidence) ? 0.5 : Math.min(1, Math.max(0, confidence)),
165
- reasoning: reasoningMatch?.[1]?.trim() || "No reasoning provided",
201
+ decision: decisionWord.toLowerCase(),
202
+ confidence: Number.isNaN(confidence) ? 0.5 : Math.min(1, Math.max(0, confidence)),
203
+ reasoning: sanitizeVoteReasoning(reasoningBody) || "No reasoning provided",
166
204
  };
167
205
  }
168
206
  async function callDirectLlm(role, config, proposalTitle, proposalDescription, evidence, proposalType) {
@@ -209,11 +247,28 @@ Please provide your structured vote.`;
209
247
  const message = choices?.[0]?.message;
210
248
  return message?.content ?? null;
211
249
  }
212
- export async function tryLLMGovernance(role, proposalTitle, proposalDescription, evidence, proposalType) {
250
+ function missFromThrown(error) {
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ const code = typeof error === "object" && error !== null && "code" in error
253
+ ? String(error.code ?? "")
254
+ : "";
255
+ const killed = typeof error === "object" &&
256
+ error !== null &&
257
+ "killed" in error &&
258
+ error.killed === true;
259
+ if (code === "ETIMEDOUT" || killed || /ETIMEDOUT|timed out/i.test(message)) {
260
+ return "timeout";
261
+ }
262
+ if (/invalid_grant|not configured|unauthorized|no credentials|authentication/i.test(message)) {
263
+ return "not-configured";
264
+ }
265
+ return "error";
266
+ }
267
+ export async function attemptLLMGovernance(role, proposalTitle, proposalDescription, evidence, proposalType) {
213
268
  const config = getConfig();
214
269
  if (!config) {
215
270
  process.stderr.write(`[llm] no config for role=${role}\n`);
216
- return null;
271
+ return { vote: null, miss: "not-configured" };
217
272
  }
218
273
  if (config.mode === "hermes") {
219
274
  process.stderr.write(`[llm] using hermes -z provider=${config.provider} model=${config.model}\n`);
@@ -230,12 +285,12 @@ export async function tryLLMGovernance(role, proposalTitle, proposalDescription,
230
285
  const content = config.mode === "hermes"
231
286
  ? runHermesGovernanceInference(buildGovernancePrompt(role, proposalTitle, proposalDescription, evidence, proposalType), config)
232
287
  : await callDirectLlm(role, config, proposalTitle, proposalDescription, evidence, proposalType);
233
- if (!content) {
288
+ if (!content?.trim()) {
234
289
  frameworkLogger.log("llm-governance", "parse", "warning", {
235
290
  issue: "missing content in response",
236
291
  role,
237
292
  });
238
- return null;
293
+ return { vote: null, miss: "empty" };
239
294
  }
240
295
  const vote = parseVote(content);
241
296
  if (!vote) {
@@ -244,23 +299,26 @@ export async function tryLLMGovernance(role, proposalTitle, proposalDescription,
244
299
  snippet: content.slice(0, 200),
245
300
  role,
246
301
  });
247
- return null;
302
+ return { vote: null, miss: "unparseable" };
248
303
  }
249
304
  frameworkLogger.log("llm-governance", "vote", "success", {
250
305
  role,
251
306
  decision: vote.decision,
252
307
  confidence: vote.confidence,
253
308
  });
254
- return vote;
309
+ return { vote, miss: null };
255
310
  }
256
311
  catch (error) {
257
312
  frameworkLogger.log("llm-governance", "error", "error", {
258
313
  error: String(error),
259
314
  role,
260
315
  });
261
- return null;
316
+ return { vote: null, miss: missFromThrown(error) };
262
317
  }
263
318
  }
319
+ export async function tryLLMGovernance(role, proposalTitle, proposalDescription, evidence, proposalType) {
320
+ return (await attemptLLMGovernance(role, proposalTitle, proposalDescription, evidence, proposalType)).vote;
321
+ }
264
322
  export function checkHermesOAuthStatus() {
265
323
  const direct = getDirectLlmConfig();
266
324
  if (direct) {