diff --git a/lib/tui/session.mjs b/lib/tui/session.mjs index 3514cff..00c1fa6 100644 --- a/lib/tui/session.mjs +++ b/lib/tui/session.mjs @@ -185,17 +185,28 @@ export function resolveTuiEntrypointEnv(env, mode = "cli") { // A-PATH ONLY: built-in tools are left enabled (acceptable single-user). Deployment B // (guest keys) MUST additionally pass --tools "" per spec §5.2(2) as the credential // wall before this argv is reachable for owner_tier=guest — guard that in PR-3 wiring. -function buildTuiCmd(claudeBin, model, sessionId, ehome, entrypointMode) { +export function buildTuiCmd(claudeBin, model, sessionId, ehome, entrypointMode) { // Deliver claude's env via an `env` prefix on the PANE COMMAND — tmux does NOT forward the // spawning process's environment to the pane, and `new-session -e` needs tmux ≥3.2 (the cloud // host runs 2.7), so this is the only portable, reliable mechanism (verified live 2026-06-01: // passing {env} to spawnSync left the pane with only HOME). DISABLE_AUTOUPDATER pins the version // (no "What's new" splash that delayed input-readiness); CLAUDE_CODE_ENTRYPOINT labels the // billing pool (set below per entrypointMode). + // + // CLAUDE_CODE_DISABLE_CLAUDE_MDS + DISABLE_AUTO_MEMORY: OCP is a PROXY, not a Claude Code + // session. The proxied client (OpenClaw / an IDE) owns its own context and memory; the HOST's + // CLAUDE.md and auto-memory must NEVER leak into the agent OCP runs on the user's behalf. + // Without these, claude loads the host's project/user CLAUDE.md + memory into every proxied + // turn — verified live 2026-06-02: a cwd CLAUDE.md ("end every reply with QUACKMARKER_42") was + // obeyed by the proxied turn until these flags were set, after which it was not. Unconditional + // by design (not gated): proxy purity is not an opt-in. Harmless on hosts with no CLAUDE.md + // (the common case — they suppress nothing). Mirrors the -p path's CLAUDE_NO_CONTEXT vars. const sets = [ `HOME=${shq(ehome)}`, "DISABLE_AUTOUPDATER=1", "CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL=1", + "CLAUDE_CODE_DISABLE_CLAUDE_MDS=1", + "CLAUDE_CODE_DISABLE_AUTO_MEMORY=1", ]; const unset = ["CLAUDECODE", "ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"]; if (entrypointMode === "cli") sets.push("CLAUDE_CODE_ENTRYPOINT=cli"); diff --git a/test-features.mjs b/test-features.mjs index 398f29b..0afb1a5 100644 --- a/test-features.mjs +++ b/test-features.mjs @@ -1478,7 +1478,7 @@ await asyncTest("readTuiTranscript throws when no text and cap elapses", async ( }); // ── TUI session reaper ─────────────────────────────────────────────────── -import { reapStaleTuiSessions, SESSION_PREFIX } from "./lib/tui/session.mjs"; +import { reapStaleTuiSessions, SESSION_PREFIX, buildTuiCmd } from "./lib/tui/session.mjs"; console.log("\nTUI session reaper:"); @@ -1486,6 +1486,26 @@ test("SESSION_PREFIX is ocp-tui-", () => { assert.equal(SESSION_PREFIX, "ocp-tui-"); }); +console.log("\nTUI command construction (proxy-purity / #4):"); + +test("buildTuiCmd suppresses host CLAUDE.md + auto-memory (proxy purity, #4)", () => { + const cmd = buildTuiCmd("/usr/bin/claude", "claude-haiku", "sid-1", "/home/u", "cli"); + // OCP is a proxy: the host's CLAUDE.md / auto-memory must never leak into the proxied turn. + assert.ok(/(^| )CLAUDE_CODE_DISABLE_CLAUDE_MDS=1( |$)/.test(cmd), "must disable CLAUDE.md injection"); + assert.ok(/(^| )CLAUDE_CODE_DISABLE_AUTO_MEMORY=1( |$)/.test(cmd), "must disable auto-memory injection"); +}); + +test("buildTuiCmd keeps version pin + entrypoint label + MCP wall", () => { + const cli = buildTuiCmd("/usr/bin/claude", "m", "sid-2", "/home/u", "cli"); + assert.ok(cli.includes("DISABLE_AUTOUPDATER=1"), "version pin retained"); + assert.ok(cli.includes("CLAUDE_CODE_ENTRYPOINT=cli"), "cli mode labels the subscription pool"); + assert.ok(cli.includes("--strict-mcp-config") && cli.includes('mcp__*'), "MCP wall retained"); + // 'auto' mode must NOT pin the entrypoint (claude self-classifies via TTY). + const auto = buildTuiCmd("/usr/bin/claude", "m", "sid-3", "/home/u", "auto"); + assert.ok(!/CLAUDE_CODE_ENTRYPOINT=/.test(auto), "auto mode leaves entrypoint unset"); + assert.ok(/-u CLAUDE_CODE_ENTRYPOINT/.test(auto), "auto mode unsets any inherited entrypoint"); +}); + test("reaper kills ONLY ocp-tui- sessions, never olp-tui-", () => { const killed = []; const fakeTmux = (args) => {