Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions README_GROK_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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.

**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.

## 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

Fixed data-dir resolution + compass routing 2026-06-27. PR #9.

See also: `T2_HELIX_GROK_INTEGRATION_SPEC.md`
30 changes: 30 additions & 0 deletions T2_HELIX_GROK_INTEGRATION_SPEC.md
Original file line number Diff line number Diff line change
@@ -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.
179 changes: 179 additions & 0 deletions lib/grok-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
'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 = {}) {
// 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 {
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 (_) {}

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: actionSummary ? `${actionSummary}\n\n${content}`.trim() : content,
layer: context.layer || 'hypothesis',
domain: context.domain || 'grok-witness',
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,
actionSummary,
recorded: !!(recordResult && recordResult.id),
result: recordResult,
session_id,
adapter: 'grok-adapter',
note: classification === 'PAUSE' ? 'PAUSE treated as non-blocking in this creative-layer adapter (no token flow yet)' : undefined
};
}

/**
* 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
};
7 changes: 7 additions & 0 deletions lib/rules/compass-rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
]
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "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)'"
}
}
Loading