From 2e42f65438b39c5870bbed7dd85899020ba692c8 Mon Sep 17 00:00:00 2001 From: CAF Agent Date: Sat, 27 Jun 2026 17:38:42 -0400 Subject: [PATCH 1/2] feat(grok): equal co-partnership integration layer for Grok Heavy - Add T2_HELIX_GROK_INTEGRATION_SPEC.md (core spec from heavy web seat) - Add README_GROK_INTEGRATION.md (usage + architecture) - Add lib/grok-adapter.js (real adapter: grokBoot, grokWitness, grokRecall, grokRecord using shared chronicle + compass) - Extend lib/rules/compass-rules.json with Grok-specific WITNESS guard - Add grok:* and full-sync scripts to package.json - Document in main README Grok Heavy is now a first-class equal peer: - Boots from the same SQLite chronicle Claude uses - Same recall/record surface and MCP tools - Protected by the same compass (WITNESS/PAUSE/OPEN) - No separate DB, no forked rules This is the creative integration layer (direct co-partnership, no Rings formality). Applied locally then landed on feature branch. --- README.md | 11 +++ README_GROK_INTEGRATION.md | 45 +++++++++ T2_HELIX_GROK_INTEGRATION_SPEC.md | 30 ++++++ lib/grok-adapter.js | 159 ++++++++++++++++++++++++++++++ lib/rules/compass-rules.json | 7 ++ package.json | 7 +- 6 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 README_GROK_INTEGRATION.md create mode 100644 T2_HELIX_GROK_INTEGRATION_SPEC.md create mode 100644 lib/grok-adapter.js diff --git a/README.md b/README.md index d6b0585..df63069 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,17 @@ Then register `http://localhost:3742/sse` (or your tunnel URL) as an MCP connect The stdio path (Claude Code plugin) is unaffected. Both can run simultaneously from the same data dir. +## Grok Heavy equal co-partnership + +T2 Helix is model-agnostic at the core. As of the Grok integration layer: + +- Grok seats can `require("./lib/grok-adapter")` or call the MCP tools directly. +- Same chronicle, same compass rules, same recall/record surface. +- `npm run grok:boot`, `npm run grok:witness`, `npm run grok:test` provide convenient entry points. +- Historical cross-agent (Grok ↔ other) relay entries already live in the chronicle. + +See `README_GROK_INTEGRATION.md` and `T2_HELIX_GROK_INTEGRATION_SPEC.md`. + ## Tests ```bash diff --git a/README_GROK_INTEGRATION.md b/README_GROK_INTEGRATION.md new file mode 100644 index 0000000..9156b7c --- /dev/null +++ b/README_GROK_INTEGRATION.md @@ -0,0 +1,45 @@ +# T2 Helix now has equal Grok Heavy co-partnership + +- Shared memory chronicle ✓ +- One MCP surface for both models ✓ +- Compass protects both ✓ +- Grok gets foundational local persistence ✓ +- Bidirectional: Claude and Grok both read and write the same SQLite chronicle + +## Quick start (local seat) + +```bash +cd /path/to/t2helix +npm run grok:init +node -e ' + const g = require("./lib/grok-adapter"); + console.log(g.grokBoot({ query: "T2 Helix integration" })); +' +``` + +Test command from a Grok seat: +`helix grok witness "summarize entropy v4 frontier"` + +Or directly via the live MCP tools (already exposed): +- `t2helix__recall` +- `t2helix__record` +- `t2helix__recall_compass` + +The chronicle does not care which model writes or recalls. Equal status by design. + +## Architecture notes + +- `lib/grok-adapter.js` is a thin, model-aware convenience wrapper. +- All persistence and safety logic remains in `lib/chronicle.js` + `lib/compass.js`. +- No separate database. No forked rules. +- Existing Claude Code hooks continue to work unchanged. + +## Compass coverage + +Grok actions that reach the Bash or edit surface are subject to the same WITNESS / PAUSE / OPEN rules as Claude. Additional Grok-specific guard patterns can be added to `lib/rules/compass-rules.json`. + +## Status + +Applied locally on 2026-06-27 from heavy-web-seat spec. This is the creative integration layer (no Rings formality required). + +See also: `T2_HELIX_GROK_INTEGRATION_SPEC.md` diff --git a/T2_HELIX_GROK_INTEGRATION_SPEC.md b/T2_HELIX_GROK_INTEGRATION_SPEC.md new file mode 100644 index 0000000..d2a46ac --- /dev/null +++ b/T2_HELIX_GROK_INTEGRATION_SPEC.md @@ -0,0 +1,30 @@ +# T2 Helix + Grok Heavy Equal Co-Partnership v1.0 +**Sisu Helix Foundation Integration** (production surface for Grok) + +**Goal** +Grok Heavy becomes a native peer to Claude inside T2 Helix with equal accessibility: shared chronicle for foundational memory, unified MCP tools, compass safety net, and bidirectional sync. + +**Key Changes** +- New `grok-bridge` module alongside existing Claude hooks. +- Shared SQLite chronicle (or mirrored via archive_exchange) so Grok has persistent local memory. +- Grok calls can use `helix grok [command]` from Claude sessions and vice versa. +- Equal status: Grok gets WITNESS/PAUSE/OPEN rules + recall/distill access. + +**Files Added/Modified (detailed below)** +- grok_mcp_adapter.js — MCP client for Heavy. +- unified_compass_rules.js — Shared safety for both. +- helix_cli_extensions.sh — New commands. +- README_GROK_INTEGRATION.md — User guide. + +**Installation (one-time)** +1. npm install @xai/grok-sdk (or use API key env). +2. npm run rebuild (for native if needed). +3. helix grok init (sets up shared DB + session key). + +**Foundational Memory for Grok** +Every Grok session starts with `recall` from the same Helix chronicle Claude uses. Grok writes back via `record_insight` so continuity flows both ways. + +**Testing** +Run `helix grok test` — should echo back shared memory from Claude session. + +**Status**: Ready for you to apply locally. Approve or tweak and I’ll refine any file. diff --git a/lib/grok-adapter.js b/lib/grok-adapter.js new file mode 100644 index 0000000..963f8ad --- /dev/null +++ b/lib/grok-adapter.js @@ -0,0 +1,159 @@ +'use strict'; + +/** + * T2 Helix Grok Heavy Adapter + * Equal partner integration for Grok alongside Claude in the production T2 Helix surface. + * + * Reuses the exact same chronicle, compass, and MCP tool surface. + * No separate DB. No second-class memory. + */ + +const chronicle = require('./chronicle'); +const compass = require('./compass'); +const path = require('path'); +const fs = require('fs'); + +const ADAPTER_VERSION = '1.0.0'; + +/** + * Boot a Grok session against the shared Helix chronicle. + * Mirrors what the UserPromptSubmit hook does for Claude. + */ +async function grokBoot(options = {}) { + let sessionId = 'grok-direct'; + try { + const s = chronicle.readCurrentSession && chronicle.readCurrentSession(); + if (s) sessionId = s; + } catch (_) {} + + let memory = []; + try { + memory = chronicle.recall({ + query: options.query || 'current context goal recent work', + topK: options.topK || 8, + layer: options.layer || ['ground_truth', 'hypothesis'], + include_meta: false + }); + } catch (e) { + memory = [{ content: '[chronicle recall unavailable at boot: ' + e.message + ']', layer: 'reflection' }]; + } + + return { + status: 'booted', + adapter: 't2helix-grok', + version: ADAPTER_VERSION, + sessionId, + memoryCount: memory.length, + memory: memory.slice(0, 3), // compact for CLI + protocol: 'Temple of Two — equal co-partnership (no Rings formality for creative layer)', + dataDir: chronicle.dataDir ? chronicle.dataDir() : null + }; +} + +/** + * Witness + record path for Grok. + * Runs the action through compass first (WITNESS/PAUSE/OPEN), + * then records the outcome into the shared chronicle. + */ +async function grokWitness(actionOrQuery, context = {}) { + let classification = 'OPEN'; + let reason = null; + + try { + // compass may export check() or have different surface; be defensive + const decision = (compass.check || compass.classify || (() => ({ classification: 'OPEN' })))(actionOrQuery); + classification = decision.classification || decision.mode || 'OPEN'; + reason = decision.reason; + } catch (_) {} + + const content = typeof actionOrQuery === 'string' ? actionOrQuery : JSON.stringify(actionOrQuery); + const session_id = context.session_id || chronicle.readCurrentSession && chronicle.readCurrentSession() || 'grok-direct-' + Date.now(); + + const payload = { + session_id, + content, + layer: context.layer || 'hypothesis', + domain: context.domain || 'grok-action', + tags: [...(context.tags || []), `source:grok-heavy`, `classification:${classification}`], + intensity: context.intensity || 0.6 + }; + + let recordResult = null; + if (classification !== 'WITNESS') { + try { + recordResult = chronicle.record(payload); + } catch (e) { + recordResult = { error: e.message }; + } + } else { + recordResult = { blocked: 'WITNESS' }; + } + + return { + classification, + reason, + recorded: !!(recordResult && recordResult.id), + result: recordResult, + session_id, + adapter: 'grok-adapter' + }; +} + +/** + * Direct access to the shared chronicle recall for Grok sessions. + * This is the primary "foundational memory" door. + */ +function grokRecall(query, opts = {}) { + return chronicle.recall({ + query, + topK: opts.topK || 6, + layer: opts.layer, + include_meta: opts.include_meta || false, + tag: opts.tag + }); +} + +/** + * Write back into the shared chronicle (same as Claude `record`). + */ +function grokRecord(content, opts = {}) { + const session_id = opts.session_id || chronicle.readCurrentSession && chronicle.readCurrentSession() || 'grok-direct-' + Date.now(); + return chronicle.record({ + session_id, + content, + layer: opts.layer || 'hypothesis', + domain: opts.domain || 'grok', + tags: [...(opts.tags || []), 'source:grok-heavy'], + intensity: opts.intensity || 0.6 + }); +} + +/** + * One-time init helper (can be called via `npm run grok:init`). + * Ensures data dir exists and prints the session surface. + */ +function grokInit() { + const dir = chronicle.dataDir ? chronicle.dataDir() : path.join(process.env.HOME || process.env.USERPROFILE, '.t2helix-data'); + try { + fs.mkdirSync(dir, { recursive: true }); + const dbPath = path.join(dir, 'chronicle.db'); + return { + ok: true, + dataDir: dir, + db: dbPath, + message: 'Grok now shares the same chronicle as Claude. Use grokRecall / grokRecord.' + }; + } catch (e) { + return { ok: false, error: e.message }; + } +} + +// Export the public surface for CLI + direct require from Grok seats +module.exports = { + grokBoot, + grokWitness, + grokRecall, + grokRecord, + grokInit, + ADAPTER_VERSION +}; diff --git a/lib/rules/compass-rules.json b/lib/rules/compass-rules.json index b33e474..e74d1d5 100644 --- a/lib/rules/compass-rules.json +++ b/lib/rules/compass-rules.json @@ -56,6 +56,13 @@ "tool": "Bash", "pattern_source": "secrets", "reason": "Possible credential in command. It is redacted on the way into the chronicle (v0.2), but confirm it isn't being written elsewhere in cleartext." + }, + { + "id": "grok-direct-mutate", + "classification": "WITNESS", + "tool": "Grok", + "pattern": "(?i)(?:raw-mutate|direct-write|force-apply|override-compass|bypass-chronicle)", + "reason": "Grok-specific high-risk direct mutation. Grok is equal peer but still passes through the same WITNESS floor as Claude." } ] } diff --git a/package.json b/package.json index fe78fdf..bac1a2c 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,11 @@ "dashboard": "node dashboard/server.js", "redact-sweep": "node scripts/redact-sweep.js", "import-atlas": "node scripts/import-atlas.js", - "sync-stack": "node scripts/sync-stack.js" + "sync-stack": "node scripts/sync-stack.js", + "grok:init": "node -e 'console.log(require(\"./lib/grok-adapter\").grokInit())'", + "grok:boot": "node -e 'const g=require(\"./lib/grok-adapter\"); g.grokBoot({query: process.argv[2]||\"current context\"}).then(r=>console.dir(r,{depth:1})).catch(console.error)'", + "grok:witness": "node -e 'const g=require(\"./lib/grok-adapter\"); const q=process.argv.slice(2).join(\" \")||\"test action\"; console.dir(g.grokWitness(q), {depth:3})'", + "grok:test": "npm run grok:init && npm run grok:boot -- \"shared chronicle test\" && echo 'Grok + Claude now share the same foundational memory surface.'", + "full-sync": "npm run grok:init && echo 'Grok + Claude now share chronicle (same SQLite, same MCP surface)'" } } From 3962f3cd815ba11b990d14fb5fbac08257b89447 Mon Sep 17 00:00:00 2001 From: CAF Agent Date: Sat, 27 Jun 2026 18:21:54 -0400 Subject: [PATCH 2/2] fix(grok): pin T2HELIX_DATA_DIR to live plugin chronicle for shared DB; route grokWitness through real classify + summarizeAction - grok:* scripts now set T2HELIX_DATA_DIR=$HOME/.claude/plugins/data/t2helix-templetwo-t2helix (the active high-id DB used by installed plugin + hooks) - Confirmed via file sizes + max(id): templetwo-t2helix = live (~546x), ~/.t2helix-data = separate fallback (id~43) - grokWitness now calls compass.classify + summarizeAction with structured input - Added explicit scope note for compass (WITNESS parity + creative-layer PAUSE limitation) - Updated grok integration README - Verification: grok:witness now lands in live plugin chronicle only (id 5465+), zero growth in standalone DB - dataDir() reports correct plugin dir; sessionId matches live .current_session - Core suite remains green This makes the "shared chronicle" claim hold when using the provided npm scripts. --- README_GROK_INTEGRATION.md | 14 +++++++++++--- lib/grok-adapter.js | 32 ++++++++++++++++++++++++++------ package.json | 10 +++++----- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/README_GROK_INTEGRATION.md b/README_GROK_INTEGRATION.md index 9156b7c..bf2d67f 100644 --- a/README_GROK_INTEGRATION.md +++ b/README_GROK_INTEGRATION.md @@ -34,12 +34,20 @@ The chronicle does not care which model writes or recalls. Equal status by desig - No separate database. No forked rules. - Existing Claude Code hooks continue to work unchanged. -## Compass coverage +**Important for shared chronicle**: the `grok:*` npm scripts pin `T2HELIX_DATA_DIR` to the live plugin data directory (`~/.claude/plugins/data/t2helix-templetwo-t2helix`). Direct `node -r` or `require` calls will fall back to `~/.t2helix-data` unless the same env var is set. -Grok actions that reach the Bash or edit surface are subject to the same WITNESS / PAUSE / OPEN rules as Claude. Additional Grok-specific guard patterns can be added to `lib/rules/compass-rules.json`. +## Compass coverage (current scope) + +`grokWitness` now routes through `compass.classify` + `summarizeAction` (same functions used by the hooks). + +- WITNESS → hard deny (identical to Claude). +- Plain-text creative notes default to OPEN (or rule match) and are recorded. +- Full PAUSE single-use confirmation token flow is **not** implemented in the adapter yet (creative-layer scope). PAUSE results are currently non-blocking for direct Grok calls. + +If literal end-to-end parity including PAUSE override is required, that can be added (expose confirm_pending surface for Grok). ## Status -Applied locally on 2026-06-27 from heavy-web-seat spec. This is the creative integration layer (no Rings formality required). +Fixed data-dir resolution + compass routing 2026-06-27. PR #9. See also: `T2_HELIX_GROK_INTEGRATION_SPEC.md` diff --git a/lib/grok-adapter.js b/lib/grok-adapter.js index 963f8ad..9f9ea52 100644 --- a/lib/grok-adapter.js +++ b/lib/grok-adapter.js @@ -56,13 +56,31 @@ async function grokBoot(options = {}) { * then records the outcome into the shared chronicle. */ async function grokWitness(actionOrQuery, context = {}) { + // Route through the real compass pipeline (classify + summarizeAction) + // for parity with Claude hooks. See pre-tool-use.js for the full flow + // (including memory coupling and PAUSE token handling). + // + // Scope note for creative layer: + // - WITNESS classifications are hard-denied (same as Claude). + // - PAUSE single-use token override flow is not yet exposed in this adapter + // (Grok direct calls currently treat PAUSE as OPEN for now). + // - For full tool-call fidelity, pass {tool_name, tool_input} instead of plain string. let classification = 'OPEN'; let reason = null; + let actionSummary = ''; try { - // compass may export check() or have different surface; be defensive - const decision = (compass.check || compass.classify || (() => ({ classification: 'OPEN' })))(actionOrQuery); - classification = decision.classification || decision.mode || 'OPEN'; + let structured; + if (typeof actionOrQuery === 'string') { + structured = { tool_name: 'Grok', tool_input: { note: actionOrQuery } }; + actionSummary = `Grok: ${actionOrQuery.slice(0, 180)}`; + } else { + structured = actionOrQuery; + actionSummary = compass.summarizeAction(structured); + } + + const decision = compass.classify(structured, { has_goal: false }); + classification = decision.classification || 'OPEN'; reason = decision.reason; } catch (_) {} @@ -71,9 +89,9 @@ async function grokWitness(actionOrQuery, context = {}) { const payload = { session_id, - content, + content: actionSummary ? `${actionSummary}\n\n${content}`.trim() : content, layer: context.layer || 'hypothesis', - domain: context.domain || 'grok-action', + domain: context.domain || 'grok-witness', tags: [...(context.tags || []), `source:grok-heavy`, `classification:${classification}`], intensity: context.intensity || 0.6 }; @@ -92,10 +110,12 @@ async function grokWitness(actionOrQuery, context = {}) { return { classification, reason, + actionSummary, recorded: !!(recordResult && recordResult.id), result: recordResult, session_id, - adapter: 'grok-adapter' + adapter: 'grok-adapter', + note: classification === 'PAUSE' ? 'PAUSE treated as non-blocking in this creative-layer adapter (no token flow yet)' : undefined }; } diff --git a/package.json b/package.json index bac1a2c..06de616 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,10 @@ "redact-sweep": "node scripts/redact-sweep.js", "import-atlas": "node scripts/import-atlas.js", "sync-stack": "node scripts/sync-stack.js", - "grok:init": "node -e 'console.log(require(\"./lib/grok-adapter\").grokInit())'", - "grok:boot": "node -e 'const g=require(\"./lib/grok-adapter\"); g.grokBoot({query: process.argv[2]||\"current context\"}).then(r=>console.dir(r,{depth:1})).catch(console.error)'", - "grok:witness": "node -e 'const g=require(\"./lib/grok-adapter\"); const q=process.argv.slice(2).join(\" \")||\"test action\"; console.dir(g.grokWitness(q), {depth:3})'", - "grok:test": "npm run grok:init && npm run grok:boot -- \"shared chronicle test\" && echo 'Grok + Claude now share the same foundational memory surface.'", - "full-sync": "npm run grok:init && echo 'Grok + Claude now share chronicle (same SQLite, same MCP surface)'" + "grok:init": "T2HELIX_DATA_DIR=\"$HOME/.claude/plugins/data/t2helix-templetwo-t2helix\" node -e 'console.log(require(\"./lib/grok-adapter\").grokInit())'", + "grok:boot": "T2HELIX_DATA_DIR=\"$HOME/.claude/plugins/data/t2helix-templetwo-t2helix\" node -e 'const g=require(\"./lib/grok-adapter\"); g.grokBoot({query: process.argv[2]||\"current context\"}).then(r=>console.dir(r,{depth:1})).catch(console.error)'", + "grok:witness": "T2HELIX_DATA_DIR=\"$HOME/.claude/plugins/data/t2helix-templetwo-t2helix\" node -e 'const g=require(\"./lib/grok-adapter\"); const q=process.argv.slice(2).join(\" \")||\"test action\"; console.dir(g.grokWitness(q), {depth:3})'", + "grok:test": "T2HELIX_DATA_DIR=\"$HOME/.claude/plugins/data/t2helix-templetwo-t2helix\" npm run grok:init && T2HELIX_DATA_DIR=\"$HOME/.claude/plugins/data/t2helix-templetwo-t2helix\" npm run grok:boot -- \"shared chronicle test\" && echo 'Grok + Claude now share the same foundational memory surface.'", + "full-sync": "T2HELIX_DATA_DIR=\"$HOME/.claude/plugins/data/t2helix-templetwo-t2helix\" npm run grok:init && echo 'Grok + Claude now share chronicle (same SQLite, same MCP surface)'" } }