import assert from "node:assert/strict"; import { execFileSync, spawn, spawnSync } from "node:child_process"; import { existsSync, readFileSync, realpathSync, statSync } from "node:fs"; import { chmod, appendFile, mkdir, mkdtemp, readFile, readdir, rm, symlink, utimes, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, beforeEach, describe, it } from "node:test"; import { buildManagedCodexHooksConfig } from "../../config/codex-hooks.js"; import { DOCUMENT_REFRESH_EXEMPTION_PREFIX } from "../../document-refresh/enforcer.js"; import { initTeamState, readTeamConfig, readTeamLeaderAttention, readTeamPhase, saveTeamConfig, writeTeamLeaderAttention, writeWorkerIdentity, } from "../../team/state.js"; import { registerTeamNotice } from "../../team/notice-ledger.js"; import { dispatchCodexNativeHook, isSloppyFallbackTranscriptStartUsable, readUnambiguousSessionStartNativeId, resolvePersistedReopenRootContext, isCodexNativeHookMainModule, looksLikeGoalCompletionPrompt, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js"; import { closeLaunchSessionBindingOnce, establishLaunchSessionBinding, finalizeBoundOnce, updateDetachedSessionMetadata, writeSessionStart, } from "../../hooks/session.js"; import { neutralizeOwnedRoutingRalplan } from '../../ralplan/documented-leader-preflight.js'; import { resetTriageConfigCache } from "../../hooks/triage-config.js"; import { executeStateOperation } from "../../state/operations.js"; import { HUD_TMUX_HEIGHT_LINES } from "../../hud/constants.js"; import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js"; import { OMX_TMUX_HUD_LEADER_PANE_ENV } from "../../hud/tmux.js"; import { readAllState } from "../../hud/state.js"; import { renderHud } from "../../hud/render.js"; import { getLegacyWikiDir, serializePage, writePage, } from "../../wiki/storage.js"; import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js"; import { createUltragoalPlan, readUltragoalPlan, } from "../../ultragoal/artifacts.js"; import { getBaseStateDir } from "../../state/paths.js"; import { maybeNudgeLeaderForAllowedWorkerStop } from "../notify-hook/team-worker-stop.js"; import { MAX_NATIVE_STDIN_JSON_BYTES } from "../hook-payload-guard.js"; const ARGUMENT_PRODUCING_RUNTIME_DENIAL_COMMANDS = [ ["node-xargs-wrapper-read", `printf x | xargs node -e "require('fs').readFileSync('src/victim.ts','utf8')"`], ] as const; const WGET_REVIEW_MUTATION_COMMANDS = [ ["wget-file-sink-without-hard-cap", "wget --no-config --no-hsts -O .omx/state/inbox/stream https://example.test/file"], ["curl-file-sink-with-timeout-but-no-hard-cap", "curl -q --max-time 1 -o .omx/state/inbox/stream https://example.test/file"], ] as const; const NATIVE_CHILD_MIXED_REFERENCE_STATE_WRITE = [ "native-child-mixed-reference-state-write", `chmod --reference=.omx/state/session.json .omx/state/reference-copy; omx state write --input '{"mode":"ultragoal"}' --json`, ] as const; const NATIVE_CHILD_REFERENCE_UNKNOWN_COMMAND = [ "native-child-reference-plus-unknown-command", "chmod --reference=.omx/state/session.json .omx/state/reference-copy; unknown-mutation-transport", ] as const; const NATIVE_CHILD_RSYNC_AUTHORITY_TARGET = [ "native-child-rsync-authority-target", "rsync .omx/state/conductor-ledger.json .omx/state/session.json", ] as const; const WGET_READ_ONLY_CONTROL_COMMANDS = [ ["wget-spider-no-body", "wget --no-config --no-hsts --spider https://example.test/file"], ["wget-stdout-output-document", "wget --no-config --no-hsts -O - https://example.test/file"], ] as const; const WGET_MAIN_METADATA_MUTATION_COMMANDS = [ ["hardlink-metadata-source-control", "ln .omx/state/conductor-ledger.json .omx/handoffs/run-1/ledger-link"], ["bounded-truncate-metadata-control", "truncate --size=16777216 .omx/state/inbox/truncate"], ] as const; const WGET_INHERITED_POSIX_COMMANDS = [ ["wget-inherited-posix-option-ordering", "wget -O src/posix-inherited-wget-owned.ts https://example.test/file -O -"], ] as const; function nativeHookScriptPath(): string { return join(process.cwd(), "dist", "scripts", "codex-native-hook.js"); } function parseSingleJsonStdout(stdout: string): Record { const trimmed = stdout.trim(); assert.notEqual(trimmed, ""); assert.equal(trimmed.split("\n").length, 1); return JSON.parse(trimmed) as Record; } function runNativeHookCli( payload: Record | string, options: { cwd?: string; env?: NodeJS.ProcessEnv } = {}, ): string { return execFileSync(process.execPath, [nativeHookScriptPath()], { cwd: options.cwd ?? process.cwd(), input: typeof payload === "string" ? payload : JSON.stringify(payload), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: options.env ?? process.env, }); } function runNativeHookCliResult( payload: Record | string, options: { cwd?: string; env?: NodeJS.ProcessEnv } = {}, ) { return spawnSync(process.execPath, [nativeHookScriptPath()], { cwd: options.cwd ?? process.cwd(), input: typeof payload === "string" ? payload : JSON.stringify(payload), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: options.env ?? process.env, }); } const OVERSIZED_STDIN_SYSTEM_MESSAGE = "OMX native hook rejected oversized stdin JSON before parsing; maxBytes=1048576."; function buildExactByteHookPayload(eventName: "PreToolUse" | "PostToolUse", byteLength: number): string { const base = JSON.stringify({ hook_event_name: eventName, session_id: `native-${eventName.toLowerCase()}-😀é`, padding: "", }); const paddingBytes = byteLength - Buffer.byteLength(base, "utf8"); assert.ok(paddingBytes >= 0); const payload = JSON.stringify({ hook_event_name: eventName, session_id: `native-${eventName.toLowerCase()}-😀é`, padding: "x".repeat(paddingBytes), }); assert.equal(Buffer.byteLength(payload, "utf8"), byteLength); assert.notEqual(payload.length, Buffer.byteLength(payload, "utf8")); return payload; } async function writeJson(path: string, value: unknown): Promise { await mkdir(dirname(path), { recursive: true }).catch(() => {}); await writeFile(path, JSON.stringify(value, null, 2)); } function readLinuxStartTicks(pid: number): number | null { try { const stat = readFileSync(`/proc/${pid}/stat`, "utf-8"); const commandEnd = stat.lastIndexOf(")"); if (commandEnd === -1) return null; const fields = stat.slice(commandEnd + 1).trim().split(/\s+/); if (fields.length <= 19) return null; const startTicks = Number(fields[19]); return Number.isInteger(startTicks) && startTicks >= 0 ? startTicks : null; } catch { return null; } } function readLinuxCmdline(pid: number): string | null { try { const text = readFileSync(`/proc/${pid}/cmdline`, "utf-8").replace(/\0+/g, " ").trim(); return text.length > 0 ? text : null; } catch { return null; } } const AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES = [ "NODE_OPTIONS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ] as const; async function withCleanAmbientNodeRuntimeEnvironment(run: () => Promise): Promise { const previousRuntimeEnv = Object.fromEntries( AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES.map((name) => [name, process.env[name]]), ); for (const name of AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES) delete process.env[name]; try { return await run(); } finally { for (const name of AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES) { if (previousRuntimeEnv[name] === undefined) delete process.env[name]; else process.env[name] = previousRuntimeEnv[name]; } } } async function writeCanonicalLeaderFixture( stateDir: string, sessionId: string, leaderThreadId: string, cwd: string, ): Promise { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: leaderThreadId, cwd, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, }, }, }, }); } async function withTrustedWorkspaceOmxCli( cwd: string, action: (omxCommand: string, trustedPath: string) => Promise, commandForm: "assignment" | "env" = "assignment", ): Promise { const cliPath = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const binDir = join(cwd, "node_modules", ".bin"); const shimPath = join(binDir, "omx"); await mkdir(binDir, { recursive: true }); await rm(shimPath, { force: true }); await symlink(cliPath, shimPath); const path = `${binDir}:${dirname(process.execPath)}:/usr/bin:/bin`; return await action(commandForm === "env" ? `env PATH="${path}" omx` : `PATH="${path}" omx`, path); } async function writeNativeMappedSessionState( cwd: string, stateDir: string, sessionId: string, nativeSessionId: string, leaderThreadId?: string, ): Promise { await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: nativeSessionId, cwd, }); if (leaderThreadId) { await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, }, }, }, }); } } async function writeLiveNativeMappedSessionState( cwd: string, stateDir: string, sessionId: string, nativeSessionId: string, leaderThreadId?: string, ): Promise { await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const liveState = await writeSessionStart(cwd, sessionId, { nativeSessionId, }); const liveStatePath = join(cwd, ".omx", "state", "session.json"); const targetStatePath = join(stateDir, "session.json"); if (liveStatePath !== targetStatePath) { await writeFile(targetStatePath, JSON.stringify(liveState, null, 2)); } if (leaderThreadId) { await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, }, }, }, }); } } async function writeLiveNativeSessionOwnerSidecar( cwd: string, stateDir: string, sessionId: string, ): Promise { const selected = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as Record; await writeJson( join(stateDir, "sessions", sessionId, "session-owner.json"), { ...selected, session_id: sessionId, native_session_id: sessionId, started_at: new Date().toISOString(), cwd, }, ); } async function withIndependentNativeSession( suffix: string, run: (fixture: { cwd: string; stateDir: string; sessionId: string; pointerBefore: string; }) => Promise, ): Promise { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-sidecar-${suffix}-`)); try { const stateDir = join(cwd, ".omx", "state"); const selectedSessionId = `native-selected-${suffix}`; const sessionId = `native-independent-${suffix}`; await writeSessionStart(cwd, selectedSessionId, { nativeSessionId: selectedSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, sessionId); await run({ cwd, stateDir, sessionId, pointerBefore: await readFile(join(stateDir, "session.json"), "utf-8"), }); } finally { await rm(cwd, { recursive: true, force: true }); } } async function writeSessionSkillActiveState( stateDir: string, sessionId: string, skill: string, phase: string, ): Promise { await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill, phase, session_id: sessionId, active_skills: [{ skill, phase, active: true, session_id: sessionId }], }, ); } async function writeIssue3239ActiveAutopilotDeepInterviewState( cwd: string, sessionId: string, threadId: string, ): Promise { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: threadId, cwd, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" }, }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "deep-interview", session_id: sessionId, thread_id: threadId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, thread_id: threadId }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, thread_id: threadId, workingDirectory: cwd, deep_interview_gate: { status: "required" }, }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, thread_id: threadId, workingDirectory: cwd, }); } async function setTeamPaneIds( cwd: string, teamName: string, paneIds: { leaderPaneId: string; workerPaneIds: Record }, ): Promise { for (const fileName of ["config.json", "manifest.v2.json"]) { const filePath = join(cwd, ".omx", "state", "team", teamName, fileName); const parsed = JSON.parse(await readFile(filePath, "utf-8")) as { leader_pane_id?: string | null; workers?: Array<{ name?: string; pane_id?: string | null }>; }; parsed.leader_pane_id = paneIds.leaderPaneId; parsed.workers = (parsed.workers ?? []).map((worker) => ({ ...worker, pane_id: worker.name ? (paneIds.workerPaneIds[worker.name] ?? worker.pane_id ?? null) : (worker.pane_id ?? null), })); await writeJson(filePath, parsed); } } async function configureAuthoritativeTeamWorker( cwd: string, teamName: string, workerPaneId = "%10", ): Promise { const stateRoot = join(cwd, ".omx", "state"); await initTeamState(teamName, "session pointer ownership regression", "executor", 1, cwd, undefined, { OMX_SESSION_ID: "leader-session", }); await setTeamPaneIds(cwd, teamName, { leaderPaneId: "%42", workerPaneIds: { "worker-1": workerPaneId }, }); for (const fileName of ["config.json", "manifest.v2.json"]) { const filePath = join(stateRoot, "team", teamName, fileName); const state = JSON.parse(await readFile(filePath, "utf-8")) as { team_state_root?: string; leader_cwd?: string; workers?: Array>; }; state.team_state_root = stateRoot; state.leader_cwd = cwd; state.workers = (state.workers ?? []).map((worker) => ({ ...worker, working_dir: cwd, worktree_path: cwd, team_state_root: stateRoot, })); await writeJson(filePath, state); } await writeJson(join(stateRoot, "team", teamName, "workers", "worker-1", "identity.json"), { name: "worker-1", index: 1, role: "executor", pane_id: workerPaneId, working_dir: cwd, worktree_path: cwd, team_state_root: stateRoot, }); process.env.TMUX = "1"; process.env.TMUX_PANE = workerPaneId; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_STATE_ROOT = stateRoot; process.env.OMX_TEAM_LEADER_CWD = cwd; process.env.OMX_SESSION_ID = "leader-session"; } async function withIsolatedHome( prefix: string, run: (homeDir: string) => Promise, ): Promise { const homeDir = await mkdtemp( join(tmpdir(), `omx-native-hook-home-${prefix}-`), ); const previousHome = process.env.HOME; try { process.env.HOME = homeDir; return await run(homeDir); } finally { if (typeof previousHome === "string") process.env.HOME = previousHome; else delete process.env.HOME; await rm(homeDir, { recursive: true, force: true }); } } async function withLoreGuardConfig( value: string, prefix: string, run: (cwd: string) => Promise, ): Promise { const cwd = await mkdtemp( join(tmpdir(), `omx-native-hook-pretool-git-commit-lore-${prefix}-`), ); const codexHome = await mkdtemp( join(tmpdir(), `omx-native-hook-codex-home-lore-${prefix}-`), ); const defaultHome = await mkdtemp( join(tmpdir(), `omx-native-hook-home-lore-${prefix}-`), ); const originalGuard = process.env.OMX_LORE_COMMIT_GUARD; const originalCodexHome = process.env.CODEX_HOME; const originalHome = process.env.HOME; try { delete process.env.OMX_LORE_COMMIT_GUARD; process.env.CODEX_HOME = codexHome; process.env.HOME = defaultHome; await writeFile( join(codexHome, "config.toml"), `[shell_environment_policy.set]\nOMX_LORE_COMMIT_GUARD = "${value}"\n`, "utf-8", ); return await run(cwd); } finally { if (originalGuard === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = originalGuard; if (originalCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = originalCodexHome; if (originalHome === undefined) delete process.env.HOME; else process.env.HOME = originalHome; await rm(cwd, { recursive: true, force: true }); await rm(codexHome, { recursive: true, force: true }); await rm(defaultHome, { recursive: true, force: true }); } } function buildWorkerStopFakeTmux( tmuxLogPath: string, options: { failSend?: boolean; busyLeader?: boolean; captureText?: string; currentCommand?: string; sendDelayMs?: number; removePathOnSend?: string; removePathOnCapture?: string; } = {}, ): string { const rawCaptureText = options.captureText ?? (options.busyLeader ? "• Working… (esc to interrupt)" : "› ready"); const captureText = `'${rawCaptureText.replace(/'/g, "'\"'\"'")}'`; const currentCommand = `'${(options.currentCommand ?? "codex").replace(/'/g, "'\"'\"'")}'`; const sendDelaySeconds = Math.max(0, options.sendDelayMs ?? 0) / 1000; const removePathOnSend = options.removePathOnSend ? `'${options.removePathOnSend.replace(/'/g, "'\"'\"'")}'` : ""; const removePathOnCapture = options.removePathOnCapture ? `'${options.removePathOnCapture.replace(/'/g, "'\"'\"'")}'` : ""; return `#!/usr/bin/env bash set -eu echo "$@" >> "${tmuxLogPath}" cmd="$1" shift || true if [[ "$cmd" == "show-option" && "\${@: -1}" == "@omx_team_pane_owner_id" ]]; then printf '%s\n' 'team:test' exit 0 fi if [[ "$cmd" == "display-message" ]]; then fmt="" while [[ "$#" -gt 0 ]]; do case "$1" in -p) ;; -t) shift ;; *) fmt="$1" ;; esac shift || true done case "$fmt" in "#{pane_in_mode}") echo "0" ;; "#{pane_id}") echo "%42" ;; "#{pane_current_path}") pwd ;; "#{pane_start_command}") echo "codex" ;; "#{pane_current_command}") printf '%s\\n' ${currentCommand} ;; "#S") echo "omx-team-worker-stop" ;; *) ;; esac exit 0 fi if [[ "$cmd" == "list-panes" ]]; then printf '%%10\t0\t12310\n%%11\t0\t12311\n%%42\t0\t12345\n' exit 0 fi if [[ "$cmd" == "capture-pane" ]]; then ${removePathOnCapture ? `rm -rf ${removePathOnCapture}` : ""} printf '%s\\n' ${captureText} exit 0 fi if [[ "$cmd" == "set-buffer" ]]; then printf '%s' "\${@: -1}" > "${tmuxLogPath}.buffer" exit 0 fi if [[ "$cmd" == "show-buffer" ]]; then if [[ -f "${tmuxLogPath}.buffer" ]]; then cat "${tmuxLogPath}.buffer"; fi exit 0 fi if [[ "$cmd" == "paste-buffer" ]]; then target="" while [[ "$#" -gt 0 ]]; do case "$1" in -t) target="$2"; shift 2 ;; *) shift ;; esac done if [[ -f "${tmuxLogPath}.buffer" ]]; then echo "send-keys -t \${target} -l $(cat "${tmuxLogPath}.buffer")" >> "${tmuxLogPath}" fi exit 0 fi if [[ "$cmd" == "delete-buffer" ]]; then rm -f "${tmuxLogPath}.buffer" exit 0 fi if [[ "$cmd" == "send-keys" ]]; then ${sendDelaySeconds > 0 ? `sleep ${sendDelaySeconds}` : ""} ${removePathOnSend ? `rm -rf ${removePathOnSend}` : ""} ${options.failSend ? "exit 1" : "exit 0"} fi exit 0 `; } function buildSessionOwnerEvidenceTmux(paneInstanceId: string, sessionInstanceId = ""): string { return `#!/usr/bin/env bash set -eu case "\${1:-}" in display-message) printf '%s\n' "omx-owner-evidence" ;; show-option|show-options) case "\${@: -1}" in @omx_pane_instance_id) printf '%s\n' "${paneInstanceId}" ;; @omx_instance_id) printf '%s\n' "${sessionInstanceId}" ;; esac ;; esac `; } async function initTempGitRepo(prefix: string): Promise { const cwd = await mkdtemp(join(tmpdir(), prefix)); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore", }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore", }); return cwd; } async function writeActiveAutopilotSession( cwd: string, sessionId: string, ): Promise { await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, }); await writeJson( join(cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json"), { active: true, current_phase: "execution", }, ); } async function writeHookCounterPlugin(cwd: string): Promise { const markerPath = join(cwd, ".omx", "stop-hook-counter.json"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "count-stop-hook.mjs"), `import { mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; export async function onHookEvent(event) { if (event.event !== "stop") return; const outPath = join(process.cwd(), ".omx", "stop-hook-counter.json"); await mkdir(dirname(outPath), { recursive: true }); let count = 0; try { count = JSON.parse(await readFile(outPath, "utf-8")).count || 0; } catch {} await writeFile(outPath, JSON.stringify({ count: count + 1 }, null, 2)); } `, "utf-8", ); return markerPath; } async function writeReleaseReadinessLeaderAttention( teamName: string, sessionId: string, cwd: string, options: { workRemaining: boolean }, ): Promise { await writeTeamLeaderAttention( teamName, { team_name: teamName, updated_at: "2026-04-12T17:20:00.000Z", source: "notify_hook", leader_decision_state: "done_waiting_on_leader", leader_attention_pending: true, leader_attention_reason: "leader_session_stopped", attention_reasons: ["leader_session_stopped"], leader_stale: true, leader_session_active: false, leader_session_id: sessionId, leader_session_stopped_at: "2026-04-12T17:20:00.000Z", unread_leader_message_count: 0, work_remaining: options.workRemaining, stalled_for_ms: null, }, cwd, ); } async function writeReleaseReadinessStateMarker( sessionId: string, teamName: string, cwd: string, ): Promise { await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "release-readiness-state.json", ), { active: true, session_id: sessionId, team_name: teamName, stable_final_recommendation_emitted: true, }, ); } const TEAM_STOP_COMMIT_GUIDANCE = " If system-generated worker auto-checkpoint commits exist, rewrite them into Lore-format final commits before merge/finalization."; const DEFAULT_AUTO_NUDGE_RESPONSE = "continue with the current task only if it is already authorized"; const TEAM_ENV_KEYS = [ "OMX_TEAM_WORKER", "OMX_TEAM_INTERNAL_WORKER", "OMX_TEAM_STATE_ROOT", "OMX_TEAM_LEADER_CWD", "OMX_TEAM_MODE", "OMX_SESSION_ID", "OMX_ROOT", "OMX_STATE_ROOT", "SESSION_ID", "OMX_QUESTION_RETURN_PANE", "OMX_LEADER_PANE_ID", "TMUX", "TMUX_PANE", "OMX_TMUX_HUD_OWNER", "OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS", "OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS", ] as const; const priorTeamEnv = new Map< (typeof TEAM_ENV_KEYS)[number], string | undefined >(); beforeEach(() => { priorTeamEnv.clear(); for (const key of TEAM_ENV_KEYS) { priorTeamEnv.set(key, process.env[key]); delete process.env[key]; } }); afterEach(() => { for (const key of TEAM_ENV_KEYS) { const value = priorTeamEnv.get(key); if (typeof value === "string") process.env[key] = value; else delete process.env[key]; } priorTeamEnv.clear(); }); describe("codex native hook config", () => { it("builds the expected managed hooks.json shape", () => { const config = buildManagedCodexHooksConfig("/tmp/omx"); assert.deepEqual(Object.keys(config.hooks), [ "SessionStart", "PreToolUse", "PostToolUse", "UserPromptSubmit", "PreCompact", "PostCompact", "Stop", ]); const sessionStart = config.hooks.SessionStart[0] as { matcher?: string; hooks?: Array>; }; assert.equal(sessionStart.matcher, "startup|resume|clear"); assert.equal(sessionStart.hooks?.[0]?.statusMessage, undefined); const preToolUse = config.hooks.PreToolUse[0] as { matcher?: string; hooks?: Array>; }; assert.equal(preToolUse.matcher, undefined); assert.match( String(preToolUse.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.equal(preToolUse.hooks?.[0]?.statusMessage, undefined); const postToolUse = config.hooks.PostToolUse[0] as { matcher?: string; hooks?: Array>; }; assert.equal(postToolUse.matcher, undefined); assert.match( String(postToolUse.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.equal(postToolUse.hooks?.[0]?.statusMessage, undefined); const userPromptSubmit = config.hooks.UserPromptSubmit[0] as { matcher?: string; hooks?: Array>; }; assert.equal(userPromptSubmit.matcher, undefined); assert.match( String(userPromptSubmit.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.equal(userPromptSubmit.hooks?.[0]?.statusMessage, undefined); const stop = config.hooks.Stop[0] as { hooks?: Array>; }; assert.equal(stop.hooks?.[0]?.timeout, 30); const postCompact = config.hooks.PostCompact[0] as { matcher?: string; hooks?: Array>; }; assert.equal(postCompact.matcher, undefined); assert.match( String(postCompact.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.doesNotMatch( String(postCompact.hooks?.[0]?.command || ""), /PostCompact Nudge|additionalContext|printf/, ); }); }); describe("codex native hook dispatch", { concurrency: false }, () => { it("treats space-containing argv entry paths as the main module", () => { const entryPath = "/tmp/omx native/codex-native-hook.js"; assert.equal( isCodexNativeHookMainModule(pathToFileURL(entryPath).href, entryPath), true, ); }); it("does not treat a different module url as the main module", () => { assert.equal( isCodexNativeHookMainModule( pathToFileURL("/tmp/omx native/other-script.js").href, "/tmp/omx native/codex-native-hook.js", ), false, ); }); it("emits Stop-schema-safe block JSON when unidentifiable malformed stdin has native Stop runtime surface", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-stop-surface-"), ); try { await mkdir(join(cwd, ".omx"), { recursive: true }); const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: "{", encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout) as { decision?: string; continue?: boolean; reason?: string; stopReason?: string; systemMessage?: string; hookSpecificOutput?: unknown; }; assert.equal(output.decision, "block"); assert.equal(output.continue, undefined); assert.equal( output.reason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.", ); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); assert.equal(output.hookSpecificOutput, undefined); assert.match( String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves non-Stop fail-closed JSON when malformed stdin identifies a non-Stop hook", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-nonstop-"), ); try { await mkdir(join(cwd, ".omx"), { recursive: true }); const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: '{hook_event_name:"PreToolUse",', encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout) as { continue?: boolean; decision?: string; stopReason?: string; systemMessage?: string; hookSpecificOutput?: unknown; }; const hookSpecificOutput = output.hookSpecificOutput as { hookEventName?: string; permissionDecision?: string; permissionDecisionReason?: string; } | undefined; assert.equal(output.continue, undefined); assert.equal(output.decision, undefined); assert.equal(output.stopReason, undefined); assert.equal(hookSpecificOutput?.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput?.permissionDecision, "deny"); assert.equal( hookSpecificOutput?.permissionDecisionReason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.", ); assert.match( String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("redacts unterminated prompt-like malformed stdin fields", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-unterminated-"), ); try { const privatePrompt = "PRIVATE_UNTERMINATED_PROMPT"; const malformed = `{hook_event_name:"PostToolUse", prompt:"${privatePrompt}`; const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: malformed, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); const log = await readFile( join( cwd, ".omx", "logs", `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`, ), "utf-8", ); const entry = JSON.parse(log.trim()) as Record; const prefix = String(entry.raw_input_prefix ?? ""); assert.doesNotMatch(prefix, new RegExp(privatePrompt)); assert.match(prefix, /prompt:"\[REDACTED\]"/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("logs a bounded redacted raw stdin prefix when CLI stdin is malformed", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-log-prefix-"), ); try { const secret = "sk-test-secret123456"; const promptText = "summarize private launch notes"; const malformed = `{hook_event_name:"PostToolUse", access_token:"${secret}", prompt:"${promptText}", text:"${promptText}", bad:"${"x".repeat(400)}"}${String.fromCharCode(10, 0, 7)}`; const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: malformed, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); const log = await readFile( join( cwd, ".omx", "logs", `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`, ), "utf-8", ); const entry = JSON.parse(log.trim()) as Record; const prefix = String(entry.raw_input_prefix ?? ""); assert.equal(entry.type, "native_hook_stdin_parse_error"); assert.equal( entry.raw_input_length, Buffer.byteLength(malformed, "utf-8"), ); assert.ok( prefix.length <= 240, `prefix should be bounded, got ${prefix.length}`, ); assert.doesNotMatch(prefix, /[\u0000-\u001f\u007f-\u009f]/); assert.doesNotMatch(prefix, new RegExp(secret)); assert.doesNotMatch(prefix, new RegExp(promptText)); assert.match(prefix, /\[REDACTED\]/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits Stop-schema-safe block JSON when malformed stdin still identifies Stop", () => { const stdout = runNativeHookCli('{hook_event_name:"Stop",'); const output = parseSingleJsonStdout(stdout) as { decision?: string; reason?: string; stopReason?: string; systemMessage?: string; hookSpecificOutput?: unknown; }; assert.equal(output.decision, "block"); assert.equal( output.reason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.", ); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); assert.equal(output.hookSpecificOutput, undefined); assert.match( String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/, ); }); it("emits no-op JSON stdout for PreToolUse non-Bash tools with null output", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-pretool-nonbash-noop-"), ); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-nonbash-noop", thread_id: "thread-cli-pretool-nonbash-noop", turn_id: "turn-cli-pretool-nonbash-noop", tool_name: "Read", tool_input: { file_path: "package.json" }, }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); assert.deepEqual(parseSingleJsonStdout(result.stdout), {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits PreToolUse CLI advisory JSON with only systemMessage", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-pretool-schema-safe-"), ); try { const output = parseSingleJsonStdout( runNativeHookCli( { hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-schema-safe", thread_id: "thread-cli-pretool-schema-safe", turn_id: "turn-cli-pretool-schema-safe", tool_name: "Bash", tool_input: { command: "rm -rf dist" }, }, { cwd }, ), ); assert.deepEqual(output, { systemMessage: "Destructive Bash command detected (`rm -rf dist`). Confirm the target and expected side effects before running it.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits PreToolUse CLI block JSON as hook-specific deny with preserved systemMessage guidance", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-pretool-block-schema-safe-"), ); try { const result = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-block-schema-safe", thread_id: "thread-cli-pretool-block-schema-safe", turn_id: "turn-cli-pretool-block-schema-safe", tool_name: "Bash", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "fix tests"', }, }, { cwd }, ); assert.equal(result.status, 0, result.stderr || result.stdout); const output = parseSingleJsonStdout(result.stdout); const hookSpecificOutput = output.hookSpecificOutput as Record< string, unknown >; assert.deepEqual(Object.keys(output).sort(), [ "hookSpecificOutput", "systemMessage", ]); assert.match(String(output.systemMessage ?? ""), /Lore protocol/); assert.equal(output.decision, undefined); assert.equal(output.reason, undefined); assert.equal(output.stopReason, undefined); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.equal( hookSpecificOutput.permissionDecisionReason, "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves ralplan PreToolUse planning guard as hook-specific deny JSON", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-ralplan-pretool-boundary-"), ); const sessionId = "sess-cli-ralplan-pretool-boundary"; const stateDir = join(cwd, ".omx", "state"); try { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: sessionId, }, ); await writeCanonicalLeaderFixture( stateDir, sessionId, "thread-cli-ralplan-pretool-boundary", cwd, ); const result = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-cli-ralplan-pretool-boundary", agent_id: "thread-cli-ralplan-pretool-boundary", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b", }, }, { cwd }, ); assert.equal(result.status, 0, result.stderr || result.stdout); const output = parseSingleJsonStdout(result.stdout); const hookSpecificOutput = output.hookSpecificOutput as Record< string, unknown >; assert.deepEqual(Object.keys(output).sort(), ["hookSpecificOutput"]); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /Ralplan is active \(phase: critic-review\)/, ); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /implementation\/write tools are blocked/, ); assert.match( String(hookSpecificOutput.additionalContext ?? ""), /Write only planning artifacts/, ); assert.equal(output.decision, undefined); assert.equal(output.reason, undefined); assert.equal(output.systemMessage, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Ralplan Markdown draft-only apply_patch on the live CLI path while denying mixed targets", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-ralplan-draft-boundary-"), ); const sessionId = "sess-cli-ralplan-draft-boundary"; const stateDir = join(cwd, ".omx", "state"); try { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }, ); await writeCanonicalLeaderFixture( stateDir, sessionId, "thread-cli-ralplan-draft-boundary", cwd, ); for (const [name, target] of [ ["relative", ".omx/drafts/issue-3105.md"], ["repository-absolute", join(cwd, ".omx", "drafts", "issue-3105.md")], ] as const) { const result = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-cli-ralplan-draft-boundary", agent_id: "thread-cli-ralplan-draft-boundary", tool_name: "apply_patch", tool_input: { input: `*** Begin Patch\n*** Add File: ${target}\n+# Draft\n*** End Patch\n`, }, }, { cwd }, ); assert.equal(result.status, 0, result.stderr || result.stdout); assert.deepEqual(parseSingleJsonStdout(result.stdout), {}); } const mixedResult = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-cli-ralplan-draft-boundary", agent_id: "thread-cli-ralplan-draft-boundary", tool_name: "apply_patch", tool_input: { input: "*** Begin Patch\n*** Add File: .omx/drafts/issue-3105.md\n+# Draft\n*** Add File: src/leak.ts\n+leak\n*** End Patch\n", }, }, { cwd }, ); assert.equal( mixedResult.status, 0, mixedResult.stderr || mixedResult.stdout, ); const mixedOutput = parseSingleJsonStdout(mixedResult.stdout); const hookSpecificOutput = mixedOutput.hookSpecificOutput as Record< string, unknown >; assert.deepEqual(Object.keys(mixedOutput).sort(), ["hookSpecificOutput"]); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /src\/leak\.ts/, ); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /implementation\/write tools are blocked/, ); assert.equal(mixedOutput.decision, undefined); assert.equal(mixedOutput.reason, undefined); assert.equal(mixedOutput.systemMessage, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("rejects unauthenticated typed Team-worker claims before planning guards", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-team-worker-typed-pretool-exempt-"), ); const sessionId = "sess-team-worker-typed-pretool-exempt"; const stateDir = join(cwd, ".omx", "state"); const basePayload = { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-team-worker-typed-pretool-exempt", agent_role: "executor", tool_name: "Edit", tool_use_id: "tool-team-worker-typed-pretool-exempt", tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b", }, }; try { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: sessionId, }, ); const nonTeamWorkerTypedSubagent = await dispatchCodexNativeHook( basePayload, { cwd }, ); assert.equal( (nonTeamWorkerTypedSubagent.outputJson as { decision?: string } | null) ?.decision, "block", "typed/native subagent PreToolUse without trusted thread_spawn provenance must remain protected outside team workers", ); process.env.OMX_TEAM_INTERNAL_WORKER = "typed-pretool-exempt/worker-1"; process.env.OMX_TEAM_WORKER = "typed-pretool-exempt/worker-1"; const teamWorkerTypedSubagent = await dispatchCodexNativeHook( basePayload, { cwd }, ); assert.equal( (teamWorkerTypedSubagent.outputJson as { decision?: string } | null) ?.decision, "block", "environment-only Team-worker claims must not bypass planning guards", ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves deep-interview PreToolUse planning guard as hook-specific deny JSON", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-deep-interview-pretool-boundary-"), ); const sessionId = "sess-cli-deep-interview-pretool-boundary"; const stateDir = join(cwd, ".omx", "state"); try { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, }, ); const result = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-cli-deep-interview-pretool-boundary", tool_name: "Write", tool_input: { file_path: "src/runtime.ts", content: "export const changed = true;\n", }, }, { cwd }, ); assert.equal(result.status, 0, result.stderr || result.stdout); const output = parseSingleJsonStdout(result.stdout); const hookSpecificOutput = output.hookSpecificOutput as Record< string, unknown >; assert.deepEqual(Object.keys(output).sort(), ["hookSpecificOutput"]); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /Deep-interview is active \(phase: intent-first\)/, ); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /implementation\/write tools are blocked/, ); assert.match( String(hookSpecificOutput.additionalContext ?? ""), /requirements\/spec mode/, ); assert.equal(output.decision, undefined); assert.equal(output.reason, undefined); assert.equal(output.systemMessage, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("synthesizes a deny for malformed explicit PreToolUse blocks instead of downgrading systemMessage to advisory", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-pretool-block-"), ); try { for (const malformedBlockShape of ["legacy", "deny"] as const) { const result = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: `sess-cli-malformed-pretool-${malformedBlockShape}`, thread_id: `thread-cli-malformed-pretool-${malformedBlockShape}`, tool_name: "Bash", tool_input: { command: "pwd" }, }, { cwd, env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_MALFORMED_PRETOOL_BLOCK: malformedBlockShape, }, }, ); assert.equal(result.status, 0, result.stderr || result.stdout); const output = parseSingleJsonStdout(result.stdout); const hookSpecificOutput = output.hookSpecificOutput as Record< string, unknown >; assert.deepEqual(Object.keys(output).sort(), [ "hookSpecificOutput", "systemMessage", ]); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.equal( hookSpecificOutput.permissionDecisionReason, String(output.systemMessage ?? "").trim(), ); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps wrapped ralplan implementation writes blocked at raw classification while allowing planning artifacts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ralplan-wrapper-implementation-block-"), ); const sessionId = "sess-ralplan-wrapper-implementation-block"; const stateDir = join(cwd, ".omx", "state"); try { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: sessionId, }, ); await writeCanonicalLeaderFixture( stateDir, sessionId, "thread-ralplan-wrapper-implementation-block", cwd, ); const blockedWrapperCommand = "bash -lc \"cat > src/scripts/__tests__/codex-native-hook.test.ts <<'EOF'\nexport const wrappedRalplanMutation = true;\nEOF\""; const blockedWrapper = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", agent_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-implementation-block", tool_input: { command: blockedWrapperCommand }, }, { cwd }, ); assert.equal( blockedWrapper.outputJson && typeof blockedWrapper.outputJson === "object" ? (blockedWrapper.outputJson as { decision?: string }).decision : undefined, "block", ); assert.match( JSON.stringify(blockedWrapper.outputJson), /Ralplan is active \(phase: critic-review\)/, ); assert.match( JSON.stringify(blockedWrapper.outputJson), /implementation\/write tools are blocked/, ); const allowedPlanningArtifactWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", agent_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Write", tool_use_id: "tool-ralplan-wrapper-planning-artifact", tool_input: { file_path: ".omx/context/ralplan-wrapper-notes.md", content: "# Planning notes\n", }, }, { cwd }, ); assert.equal(allowedPlanningArtifactWrite.outputJson, null); const allowedPlanningTmpWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", agent_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Write", tool_use_id: "tool-ralplan-wrapper-planning-tmp", tool_input: { file_path: ".omx/tmp/sess-ralplan-wrapper/notes.md", content: "# Scratch notes\n", }, }, { cwd }, ); assert.equal(allowedPlanningTmpWrite.outputJson, null); const blockedPlanningTmpScriptWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Write", tool_use_id: "tool-ralplan-wrapper-planning-tmp-script-write", tool_input: { file_path: ".omx/tmp/sess-ralplan-wrapper/run.sh", content: "printf pwned > src/pwned.ts\n", }, }, { cwd }, ); assert.equal( ( blockedPlanningTmpScriptWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify(blockedPlanningTmpScriptWrite.outputJson), /\.omx\/tmp|planning artifact paths/, ); const blockedPlanningTmpScriptExecution = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-planning-tmp-script-exec", tool_input: { command: "sh .omx/tmp/sess-ralplan-wrapper/run.sh" }, }, { cwd }, ); assert.equal( ( blockedPlanningTmpScriptExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify(blockedPlanningTmpScriptExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); const blockedPlanningTmpExtensionlessExecution = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-planning-tmp-extensionless-exec", tool_input: { command: "./.omx/tmp/sess-ralplan-wrapper/generated", }, }, { cwd }, ); assert.equal( ( blockedPlanningTmpExtensionlessExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify(blockedPlanningTmpExtensionlessExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); const blockedPlanningTmpVersionedInterpreterExecution = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-planning-tmp-versioned-python-exec", tool_input: { command: "python3.12 .omx/tmp/sess-ralplan-wrapper/generated.txt", }, }, { cwd }, ); assert.equal( ( blockedPlanningTmpVersionedInterpreterExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify( blockedPlanningTmpVersionedInterpreterExecution.outputJson, ), /generated-script transport|\.omx\/tmp/, ); const blockedPlanningTmpTsxExecution = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-planning-tmp-tsx-exec", tool_input: { command: "tsx .omx/tmp/sess-ralplan-wrapper/generated.ts", }, }, { cwd }, ); assert.equal( ( blockedPlanningTmpTsxExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify(blockedPlanningTmpTsxExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); const blockedPlanningTmpTsxWithOptionsExecution = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-planning-tmp-tsx-options-exec", tool_input: { command: "tsx --tsconfig tsconfig.json watch .omx/tmp/sess-ralplan-wrapper/generated.ts", }, }, { cwd }, ); assert.equal( ( blockedPlanningTmpTsxWithOptionsExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify(blockedPlanningTmpTsxWithOptionsExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); const allowedBeadsMetadataWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Write", tool_use_id: "tool-ralplan-wrapper-beads-metadata", tool_input: { file_path: ".beads/ralplan-wrapper.json", content: "{}\n", }, }, { cwd }, ); assert.equal(allowedBeadsMetadataWrite.outputJson, null); const allowedQuotedMention = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-quoted-mention", tool_input: { command: "printf '%s\\n' 'src/scripts/__tests__/codex-native-hook.test.ts'", }, }, { cwd }, ); assert.equal(allowedQuotedMention.outputJson, null); const blockedPythonPlanningArtifactExecution = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-python-planning-artifact-exec", tool_input: { command: `python3 - <<'PY' from pathlib import Path Path('.omx/plans/run.sh').write_text('echo ran') PY sh .omx/plans/run.sh`, }, }, { cwd }, ); assert.equal( blockedPythonPlanningArtifactExecution.outputJson && typeof blockedPythonPlanningArtifactExecution.outputJson === "object" ? ( blockedPythonPlanningArtifactExecution.outputJson as { decision?: string; } ).decision : undefined, "block", ); assert.match( JSON.stringify(blockedPythonPlanningArtifactExecution.outputJson), /same-command|Bash write intent|implementation/i, ); const blockedPythonAllowedMkdirDynamicSourceWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-python-allowed-mkdir-dynamic-source-write", tool_input: { command: `python3 - <<'PY' from pathlib import Path Path('.omx/plans').mkdir(parents=True, exist_ok=True) (Path('src') / 'generated.ts').write_text('implementation') PY`, }, }, { cwd }, ); assert.equal( blockedPythonAllowedMkdirDynamicSourceWrite.outputJson && typeof blockedPythonAllowedMkdirDynamicSourceWrite.outputJson === "object" ? ( blockedPythonAllowedMkdirDynamicSourceWrite.outputJson as { decision?: string; } ).decision : undefined, "block", ); assert.match( JSON.stringify(blockedPythonAllowedMkdirDynamicSourceWrite.outputJson), /write intent did not identify an allowed planning artifact path/, ); const blockedPythonSourceWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-python-source-write", tool_input: { command: `python3 - <<'PY' from pathlib import Path Path('src/generated.ts').write_text('implementation') PY`, }, }, { cwd }, ); assert.equal( blockedPythonSourceWrite.outputJson && typeof blockedPythonSourceWrite.outputJson === "object" ? (blockedPythonSourceWrite.outputJson as { decision?: string }) .decision : undefined, "block", ); assert.match( JSON.stringify(blockedPythonSourceWrite.outputJson), /Bash .* target src\/generated\.ts/, ); const blockedPythonMixedWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-wrapper-implementation-block", tool_name: "Bash", tool_use_id: "tool-ralplan-wrapper-python-mixed-write", tool_input: { command: `python3 - <<'PY' from pathlib import Path Path('.omx/plans/rebase-pr3010-ultragoal-fix-plan.md').write_text('planning text') Path('src/generated.ts').write_text('implementation') PY`, }, }, { cwd }, ); assert.equal( blockedPythonMixedWrite.outputJson && typeof blockedPythonMixedWrite.outputJson === "object" ? (blockedPythonMixedWrite.outputJson as { decision?: string }) .decision : undefined, "block", ); assert.match( JSON.stringify(blockedPythonMixedWrite.outputJson), /Bash .* target src\/generated\.ts/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks deep-interview PreToolUse implementation writes when terminal Autopilot run-state shadows stale active state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-terminal-pretool-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-deep-interview-terminal-pretool"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, }, ); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-30T00:00:00.000Z", updated_at: "2026-05-30T00:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-deep-interview-terminal-pretool", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( JSON.stringify(result.outputJson), /Deep-interview is active \(phase: intent-first\)/, ); assert.match( JSON.stringify(result.outputJson), /implementation\/write tools are blocked/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits parseable no-op JSON stdout for inactive Stop CLI runs", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-noop-json-"), ); try { const stdout = runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-noop-json", thread_id: "thread-cli-stop-noop-json", turn_id: "turn-cli-stop-noop-json", }, { cwd }, ); const output = parseSingleJsonStdout(stdout); assert.deepEqual(output, {}); assert.equal(existsSync(join(cwd, ".omx", "state")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON stdout for Stop payloads with no runtime output", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-null-output-"), ); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-null-output", thread_id: "thread-cli-stop-null-output", turn_id: "turn-cli-stop-null-output", stop_hook_active: true, }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); assert.deepEqual(parseSingleJsonStdout(result.stdout), {}); assert.equal(existsSync(join(cwd, ".omx", "state")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON for oversized Stop stdin without parsing or creating inactive state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-"), ); try { const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-oversized", transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); const stdout = runNativeHookCli(oversizedStop, { cwd }); assert.deepEqual(parseSingleJsonStdout(stdout), {}); assert.equal(existsSync(join(cwd, ".omx", "state")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("drains oversized stdin without breaking the parent writer pipe", async () => { const child = spawn(process.execPath, [nativeHookScriptPath()], { stdio: ["pipe", "ignore", "ignore"], }); const payload = JSON.stringify({ hook_event_name: "UserPromptSubmit", prompt: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES * 2), }); const exitCode = new Promise((resolve) => { child.once("close", resolve); }); const writeError = await new Promise((resolve) => { child.stdin.once("error", resolve); child.stdin.end(payload, () => resolve(null)); }); assert.equal(writeError, null); assert.equal(await exitCode, 0); }); it("blocks oversized Stop stdin when current session autopilot is active", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-active-"), ); try { await writeActiveAutopilotSession(cwd, "sess-cli-stop-oversized-active"); const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "native-session-hidden-by-oversized-payload", transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); const output = parseSingleJsonStdout( runNativeHookCli(oversizedStop, { cwd }), ) as { decision?: string; stopReason?: string; systemMessage?: string; }; assert.equal(output.decision, "block"); assert.equal( output.stopReason, "native_stop_stdin_oversized_active_workflow", ); assert.match( String(output.systemMessage ?? ""), /active current-session workflow state/, ); assert.equal(existsSync(join(cwd, ".omx", "logs")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON for oversized Stop stdin for unrelated root autopilot state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-stale-root-"), ); try { await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: "sess-current-without-active-autopilot", cwd, }); await writeJson(join(cwd, ".omx", "state", "autopilot-state.json"), { active: true, current_phase: "execution", }); const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); assert.deepEqual( parseSingleJsonStdout(runNativeHookCli(oversizedStop, { cwd })), {}, ); assert.equal(existsSync(join(cwd, ".omx", "logs")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON for oversized Stop stdin when terminal run-state shadows stale autopilot state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-terminal-run-"), ); try { const sessionId = "sess-cli-stop-oversized-terminal-run"; await writeActiveAutopilotSession(cwd, sessionId); await writeJson( join(cwd, ".omx", "state", "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-20T11:00:00.000Z", updated_at: "2026-05-20T11:00:00.000Z", }, ); const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); assert.deepEqual( parseSingleJsonStdout(runNativeHookCli(oversizedStop, { cwd })), {}, ); assert.equal(existsSync(join(cwd, ".omx", "logs")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("accepts exact UTF-8 byte-limit PreToolUse and PostToolUse payloads and rejects only larger input", () => { for (const eventName of ["PreToolUse", "PostToolUse"] as const) { for (const byteLength of [MAX_NATIVE_STDIN_JSON_BYTES - 1, MAX_NATIVE_STDIN_JSON_BYTES]) { const result = runNativeHookCliResult(buildExactByteHookPayload(eventName, byteLength)); assert.equal(result.status, 0, result.stderr || result.stdout); assert.doesNotMatch(result.stdout, /native_hook_stdin_oversized/); } const result = runNativeHookCliResult(buildExactByteHookPayload(eventName, MAX_NATIVE_STDIN_JSON_BYTES + 1)); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const expected = eventName === "PreToolUse" ? { systemMessage: OVERSIZED_STDIN_SYSTEM_MESSAGE, hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: OVERSIZED_STDIN_SYSTEM_MESSAGE, }, } : { continue: false, stopReason: "native_hook_stdin_oversized", systemMessage: OVERSIZED_STDIN_SYSTEM_MESSAGE, }; assert.deepEqual(parseSingleJsonStdout(result.stdout), expected); } }); it("fails closed for oversized non-Stop stdin before parsing", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-nonstop-oversized-"), ); try { const oversizedPrompt = JSON.stringify({ hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-cli-prompt-oversized", prompt: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); const output = parseSingleJsonStdout( runNativeHookCli(oversizedPrompt, { cwd }), ) as { continue?: boolean; stopReason?: string; systemMessage?: string; }; assert.equal(output.continue, false); assert.equal(output.stopReason, "native_hook_stdin_oversized"); assert.match( String(output.systemMessage ?? ""), /rejected oversized stdin JSON before parsing/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves all structural blockers through raw UserPromptSubmit state and Stop", async () => { const cases = [ { name: "unsafe-token-boundary", prompt: "$ralplan\u200B.md", expectedSkill: null }, { name: "at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "fullwidth-at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "fullwidth-hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "fullwidth-equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "directive-use-ralplan", prompt: "use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-please-use-ralplan", prompt: "please use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-run-ralplan", prompt: "run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-list-use-ralplan", prompt: "- use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-documentation-then-command", prompt: "use $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-then-implicit-command", prompt: "use $ralplan is the consensus-planning command\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-trailing-prose-then-command", prompt: "use $ralplan is the workflow command for planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-prose", prompt: "use $ralplan is the workflow command for autopilot mode", expectedSkill: null }, { name: "directive-documentation-alias-prose", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.", expectedSkill: null }, { name: "directive-coordinated-documentation-then-command", prompt: "- use $ralplan and $autopilot are workflow commands\n$ralph execute it", expectedSkill: "ralph" }, { name: "directive-documentation-semicolon-directive", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-two-documentation-blocks", prompt: "use $ralplan is the consensus-planning command\nuse $autopilot is the autonomous workflow command\n$ralph execute it", expectedSkill: "ralph" }, { name: "directive-documentation-embedded-token-then-command", prompt: "use $ralplan is the workflow command for $team\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-task-noun-followup", prompt: "use $ralplan is the workflow command; use $autopilot update the documentation", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-semicolon-followup", prompt: "use $ralplan is the consensus-planning command; use autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-transition-followup", prompt: "use $ralplan is the consensus-planning command; then use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-explicit-alias", prompt: "use $ralplan is the consensus-planning command; $team is its alias", expectedSkill: null }, { name: "directive-documentation-implicit-chain", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.\n$ralph execute it", expectedSkill: "ralph" }, { name: "directive-documentation-fullwidth-separator", prompt: "use $ralplan,$autopilot are workflow commands", expectedSkill: null }, { name: "directive-documentation-compact-slash", prompt: "use $ralplan/$autopilot are workflow commands\n$ralph execute it", expectedSkill: "ralph" }, { name: "reference-prompts-title-then-command", prompt: "[docs]: /target \"title\nUse /prompts:architect\"\n$ralph execute it", expectedSkill: "ralph" }, { name: "doc-period-implicit", prompt: "use $ralplan is the consensus-planning command. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-bare-implicit", prompt: "use $ralplan is the consensus-planning command; autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-fullwidth-semicolon", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-but-followup", prompt: "use $ralplan is the workflow command; but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fullwidth-oxford", prompt: "use $ralplan, $autopilot, and $team are workflow commands", expectedSkill: null }, { name: "reference-zero-title", prompt: "[docs]: ./target\n(autopilot mode)", expectedSkill: null }, { name: "doc-also-alias-explicit", prompt: "use $ralplan is the consensus-planning command; $team is also its alias", expectedSkill: null }, { name: "doc-also-alias-implicit", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is also its alias.", expectedSkill: null }, { name: "doc-embedded-mention", prompt: "use $ralplan is the workflow command; $team appears in the documentation.", expectedSkill: null }, { name: "chained-negation", prompt: "$ralplan; $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "long-negation", prompt: `$ralplan; $autopilot${" ".repeat(193)}is prohibited`, expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "doc-arabic-comma", prompt: "use $ralplan، $autopilot are workflow commands", expectedSkill: null }, { name: "arabic-negation", prompt: "$ralplan، $autopilot are prohibited", expectedSkill: null }, { name: "implicit-arabic-negation", prompt: "Autopilot mode، deep interview are prohibited.", expectedSkill: null }, { name: "fullwidth-frame-reset", prompt: "For instance: manual mode is slower。 Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-abbreviation", prompt: "use $ralplan is the workflow command, e.g. use $autopilot in examples.", expectedSkill: null }, { name: "implicit-doc-mention", prompt: "use $ralplan is the workflow command; autopilot mode appears in the documentation.", expectedSkill: null }, { name: "implicit-doc-chain", prompt: "use $ralplan is the workflow command; autopilot mode is its alias; $ralph execute it", expectedSkill: "ralph" }, { name: "long-command-gap", prompt: `use $ralplan is the workflow command; use${" ".repeat(161)}$autopilot build it`, expectedSkill: "autopilot" }, { name: "ideographic-negation", prompt: "$ralplan、 $autopilot are prohibited", expectedSkill: null }, { name: "implicit-ideographic-negation", prompt: "Autopilot mode、 deep interview are prohibited.", expectedSkill: null }, { name: "doc-exclamation-followup", prompt: "use $ralplan is the consensus-planning command! run $autopilot", expectedSkill: "autopilot" }, { name: "doc-fullwidth-question-followup", prompt: "use $ralplan is the consensus-planning command? run $autopilot", expectedSkill: "autopilot" }, { name: "implicit-doc-predecessor", prompt: "Autopilot mode is workflow documentation.\n$ralph execute it", expectedSkill: "ralph" }, { name: "confusable-use-verb", prompt: "uſe $ralplan plan it", expectedSkill: null }, { name: "confusable-please-prefix", prompt: "pleaſe use $ralplan plan it", expectedSkill: null }, { name: "confusable-prompts-token-then-command", prompt: "/promptſ:architect; use autopilot mode.", expectedSkill: "autopilot" }, { name: "reserved-em-dash-boundary", prompt: "/prompts:architect— use autopilot mode", expectedSkill: null }, { name: "reserved-fullwidth-comma-boundary", prompt: "/prompts:architect, use autopilot mode", expectedSkill: null }, { name: "confusable-implicit-verb", prompt: "Do not use deep interview but uſe autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-colon", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-comma", prompt: "For instance, use autopilot mode.", expectedSkill: null }, { name: "frame-arabic-comma", prompt: "For instance، use autopilot mode.", expectedSkill: null }, { name: "frame-ideo-comma", prompt: "For instance、 use autopilot mode.", expectedSkill: null }, { name: "doc-explicit-documented", prompt: "use $ralplan is the workflow command; $autopilot is documented in the guide.", expectedSkill: null }, { name: "doc-explicit-described", prompt: "$autopilot is described in the manual.", expectedSkill: null }, { name: "doc-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fw-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-arabic-followup", prompt: "use $ralplan is the workflow command، but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-ideo-followup", prompt: "use $ralplan is the workflow command、 but use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-arabic-followup", prompt: "Do not run $ralplan، use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-ideo-followup", prompt: "Do not run $ralplan、 use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-doc-prefix-next", prompt: "The docs mention autopilot mode.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-prefix-same-line", prompt: "The docs mention autopilot mode; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-subject-same-line", prompt: "Autopilot mode is workflow documentation; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "compact-explicit-negation", prompt: "$ralplan,$autopilot are prohibited", expectedSkill: null }, { name: "compact-implicit-negation", prompt: "Autopilot mode،deep interview are prohibited.", expectedSkill: null }, { name: "doc-clause-local-prefix", prompt: "$ralplan; $autopilot is documented in the guide.", expectedSkill: "ralplan" }, { name: "doc-chain-described", prompt: "use $ralplan is the workflow command; autopilot mode is documented in the guide; $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "doc-chain-workflow", prompt: "use $ralplan is the workflow command; autopilot mode is workflow documentation; use $ralph execute it", expectedSkill: "ralph" }, { name: "ref-inline-explicit", prompt: "[docs]: $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "ref-inline-prompts", prompt: "[docs]: /prompts:architect\n$autopilot build it", expectedSkill: "autopilot" }, { name: "list-fullwidth-explicit-doc", prompt: "- $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "list-fullwidth-implicit-doc", prompt: "- autopilot mode: autonomous workflow command", expectedSkill: null }, { name: "possessive-straight", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-curly", prompt: "$ralplan’s workflow is documented", expectedSkill: null }, { name: "possessive-fullwidth", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-prompts", prompt: "/prompts:architect's syntax is documented", expectedSkill: null }, { name: "malformed-prefix-kata", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-half", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-arabic", prompt: "$٪autopilot mode", expectedSkill: null }, { name: "malformed-prefix-division", prompt: "$∕autopilot mode", expectedSkill: null }, { name: "doc-but-directive", prompt: "use $autopilot is documented but use $ralplan plan it", expectedSkill: "ralplan" }, { name: "list-directive-fw-colon", prompt: "- use $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "doc-arabic-question", prompt: "use $ralplan is the workflow command؟ run $autopilot", expectedSkill: "autopilot" }, { name: "arabic-semicolon-negation", prompt: "$ralplan؛ $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "confusable-postposed-transition", prompt: "$ralplan is prohibited but uſe autopilot mode.", expectedSkill: null }, { name: "mixed-negation", prompt: "Autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "both-mixed-negation", prompt: "Both autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "mixed-documentation", prompt: "use $ralplan and autopilot mode are workflow commands", expectedSkill: null }, { name: "prose-doc-no-reopen", prompt: "$ralplan is prohibited because docs use $autopilot.", expectedSkill: null }, { name: "neg-fw-dot-reopen", prompt: "Do not run $ralplan. use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-greek-q-reopen", prompt: "Do not run $ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "unicode-attached-contrast", prompt: "Do not use deep interview яbut use autopilot mode.", expectedSkill: null }, { name: "prefix-list-followup", prompt: "Do not run $ralplan, $autopilot; use $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "mixed-postposed-chain", prompt: "$ralplan, autopilot mode, $team are prohibited.", expectedSkill: null }, { name: "implicit-first-doc-chain", prompt: "Autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "both-mixed-doc-followup", prompt: "Both autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "doc-semicolon-preserves-earlier", prompt: "Use autopilot mode; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-independent-comma", prompt: "Use autopilot mode, and $ralplan is documented in the guide.", expectedSkill: "autopilot" }, { name: "reference-unclosed-quote-destination", prompt: "[docs]: \"target\n$autopilot build it", expectedSkill: null }, { name: "reference-unclosed-inline-destination", prompt: "[docs]: `target\n$autopilot build it", expectedSkill: null }, { name: "mixed-prefix-negation-implicit", prompt: "Do not run $ralplan and use autopilot mode.", expectedSkill: null }, { name: "repeated-postposed-followup", prompt: "$team is prohibited and is forbidden; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "doc-preserves-earlier", prompt: "Use autopilot mode; \"note\"; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-colon-followup", prompt: "use $ralplan is the workflow command: use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-followup", prompt: "Mode | Meaning\n--- | ---\nmanual | documentation\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "neg-advance-reopen", prompt: "Do not run $ralplan but advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "neg-jump-reopen", prompt: "Do not run $ralplan but jump straight to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "reference-plain-title", prompt: "[docs]: /target \"title\nplain text\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-plain-destination", prompt: "[docs]: ./target\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "directive-use-the", prompt: "Do not run $ralplan; use the $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-continue-after-quote", prompt: "\"quoted\"\ncontinue with $ralplan", expectedSkill: "ralplan" }, { name: "doc-advance-followup", prompt: "use $ralplan is the workflow command; advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "directive-run-analyze", prompt: "run $analyze", expectedSkill: "analyze", expectedStopBlock: false }, { name: "directive-run-code-review", prompt: "run $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-use-code-review", prompt: "please use $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-run-ralplan", prompt: "please run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-start-ralplan", prompt: "start $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-enable-deep-interview", prompt: "enable $deep-interview", expectedSkill: "deep-interview", expectedStopBlock: false }, { name: "directive-launch-autopilot", prompt: "launch $autopilot", expectedSkill: "autopilot" }, { name: "directive-invoke-ralph", prompt: "invoke $ralph", expectedSkill: "ralph" }, { name: "directive-activate-ultrawork", prompt: "activate $ultrawork", expectedSkill: "ultrawork" }, { name: "directive-resume-ralplan", prompt: "resume $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-continue-code-review", prompt: "continue $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-documentation", prompt: "use $ralplan is the consensus-planning command", expectedSkill: null }, { name: "g1a-ordered-multi-skill", prompt: "$ralplan, $autopilot; $team", expectedSkill: "ralplan", expectedDeferredSkills: ["autopilot", "team"], expectedActiveSkills: ["ralplan"], expectedActiveDetailSkills: ["ralplan"], insideTmux: true }, { name: "g1c-duplicate-alias", prompt: "$autopilot $oh-my-codex:autopilot build it", expectedSkill: "autopilot", expectedDeferredSkills: [], expectedActiveSkills: [] }, { name: "b3-longer-valid-fence", prompt: "```text\n$ralplan plan it\n````\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "b4-shorter-invalid-fence", prompt: "````text\n$ralplan plan it\n```\n$autopilot build it", expectedSkill: null }, { name: "b5-different-marker-invalid-fence", prompt: "```text\n$ralplan plan it\n~~~\n$autopilot build it", expectedSkill: null }, { name: "directive-non-leading-prose", prompt: "The docs say use $ralplan plan this", expectedSkill: null }, { name: "nested-bounded-child-unbounded-parent", prompt: "\"`x`\n$ralplan plan it", expectedSkill: null }, { name: "first-contiguous-block-terminal", prompt: "$ralplan plan it\n\"x\"\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "leading-reserved-dominance", prompt: "/prompts:architect\n\"x\"\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-root-opener", prompt: "- ```\n sample\n```\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-relative-closer", prompt: "- ```\n sample\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-multiline-title-explicit", prompt: "[docs]: /target \"title\nuse /prompts:architect\n$ralplan plan it\"", expectedSkill: null }, { name: "reference-multiline-title-implicit", prompt: "[docs]: /target \"title\nuse autopilot mode\"", expectedSkill: null }, { name: "reference-next-line-title", prompt: "[docs]: ./target\n (autopilot mode)", expectedSkill: null }, { name: "reference-next-line-destination-title", prompt: "[docs]:\n ./target\n (autopilot mode)", expectedSkill: null }, { name: "kelvin-case-fold-suffix", prompt: "$ultraworK execute", expectedSkill: null }, { name: "katakana-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "halfwidth-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "arabic-percent-suffix", prompt: "$ralplan٪docs", expectedSkill: null }, { name: "division-slash-suffix", prompt: "$ralplan∕config", expectedSkill: null }, { name: "implicit-negative", prompt: "Do not use autopilot mode.", expectedSkill: null }, { name: "implicit-negative-list", prompt: "Do not use deep interview, autopilot mode.", expectedSkill: null }, { name: "implicit-negative-nor", prompt: "Do not use deep interview, nor autopilot mode.", expectedSkill: null }, { name: "implicit-negative-avoid", prompt: "Avoid autopilot mode.", expectedSkill: null }, { name: "implicit-negative-suffix", prompt: "Autopilot mode is not allowed.", expectedSkill: null }, { name: "implicit-negative-contraction", prompt: "Autopilot mode isn't allowed.", expectedSkill: null }, { name: "implicit-negative-prohibited", prompt: "Autopilot mode is prohibited.", expectedSkill: null }, { name: "implicit-no-prefix", prompt: "No autopilot mode.", expectedSkill: null }, { name: "implicit-quoted-doc", prompt: "The docs call this \"autopilot mode\".", expectedSkill: null }, { name: "implicit-fenced", prompt: "```\nautopilot mode\n```", expectedSkill: null }, { name: "implicit-list-fenced", prompt: "- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-sibling-list-fence", prompt: "- ```\n first example\n- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-prompts-protocol", prompt: "use /prompts:architect autopilot mode", expectedSkill: null }, { name: "implicit-quoted-prompts-positive", prompt: "Ignore the quoted \"/prompts:architect\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-doc-verb", prompt: "This documents autopilot mode.", expectedSkill: null }, { name: "implicit-guide-frame", prompt: "The guide says do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-link", prompt: "[autopilot mode](./docs.md)", expectedSkill: null }, { name: "implicit-markdown-heading", prompt: "## Autopilot mode", expectedSkill: null }, { name: "implicit-markdown-table", prompt: "| autopilot mode | workflow command |", expectedSkill: null }, { name: "implicit-dash-documentation", prompt: "Autopilot mode — autonomous workflow command", expectedSkill: null }, { name: "implicit-unicode-adjacency", prompt: "문서autopilot mode한글", expectedSkill: null }, { name: "implicit-korean-negative", prompt: "autopilot mode는 사용하지 마세요", expectedSkill: null }, { name: "implicit-postposed-coordinated-negative", prompt: "Autopilot mode and deep interview are prohibited.", expectedSkill: null }, { name: "implicit-postposed-modal-negative", prompt: "Autopilot mode should be avoided.", expectedSkill: null }, { name: "implicit-example-frame", prompt: "Example: do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-single-quote", prompt: "'autopilot mode'", expectedSkill: null }, { name: "escaped-plugin-autopilot", prompt: "\\$oh-my-codex:autopilot mode", expectedSkill: null }, { name: "implicit-comma-coordinated-negative", prompt: "Autopilot mode, deep interview, and team are prohibited.", expectedSkill: null }, { name: "implicit-for-example-frame", prompt: "For example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-docs-comma-frame", prompt: "According to the docs, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-dont-negative", prompt: "Don’t use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-dont-negative", prompt: "Don't use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-isnt-negative", prompt: "Autopilot mode isn’t allowed.", expectedSkill: null }, { name: "implicit-copular-infinitive-negative", prompt: "Autopilot mode is to be avoided.", expectedSkill: null }, { name: "implicit-past-copular-infinitive-negative", prompt: "Autopilot mode was to be disabled.", expectedSkill: null }, { name: "implicit-article-coordinated-negative", prompt: "Autopilot mode and the deep interview workflow are prohibited.", expectedSkill: null }, { name: "implicit-as-example-frame", prompt: "As an example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-frame", prompt: "For instance, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-reference-link", prompt: "[autopilot mode][docs]", expectedSkill: null }, { name: "implicit-plural-coordinated-negative", prompt: "Autopilot mode and deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-as-example-governing-frame", prompt: "As an example, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-governing-frame", prompt: "For instance, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-definition", prompt: "[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-markdown-shortcut-label", prompt: "[autopilot mode]", expectedSkill: null }, { name: "implicit-markdown-setext", prompt: "Autopilot mode\n===", expectedSkill: null }, { name: "explicit-markdown-pipe-table", prompt: "$ralplan | workflow\n--- | ---", expectedSkill: null }, { name: "implicit-markdown-table-body", prompt: "Mode | Meaning\n--- | ---\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-as-well-as-negative", prompt: "Autopilot mode as well as deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-along-with-negative", prompt: "Autopilot mode along with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-together-with-negative", prompt: "Autopilot mode together with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-ampersand-negative", prompt: "Autopilot mode & deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-example-colon-frame", prompt: "As an example: ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame", prompt: "For instance — ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-example-hyphen-frame", prompt: "For example - ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-colon-frame-no-doc-word", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame-no-doc-word", prompt: "For instance — use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-later-table-body", prompt: "Mode | Meaning\n--- | ---\nmanual | docs\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-embedded-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-parenthetical-as-well-negative", prompt: "Autopilot mode, as well as deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-along-negative", prompt: "Autopilot mode, along with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-together-negative", prompt: "Autopilot mode, together with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-normalized-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-ignore-exclusion", prompt: "Ignore autopilot mode.", expectedSkill: null }, { name: "implicit-skip-exclusion", prompt: "Skip autopilot mode.", expectedSkill: null }, { name: "implicit-exclude-exclusion", prompt: "Exclude autopilot mode.", expectedSkill: null }, { name: "explicit-postposed-prohibited", prompt: "$ralplan is prohibited.", expectedSkill: null }, { name: "explicit-postposed-modal-negative", prompt: "$ralplan should not be run.", expectedSkill: null }, { name: "explicit-postposed-coordinated-negative", prompt: "$ralplan and $autopilot are prohibited.", expectedSkill: null }, { name: "explicit-postposed-article-coordination", prompt: "$ralplan and the $autopilot workflow are prohibited.", expectedSkill: null }, { name: "explicit-postposed-implicit-coordination", prompt: "$ralplan and autopilot mode are prohibited.", expectedSkill: null }, { name: "implicit-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-list-reference-definition", prompt: "See [autopilot mode] for details.\n\n- [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-indented-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "explicit-list-contained-indented-code", prompt: "- $ralplan", expectedSkill: null }, { name: "implicit-list-contained-indented-code", prompt: "1. autopilot mode", expectedSkill: null }, { name: "implicit-four-space-blockquote-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-multiline-reference-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]:\n ./docs", expectedSkill: null }, { name: "explicit-list-tab-code-boundary", prompt: "- \t$ralplan", expectedSkill: null }, { name: "implicit-nested-list-contained-code", prompt: "- - autopilot mode", expectedSkill: null }, { name: "implicit-list-nested-blockquote", prompt: "- > autopilot mode", expectedSkill: null }, { name: "implicit-deep-nested-list-contained-code", prompt: "- - - - - - - - - autopilot mode", expectedSkill: null }, { name: "implicit-ordered-list-nested-blockquote", prompt: "1234. > autopilot mode", expectedSkill: null }, { name: "explicit-nested-list-positive", prompt: "- - $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-four-digit-ordered-list-positive", prompt: "1234. $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-list-fence-then-positive", prompt: "- ```\n $ralplan\n ```\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-nested-list-doc-then-positive", prompt: "- - $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-after-closed-blockquote", prompt: "> quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-fence", prompt: "```text\nquoted context\n```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-indented-code", prompt: " quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-quote", prompt: "\"quoted context\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-prompts-context", prompt: "Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-bare-prompts-precedence", prompt: "Prose\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-after-unclosed-fence", prompt: "```text\nquoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-mismatched-fence", prompt: "```text\nquoted context\n~~~\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-unclosed-quote", prompt: "\"quoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-blockquote-prose", prompt: "> quoted context\nThe docs mention $ralplan only", expectedSkill: null }, { name: "explicit-after-quote-negative", prompt: "\"quoted context\"\nDo not run $ralplan", expectedSkill: null }, { name: "explicit-stale-predecessor-prose", prompt: "> quoted context\nProse\n$ralplan implement this", expectedSkill: null }, { name: "explicit-stale-predecessor-directive-clause", prompt: "> quoted context\nProse\nUse $ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-prompts", prompt: "> quoted context\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-second-block", prompt: "> quoted context\n$ralplan plan it\nLater discussion.\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "explicit-negative-before-unclosed-quote", prompt: "Do not run $ralplan.\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-reference-before-unclosed-quote", prompt: "[$ralplan]: ./docs\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-prompts-inside-unclosed-quote", prompt: "\"Use /prompts:architect\n$ralplan plan it", expectedSkill: null }, { name: "explicit-malformed-prompts-suffix", prompt: "/prompts:architect한글\n$ralplan plan it", expectedSkill: null }, { name: "explicit-nested-list-prompts-positive", prompt: "- Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-nested-list-fence-positive", prompt: "- - ```\n quoted context\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-reference-destination", prompt: "[docs]:\nautopilot", expectedSkill: null }, { name: "explicit-middle-dot-suffix", prompt: "$ralplan·suffix plan it", expectedSkill: null }, { name: "explicit-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-fullwidth-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-postposed-also-negative", prompt: "$ralplan is also prohibited.", expectedSkill: null }, { name: "implicit-postposed-still-negative", prompt: "Autopilot mode is still prohibited.", expectedSkill: null }, { name: "implicit-commonmark-case-fold", prompt: "See [ẞ autopilot mode] for details.\n\n[SS autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-commonmark-escaped-bracket", prompt: "See [foo\\] autopilot mode] for details.\n\n[foo\\] autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-version-decimal-frame", prompt: "For instance: in version 1.2, use autopilot mode.", expectedSkill: null }, { name: "implicit-abbreviation-frame", prompt: "For instance: e.g. use autopilot mode.", expectedSkill: null }, { name: "implicit-slash-documentation", prompt: "Autopilot mode / deep interview are workflow commands.", expectedSkill: null }, { name: "implicit-low-quote", prompt: "„autopilot mode“", expectedSkill: null }, { name: "slash-list-documentation", prompt: "- $ralplan / $autopilot are workflow commands", expectedSkill: null }, { name: "compact-slash-list-documentation", prompt: "- $ralplan/$autopilot are workflow commands", expectedSkill: null }, { name: "slash-list-composition", prompt: "- $ralplan / $autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "compact-slash-list-composition", prompt: "- $ralplan/$autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "but-use-positive", prompt: "Do not run $ralplan but use $autopilot build it", expectedSkill: "autopilot" }, { name: "but-instead-use-positive", prompt: "Do not run $ralplan but instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "em-dash-instead-use-positive", prompt: "Do not run $ralplan — instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-positive-contrast", prompt: "Do not use deep interview, but use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-but-instead-positive", prompt: "Do not use deep interview but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-list-verb-positive", prompt: "List files and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-dont-stop-positive", prompt: "No, don't stop.", expectedSkill: "ralph" }, { name: "implicit-doc-suffix", prompt: "autopilot mode is workflow documentation.", expectedSkill: null }, { name: "implicit-inline-code", prompt: "`autopilot mode`", expectedSkill: null }, { name: "implicit-blockquote", prompt: "> autopilot mode", expectedSkill: null }, { name: "implicit-doc-then-positive", prompt: "Autopilot mode is workflow documentation.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-then-positive", prompt: "Use /prompts:architect.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-negative-explicit-positive-implicit", prompt: "Do not run $ralplan but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-quoted-explicit-positive-implicit", prompt: "Ignore \"$ralplan\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-positive-implicit", prompt: "Ignore \\/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-prompts-positive-implicit", prompt: "See https://example.com/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-used", prompt: "Autopilot mode should be used.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-enabled", prompt: "Autopilot mode must be enabled.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-run", prompt: "Autopilot mode can be run.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-possessive-positive", prompt: "User's request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "markdown-prompts-link-positive", prompt: "See [/prompts:architect](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-subordinate-positive", prompt: "Use autopilot mode, while deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-sentence-then-positive", prompt: "Read the docs. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-semicolon-then-positive", prompt: "The docs are stale; use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-comma-directive-positive", prompt: "Ignore the docs, use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-explicit-followup", prompt: "Ignore \\/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-explicit-followup", prompt: "See https://example.com/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-explicit-followup", prompt: "See [/prompts:architect](./docs.md)\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "linked-explicit-implicit-positive", prompt: "See [$ralplan](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-explicit-implicit-positive", prompt: "See https://example.com/$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-and-directive-positive", prompt: "Ignore the docs and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-same-line-explicit", prompt: "Ignore \\/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-same-line-explicit", prompt: "See https://example.com/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-same-line-explicit", prompt: "See [/prompts:architect](./docs.md); use $ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-prompts-same-line-explicit", prompt: "See [/prompts:architect][docs]; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-prompts-explicit-followup", prompt: "## /prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "table-prompts-explicit-followup", prompt: "| /prompts:architect |\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-explicit-implicit-positive", prompt: "## $ralplan\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "table-explicit-implicit-positive", prompt: "| $ralplan |\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "reference-explicit-implicit-positive", prompt: "See [$ralplan][docs] and use autopilot mode.", expectedSkill: "autopilot" }, { name: "windows-path-explicit-implicit-positive", prompt: "See C:\\docs\\$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-plural-possessive-positive", prompt: "Users' request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-modal-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview should be avoided.", expectedSkill: "autopilot" }, { name: "docs-but-directive-positive", prompt: "Ignore the docs but use autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-markdown-definition-explicit-followup", prompt: "[/prompts:architect]: ./docs\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "prompts-markdown-shortcut-explicit-followup", prompt: "[/prompts:architect]\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-markdown-definition-implicit-positive", prompt: "[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-shortcut-implicit-positive", prompt: "[$ralplan]\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-setext-implicit-positive", prompt: "$ralplan\n===\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-pipe-table-implicit-positive", prompt: "$ralplan | workflow\n--- | ---\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "heading-explicit-later-explicit", prompt: "## $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "linked-explicit-later-explicit", prompt: "See [$ralplan](./docs.md); use $autopilot build it", expectedSkill: "autopilot" }, { name: "windows-path-explicit-later-explicit", prompt: "See C:\\docs\\$ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-body-explicit-later-explicit", prompt: "Mode | Meaning\n--- | ---\n$ralplan | planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "embedded-shortcut-explicit-implicit-positive", prompt: "See [$ralplan] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "quoted-explicit-later-explicit", prompt: "Ignore \"$ralplan\" and use $autopilot build it", expectedSkill: "autopilot" }, { name: "inline-code-explicit-later-explicit", prompt: "Ignore `$ralplan` and use $autopilot build it", expectedSkill: "autopilot" }, { name: "normalized-shortcut-explicit-implicit-positive", prompt: "See [$ralplan ] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "intro-frame-sentence-reset-positive", prompt: "For instance: manual mode is slower. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "inline-link-overlap-later-explicit", prompt: "See [`$ralplan`](./docs.md) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-exclusion-later-positive", prompt: "Ignore deep interview and use autopilot mode.", expectedSkill: "autopilot" }, { name: "link-destination-later-explicit", prompt: "See [docs](https://example.com/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "link-title-later-explicit", prompt: "See [docs](./docs.md \"$ralplan reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-destination-later-explicit", prompt: "See [docs](https://example.com/(v1)/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-link-text-later-explicit", prompt: "See [$ralplan](https://example.com/(v1)) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "quoted-title-parenthesis-later-explicit", prompt: "See [docs](./docs.md \"$ralplan (reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "blockquote-code-definition-boundary", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: "autopilot" }, { name: "explicit-list-tab-content-boundary", prompt: "-\t $ralplan", expectedSkill: "ralplan" }, { name: "postposed-negative-but-later-explicit", prompt: "$ralplan is prohibited but use $autopilot build it", expectedSkill: "autopilot" }, { name: "postposed-negative-and-later-explicit", prompt: "$ralplan is prohibited and use $autopilot build it", expectedSkill: "autopilot" }, { name: "later-positive", prompt: "Do not run $ralplan, instead $autopilot build it", expectedSkill: "autopilot" }, { name: "list-documentation", prompt: "- $ralplan, $autopilot are workflow commands", expectedSkill: null }, { name: "list-composition", prompt: "- $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "escaped-quote", prompt: "\"$ralplan \\\"; $autopilot build it", expectedSkill: null }, { name: "fence-prefix", prompt: "```\n$ralplan\n> ```\n$autopilot build it", expectedSkill: null }, { name: "matching-fence-control", prompt: "> ```\n> $ralplan\n> ```\n$autopilot build it", expectedSkill: "autopilot" }, ] as const; for (const testCase of cases) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-raw-classification-${testCase.name}-`)); const sessionId = `sess-raw-${testCase.name}`; const previousTmux = process.env.TMUX; const previousTmuxPane = process.env.TMUX_PANE; const previousTeamMode = process.env.OMX_TEAM_MODE; if ("insideTmux" in testCase && testCase.insideTmux) { process.env.TMUX = "/tmp/tmux-pr3140-regression"; process.env.TMUX_PANE = "%3140"; process.env.OMX_TEAM_MODE = "enabled"; } else { delete process.env.TMUX; delete process.env.TMUX_PANE; delete process.env.OMX_TEAM_MODE; } try { const submit = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${testCase.name}`, turn_id: `turn-${testCase.name}`, prompt: testCase.prompt, }, { cwd }); assert.equal((submit.outputJson as { continue?: boolean } | null)?.continue, undefined, testCase.name); const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); const skillStatePath = join(sessionDir, "skill-active-state.json"); const expectsAutopilotDenial = testCase.expectedSkill === "autopilot"; if (testCase.expectedSkill === null) { assert.equal(existsSync(skillStatePath), false, testCase.name); } else { const skillState = JSON.parse(await readFile(skillStatePath, "utf-8")) as { active?: boolean; skill?: string; phase?: string; error?: string; deferred_skills?: string[]; active_skills?: Array<{ skill?: string }> }; if (expectsAutopilotDenial) { assert.equal(skillState.active, false, testCase.name); assert.equal(skillState.skill, "autopilot", testCase.name); assert.equal(skillState.phase, "failed", testCase.name); assert.equal(skillState.error, "documented_host_consensus_receipt_unavailable", testCase.name); assert.deepEqual(skillState.active_skills, [], testCase.name); const guidance = String((submit.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""); assert.match(guidance, /documented_host_consensus_receipt_unavailable/, testCase.name); assert.doesNotMatch(guidance, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal/, testCase.name); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false, testCase.name); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false, testCase.name); } else { assert.equal(skillState.active, true, testCase.name); assert.equal(skillState.skill, testCase.expectedSkill, testCase.name); } if ("expectedDeferredSkills" in testCase) { assert.deepEqual(skillState.deferred_skills ?? [], testCase.expectedDeferredSkills, testCase.name); } if ("expectedActiveSkills" in testCase) { assert.deepEqual(skillState.active_skills?.map((entry) => entry.skill) ?? [], testCase.expectedActiveSkills, testCase.name); } if ("expectedActiveDetailSkills" in testCase) { const activeDetailSkills = (await Promise.all(["ralplan", "autopilot"].map(async (skill) => { const detailPath = join(sessionDir, `${skill}-state.json`); if (!existsSync(detailPath)) return null; const detailState = JSON.parse(await readFile(detailPath, "utf-8")) as { active?: boolean }; return detailState.active ? skill : null; }))).filter((skill): skill is string => skill !== null); assert.deepEqual(activeDetailSkills, testCase.expectedActiveDetailSkills, testCase.name); } } const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${testCase.name}`, turn_id: `stop-${testCase.name}`, }, { cwd }); if (testCase.expectedSkill === null || expectsAutopilotDenial) { assert.equal(stop.outputJson, null, testCase.name); } else if ("expectedStopBlock" in testCase && !testCase.expectedStopBlock) { assert.notEqual((stop.outputJson as { decision?: string } | null)?.decision, "block", testCase.name); } else assert.equal((stop.outputJson as { decision?: string } | null)?.decision, "block", testCase.name); } finally { if (previousTmux === undefined) delete process.env.TMUX; else process.env.TMUX = previousTmux; if (previousTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = previousTmuxPane; if (previousTeamMode === undefined) delete process.env.OMX_TEAM_MODE; else process.env.OMX_TEAM_MODE = previousTeamMode; await rm(cwd, { recursive: true, force: true }); } } }); it("preserves exact structural prompt classification through compiled UserPromptSubmit state and Stop", async () => { const cases = [ { name: "mixed-indent", prompt: " \t$ralplan plan this", expectedSkill: null }, { name: "at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "fullwidth-at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "fullwidth-hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "fullwidth-equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "directive-use-ralplan", prompt: "use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-please-use-ralplan", prompt: "please use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-run-ralplan", prompt: "run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-list-use-ralplan", prompt: "- use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-documentation-then-command", prompt: "use $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-then-implicit-command", prompt: "use $ralplan is the consensus-planning command\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-trailing-prose-then-command", prompt: "use $ralplan is the workflow command for planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-prose", prompt: "use $ralplan is the workflow command for autopilot mode", expectedSkill: null }, { name: "directive-documentation-alias-prose", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.", expectedSkill: null }, { name: "directive-coordinated-documentation-then-command", prompt: "- use $ralplan and $autopilot are workflow commands\n$ralph execute it", expectedSkill: "ralph" }, { name: "directive-documentation-semicolon-directive", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-two-documentation-blocks", prompt: "use $ralplan is the consensus-planning command\nuse $autopilot is the autonomous workflow command\n$ralph execute it", expectedSkill: "ralph" }, { name: "directive-documentation-embedded-token-then-command", prompt: "use $ralplan is the workflow command for $team\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-task-noun-followup", prompt: "use $ralplan is the workflow command; use $autopilot update the documentation", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-semicolon-followup", prompt: "use $ralplan is the consensus-planning command; use autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-transition-followup", prompt: "use $ralplan is the consensus-planning command; then use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-explicit-alias", prompt: "use $ralplan is the consensus-planning command; $team is its alias", expectedSkill: null }, { name: "directive-documentation-implicit-chain", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.\n$ralph execute it", expectedSkill: "ralph" }, { name: "directive-documentation-fullwidth-separator", prompt: "use $ralplan,$autopilot are workflow commands", expectedSkill: null }, { name: "directive-documentation-compact-slash", prompt: "use $ralplan/$autopilot are workflow commands\n$ralph execute it", expectedSkill: "ralph" }, { name: "reference-prompts-title-then-command", prompt: "[docs]: /target \"title\nUse /prompts:architect\"\n$ralph execute it", expectedSkill: "ralph" }, { name: "doc-period-implicit", prompt: "use $ralplan is the consensus-planning command. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-bare-implicit", prompt: "use $ralplan is the consensus-planning command; autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-fullwidth-semicolon", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-but-followup", prompt: "use $ralplan is the workflow command; but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fullwidth-oxford", prompt: "use $ralplan, $autopilot, and $team are workflow commands", expectedSkill: null }, { name: "reference-zero-title", prompt: "[docs]: ./target\n(autopilot mode)", expectedSkill: null }, { name: "doc-also-alias-explicit", prompt: "use $ralplan is the consensus-planning command; $team is also its alias", expectedSkill: null }, { name: "doc-also-alias-implicit", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is also its alias.", expectedSkill: null }, { name: "doc-embedded-mention", prompt: "use $ralplan is the workflow command; $team appears in the documentation.", expectedSkill: null }, { name: "chained-negation", prompt: "$ralplan; $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "long-negation", prompt: `$ralplan; $autopilot${" ".repeat(193)}is prohibited`, expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "doc-arabic-comma", prompt: "use $ralplan، $autopilot are workflow commands", expectedSkill: null }, { name: "arabic-negation", prompt: "$ralplan، $autopilot are prohibited", expectedSkill: null }, { name: "implicit-arabic-negation", prompt: "Autopilot mode، deep interview are prohibited.", expectedSkill: null }, { name: "fullwidth-frame-reset", prompt: "For instance: manual mode is slower。 Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-abbreviation", prompt: "use $ralplan is the workflow command, e.g. use $autopilot in examples.", expectedSkill: null }, { name: "implicit-doc-mention", prompt: "use $ralplan is the workflow command; autopilot mode appears in the documentation.", expectedSkill: null }, { name: "implicit-doc-chain", prompt: "use $ralplan is the workflow command; autopilot mode is its alias; $ralph execute it", expectedSkill: "ralph" }, { name: "long-command-gap", prompt: `use $ralplan is the workflow command; use${" ".repeat(161)}$autopilot build it`, expectedSkill: "autopilot" }, { name: "ideographic-negation", prompt: "$ralplan、 $autopilot are prohibited", expectedSkill: null }, { name: "implicit-ideographic-negation", prompt: "Autopilot mode、 deep interview are prohibited.", expectedSkill: null }, { name: "doc-exclamation-followup", prompt: "use $ralplan is the consensus-planning command! run $autopilot", expectedSkill: "autopilot" }, { name: "doc-fullwidth-question-followup", prompt: "use $ralplan is the consensus-planning command? run $autopilot", expectedSkill: "autopilot" }, { name: "implicit-doc-predecessor", prompt: "Autopilot mode is workflow documentation.\n$ralph execute it", expectedSkill: "ralph" }, { name: "confusable-use-verb", prompt: "uſe $ralplan plan it", expectedSkill: null }, { name: "confusable-please-prefix", prompt: "pleaſe use $ralplan plan it", expectedSkill: null }, { name: "confusable-prompts-token-then-command", prompt: "/promptſ:architect; use autopilot mode.", expectedSkill: "autopilot" }, { name: "reserved-em-dash-boundary", prompt: "/prompts:architect— use autopilot mode", expectedSkill: null }, { name: "reserved-fullwidth-comma-boundary", prompt: "/prompts:architect, use autopilot mode", expectedSkill: null }, { name: "confusable-implicit-verb", prompt: "Do not use deep interview but uſe autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-colon", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-comma", prompt: "For instance, use autopilot mode.", expectedSkill: null }, { name: "frame-arabic-comma", prompt: "For instance، use autopilot mode.", expectedSkill: null }, { name: "frame-ideo-comma", prompt: "For instance、 use autopilot mode.", expectedSkill: null }, { name: "doc-explicit-documented", prompt: "use $ralplan is the workflow command; $autopilot is documented in the guide.", expectedSkill: null }, { name: "doc-explicit-described", prompt: "$autopilot is described in the manual.", expectedSkill: null }, { name: "doc-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fw-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-arabic-followup", prompt: "use $ralplan is the workflow command، but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-ideo-followup", prompt: "use $ralplan is the workflow command、 but use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-arabic-followup", prompt: "Do not run $ralplan، use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-ideo-followup", prompt: "Do not run $ralplan、 use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-doc-prefix-next", prompt: "The docs mention autopilot mode.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-prefix-same-line", prompt: "The docs mention autopilot mode; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-subject-same-line", prompt: "Autopilot mode is workflow documentation; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "compact-explicit-negation", prompt: "$ralplan,$autopilot are prohibited", expectedSkill: null }, { name: "compact-implicit-negation", prompt: "Autopilot mode،deep interview are prohibited.", expectedSkill: null }, { name: "doc-clause-local-prefix", prompt: "$ralplan; $autopilot is documented in the guide.", expectedSkill: "ralplan" }, { name: "doc-chain-described", prompt: "use $ralplan is the workflow command; autopilot mode is documented in the guide; $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "doc-chain-workflow", prompt: "use $ralplan is the workflow command; autopilot mode is workflow documentation; use $ralph execute it", expectedSkill: "ralph" }, { name: "ref-inline-explicit", prompt: "[docs]: $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "ref-inline-prompts", prompt: "[docs]: /prompts:architect\n$autopilot build it", expectedSkill: "autopilot" }, { name: "list-fullwidth-explicit-doc", prompt: "- $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "list-fullwidth-implicit-doc", prompt: "- autopilot mode: autonomous workflow command", expectedSkill: null }, { name: "possessive-straight", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-curly", prompt: "$ralplan’s workflow is documented", expectedSkill: null }, { name: "possessive-fullwidth", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-prompts", prompt: "/prompts:architect's syntax is documented", expectedSkill: null }, { name: "malformed-prefix-kata", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-half", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-arabic", prompt: "$٪autopilot mode", expectedSkill: null }, { name: "malformed-prefix-division", prompt: "$∕autopilot mode", expectedSkill: null }, { name: "doc-but-directive", prompt: "use $autopilot is documented but use $ralplan plan it", expectedSkill: "ralplan" }, { name: "list-directive-fw-colon", prompt: "- use $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "doc-arabic-question", prompt: "use $ralplan is the workflow command؟ run $autopilot", expectedSkill: "autopilot" }, { name: "arabic-semicolon-negation", prompt: "$ralplan؛ $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "confusable-postposed-transition", prompt: "$ralplan is prohibited but uſe autopilot mode.", expectedSkill: null }, { name: "mixed-negation", prompt: "Autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "both-mixed-negation", prompt: "Both autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "mixed-documentation", prompt: "use $ralplan and autopilot mode are workflow commands", expectedSkill: null }, { name: "prose-doc-no-reopen", prompt: "$ralplan is prohibited because docs use $autopilot.", expectedSkill: null }, { name: "neg-fw-dot-reopen", prompt: "Do not run $ralplan. use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-greek-q-reopen", prompt: "Do not run $ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "unicode-attached-contrast", prompt: "Do not use deep interview яbut use autopilot mode.", expectedSkill: null }, { name: "prefix-list-followup", prompt: "Do not run $ralplan, $autopilot; use $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "mixed-postposed-chain", prompt: "$ralplan, autopilot mode, $team are prohibited.", expectedSkill: null }, { name: "implicit-first-doc-chain", prompt: "Autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "both-mixed-doc-followup", prompt: "Both autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", expectedStopBlock: false, insideTmux: true }, { name: "doc-semicolon-preserves-earlier", prompt: "Use autopilot mode; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-independent-comma", prompt: "Use autopilot mode, and $ralplan is documented in the guide.", expectedSkill: "autopilot" }, { name: "reference-unclosed-quote-destination", prompt: "[docs]: \"target\n$autopilot build it", expectedSkill: null }, { name: "reference-unclosed-inline-destination", prompt: "[docs]: `target\n$autopilot build it", expectedSkill: null }, { name: "mixed-prefix-negation-implicit", prompt: "Do not run $ralplan and use autopilot mode.", expectedSkill: null }, { name: "repeated-postposed-followup", prompt: "$team is prohibited and is forbidden; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "doc-preserves-earlier", prompt: "Use autopilot mode; \"note\"; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-colon-followup", prompt: "use $ralplan is the workflow command: use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-followup", prompt: "Mode | Meaning\n--- | ---\nmanual | documentation\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "neg-advance-reopen", prompt: "Do not run $ralplan but advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "neg-jump-reopen", prompt: "Do not run $ralplan but jump straight to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "reference-plain-title", prompt: "[docs]: /target \"title\nplain text\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-plain-destination", prompt: "[docs]: ./target\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "directive-use-the", prompt: "Do not run $ralplan; use the $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-continue-after-quote", prompt: "\"quoted\"\ncontinue with $ralplan", expectedSkill: "ralplan" }, { name: "doc-advance-followup", prompt: "use $ralplan is the workflow command; advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "directive-run-analyze", prompt: "run $analyze", expectedSkill: "analyze", expectedStopBlock: false }, { name: "directive-run-code-review", prompt: "run $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-use-code-review", prompt: "please use $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-run-ralplan", prompt: "please run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-start-ralplan", prompt: "start $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-enable-deep-interview", prompt: "enable $deep-interview", expectedSkill: "deep-interview", expectedStopBlock: false }, { name: "directive-launch-autopilot", prompt: "launch $autopilot", expectedSkill: "autopilot" }, { name: "directive-invoke-ralph", prompt: "invoke $ralph", expectedSkill: "ralph" }, { name: "directive-activate-ultrawork", prompt: "activate $ultrawork", expectedSkill: "ultrawork" }, { name: "directive-resume-ralplan", prompt: "resume $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-continue-code-review", prompt: "continue $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-documentation", prompt: "use $ralplan is the consensus-planning command", expectedSkill: null }, { name: "g1a-ordered-multi-skill", prompt: "$ralplan, $autopilot; $team", expectedSkill: "ralplan", expectedDeferredSkills: ["autopilot", "team"], expectedActiveSkills: ["ralplan"], expectedActiveDetailSkills: ["ralplan"], insideTmux: true }, { name: "g1c-duplicate-alias", prompt: "$autopilot $oh-my-codex:autopilot build it", expectedSkill: "autopilot", expectedDeferredSkills: [], expectedActiveSkills: [] }, { name: "b3-longer-valid-fence", prompt: "```text\n$ralplan plan it\n````\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "b4-shorter-invalid-fence", prompt: "````text\n$ralplan plan it\n```\n$autopilot build it", expectedSkill: null }, { name: "b5-different-marker-invalid-fence", prompt: "```text\n$ralplan plan it\n~~~\n$autopilot build it", expectedSkill: null }, { name: "directive-non-leading-prose", prompt: "The docs say use $ralplan plan this", expectedSkill: null }, { name: "nested-bounded-child-unbounded-parent", prompt: "\"`x`\n$ralplan plan it", expectedSkill: null }, { name: "first-contiguous-block-terminal", prompt: "$ralplan plan it\n\"x\"\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "leading-reserved-dominance", prompt: "/prompts:architect\n\"x\"\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-root-opener", prompt: "- ```\n sample\n```\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-relative-closer", prompt: "- ```\n sample\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-multiline-title-explicit", prompt: "[docs]: /target \"title\nuse /prompts:architect\n$ralplan plan it\"", expectedSkill: null }, { name: "reference-multiline-title-implicit", prompt: "[docs]: /target \"title\nuse autopilot mode\"", expectedSkill: null }, { name: "reference-next-line-title", prompt: "[docs]: ./target\n (autopilot mode)", expectedSkill: null }, { name: "reference-next-line-destination-title", prompt: "[docs]:\n ./target\n (autopilot mode)", expectedSkill: null }, { name: "kelvin-case-fold-suffix", prompt: "$ultraworK execute", expectedSkill: null }, { name: "katakana-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "halfwidth-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "arabic-percent-suffix", prompt: "$ralplan٪docs", expectedSkill: null }, { name: "division-slash-suffix", prompt: "$ralplan∕config", expectedSkill: null }, { name: "implicit-negative", prompt: "Do not use autopilot mode.", expectedSkill: null }, { name: "implicit-negative-list", prompt: "Do not use deep interview, autopilot mode.", expectedSkill: null }, { name: "implicit-negative-nor", prompt: "Do not use deep interview, nor autopilot mode.", expectedSkill: null }, { name: "implicit-negative-avoid", prompt: "Avoid autopilot mode.", expectedSkill: null }, { name: "implicit-negative-suffix", prompt: "Autopilot mode is not allowed.", expectedSkill: null }, { name: "implicit-negative-contraction", prompt: "Autopilot mode isn't allowed.", expectedSkill: null }, { name: "implicit-negative-prohibited", prompt: "Autopilot mode is prohibited.", expectedSkill: null }, { name: "implicit-no-prefix", prompt: "No autopilot mode.", expectedSkill: null }, { name: "implicit-quoted-doc", prompt: "The docs call this \"autopilot mode\".", expectedSkill: null }, { name: "implicit-fenced", prompt: "```\nautopilot mode\n```", expectedSkill: null }, { name: "implicit-list-fenced", prompt: "- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-sibling-list-fence", prompt: "- ```\n first example\n- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-prompts-protocol", prompt: "use /prompts:architect autopilot mode", expectedSkill: null }, { name: "implicit-quoted-prompts-positive", prompt: "Ignore the quoted \"/prompts:architect\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-doc-verb", prompt: "This documents autopilot mode.", expectedSkill: null }, { name: "implicit-guide-frame", prompt: "The guide says do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-link", prompt: "[autopilot mode](./docs.md)", expectedSkill: null }, { name: "implicit-markdown-heading", prompt: "## Autopilot mode", expectedSkill: null }, { name: "implicit-markdown-table", prompt: "| autopilot mode | workflow command |", expectedSkill: null }, { name: "implicit-dash-documentation", prompt: "Autopilot mode — autonomous workflow command", expectedSkill: null }, { name: "implicit-unicode-adjacency", prompt: "문서autopilot mode한글", expectedSkill: null }, { name: "implicit-korean-negative", prompt: "autopilot mode는 사용하지 마세요", expectedSkill: null }, { name: "implicit-postposed-coordinated-negative", prompt: "Autopilot mode and deep interview are prohibited.", expectedSkill: null }, { name: "implicit-postposed-modal-negative", prompt: "Autopilot mode should be avoided.", expectedSkill: null }, { name: "implicit-example-frame", prompt: "Example: do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-single-quote", prompt: "'autopilot mode'", expectedSkill: null }, { name: "escaped-plugin-autopilot", prompt: "\\$oh-my-codex:autopilot mode", expectedSkill: null }, { name: "implicit-comma-coordinated-negative", prompt: "Autopilot mode, deep interview, and team are prohibited.", expectedSkill: null }, { name: "implicit-for-example-frame", prompt: "For example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-docs-comma-frame", prompt: "According to the docs, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-dont-negative", prompt: "Don’t use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-dont-negative", prompt: "Don't use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-isnt-negative", prompt: "Autopilot mode isn’t allowed.", expectedSkill: null }, { name: "implicit-copular-infinitive-negative", prompt: "Autopilot mode is to be avoided.", expectedSkill: null }, { name: "implicit-past-copular-infinitive-negative", prompt: "Autopilot mode was to be disabled.", expectedSkill: null }, { name: "implicit-article-coordinated-negative", prompt: "Autopilot mode and the deep interview workflow are prohibited.", expectedSkill: null }, { name: "implicit-as-example-frame", prompt: "As an example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-frame", prompt: "For instance, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-reference-link", prompt: "[autopilot mode][docs]", expectedSkill: null }, { name: "implicit-plural-coordinated-negative", prompt: "Autopilot mode and deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-as-example-governing-frame", prompt: "As an example, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-governing-frame", prompt: "For instance, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-definition", prompt: "[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-markdown-shortcut-label", prompt: "[autopilot mode]", expectedSkill: null }, { name: "implicit-markdown-setext", prompt: "Autopilot mode\n===", expectedSkill: null }, { name: "explicit-markdown-pipe-table", prompt: "$ralplan | workflow\n--- | ---", expectedSkill: null }, { name: "implicit-markdown-table-body", prompt: "Mode | Meaning\n--- | ---\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-as-well-as-negative", prompt: "Autopilot mode as well as deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-along-with-negative", prompt: "Autopilot mode along with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-together-with-negative", prompt: "Autopilot mode together with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-ampersand-negative", prompt: "Autopilot mode & deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-example-colon-frame", prompt: "As an example: ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame", prompt: "For instance — ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-example-hyphen-frame", prompt: "For example - ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-colon-frame-no-doc-word", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame-no-doc-word", prompt: "For instance — use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-later-table-body", prompt: "Mode | Meaning\n--- | ---\nmanual | docs\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-embedded-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-parenthetical-as-well-negative", prompt: "Autopilot mode, as well as deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-along-negative", prompt: "Autopilot mode, along with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-together-negative", prompt: "Autopilot mode, together with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-normalized-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-ignore-exclusion", prompt: "Ignore autopilot mode.", expectedSkill: null }, { name: "implicit-skip-exclusion", prompt: "Skip autopilot mode.", expectedSkill: null }, { name: "implicit-exclude-exclusion", prompt: "Exclude autopilot mode.", expectedSkill: null }, { name: "explicit-postposed-prohibited", prompt: "$ralplan is prohibited.", expectedSkill: null }, { name: "explicit-postposed-modal-negative", prompt: "$ralplan should not be run.", expectedSkill: null }, { name: "explicit-postposed-coordinated-negative", prompt: "$ralplan and $autopilot are prohibited.", expectedSkill: null }, { name: "explicit-postposed-article-coordination", prompt: "$ralplan and the $autopilot workflow are prohibited.", expectedSkill: null }, { name: "explicit-postposed-implicit-coordination", prompt: "$ralplan and autopilot mode are prohibited.", expectedSkill: null }, { name: "implicit-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-list-reference-definition", prompt: "See [autopilot mode] for details.\n\n- [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-indented-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "explicit-list-contained-indented-code", prompt: "- $ralplan", expectedSkill: null }, { name: "implicit-list-contained-indented-code", prompt: "1. autopilot mode", expectedSkill: null }, { name: "implicit-four-space-blockquote-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-multiline-reference-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]:\n ./docs", expectedSkill: null }, { name: "explicit-list-tab-code-boundary", prompt: "- \t$ralplan", expectedSkill: null }, { name: "implicit-nested-list-contained-code", prompt: "- - autopilot mode", expectedSkill: null }, { name: "implicit-list-nested-blockquote", prompt: "- > autopilot mode", expectedSkill: null }, { name: "implicit-deep-nested-list-contained-code", prompt: "- - - - - - - - - autopilot mode", expectedSkill: null }, { name: "implicit-ordered-list-nested-blockquote", prompt: "1234. > autopilot mode", expectedSkill: null }, { name: "explicit-nested-list-positive", prompt: "- - $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-four-digit-ordered-list-positive", prompt: "1234. $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-list-fence-then-positive", prompt: "- ```\n $ralplan\n ```\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-nested-list-doc-then-positive", prompt: "- - $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-after-closed-blockquote", prompt: "> quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-fence", prompt: "```text\nquoted context\n```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-indented-code", prompt: " quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-quote", prompt: "\"quoted context\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-prompts-context", prompt: "Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-bare-prompts-precedence", prompt: "Prose\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-after-unclosed-fence", prompt: "```text\nquoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-mismatched-fence", prompt: "```text\nquoted context\n~~~\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-unclosed-quote", prompt: "\"quoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-blockquote-prose", prompt: "> quoted context\nThe docs mention $ralplan only", expectedSkill: null }, { name: "explicit-after-quote-negative", prompt: "\"quoted context\"\nDo not run $ralplan", expectedSkill: null }, { name: "explicit-stale-predecessor-prose", prompt: "> quoted context\nProse\n$ralplan implement this", expectedSkill: null }, { name: "explicit-stale-predecessor-directive-clause", prompt: "> quoted context\nProse\nUse $ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-prompts", prompt: "> quoted context\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-second-block", prompt: "> quoted context\n$ralplan plan it\nLater discussion.\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "explicit-negative-before-unclosed-quote", prompt: "Do not run $ralplan.\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-reference-before-unclosed-quote", prompt: "[$ralplan]: ./docs\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-prompts-inside-unclosed-quote", prompt: "\"Use /prompts:architect\n$ralplan plan it", expectedSkill: null }, { name: "explicit-malformed-prompts-suffix", prompt: "/prompts:architect한글\n$ralplan plan it", expectedSkill: null }, { name: "explicit-nested-list-prompts-positive", prompt: "- Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-nested-list-fence-positive", prompt: "- - ```\n quoted context\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-reference-destination", prompt: "[docs]:\nautopilot", expectedSkill: null }, { name: "explicit-middle-dot-suffix", prompt: "$ralplan·suffix plan it", expectedSkill: null }, { name: "explicit-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-fullwidth-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-postposed-also-negative", prompt: "$ralplan is also prohibited.", expectedSkill: null }, { name: "implicit-postposed-still-negative", prompt: "Autopilot mode is still prohibited.", expectedSkill: null }, { name: "implicit-commonmark-case-fold", prompt: "See [ẞ autopilot mode] for details.\n\n[SS autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-commonmark-escaped-bracket", prompt: "See [foo\\] autopilot mode] for details.\n\n[foo\\] autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-version-decimal-frame", prompt: "For instance: in version 1.2, use autopilot mode.", expectedSkill: null }, { name: "implicit-abbreviation-frame", prompt: "For instance: e.g. use autopilot mode.", expectedSkill: null }, { name: "implicit-slash-documentation", prompt: "Autopilot mode / deep interview are workflow commands.", expectedSkill: null }, { name: "implicit-low-quote", prompt: "„autopilot mode“", expectedSkill: null }, { name: "implicit-list-documentation", prompt: "- autopilot mode is a workflow command", expectedSkill: null }, { name: "implicit-doc-prefix", prompt: "The reference describes autopilot mode.", expectedSkill: null }, { name: "implicit-doc-suffix", prompt: "autopilot mode is workflow documentation.", expectedSkill: null }, { name: "implicit-inline-code", prompt: "`autopilot mode`", expectedSkill: null }, { name: "implicit-blockquote", prompt: "> autopilot mode", expectedSkill: null }, { name: "documentation-suffix", prompt: "$ralplan.md is the workflow documentation file", expectedSkill: null }, { name: "path-suffix", prompt: "$autopilot/config", expectedSkill: null }, { name: "unicode-suffix", prompt: "$ralplan한글", expectedSkill: null }, { name: "zero-width-suffix", prompt: "$ralplan\u200B.md", expectedSkill: null }, { name: "compatibility-path-suffix", prompt: "$ralplan/config", expectedSkill: null }, { name: "nul-suffix", prompt: "$ralplan\u0000md", expectedSkill: null }, { name: "bidi-control-suffix", prompt: "$ralplan\u202Emd", expectedSkill: null }, { name: "bom-suffix", prompt: "$ralplan\uFEFF.md", expectedSkill: null }, { name: "fullwidth-dot-suffix", prompt: "$ralplan.md", expectedSkill: null }, { name: "direct-prompts-reservation", prompt: "/prompts:architect $ralplan plan this", expectedSkill: null }, { name: "list-documentation", prompt: "- $ralplan is the consensus-planning command", expectedSkill: null }, { name: "colon-list-documentation", prompt: "- $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "dash-list-documentation", prompt: "- $ralplan — consensus-planning command", expectedSkill: null }, { name: "plural-list-documentation", prompt: "- $ralplan, $autopilot are workflow commands", expectedSkill: null }, { name: "conjunction-list-documentation", prompt: "- $ralplan and $autopilot are workflow commands", expectedSkill: null }, { name: "oxford-list-documentation", prompt: "- $ralplan, $autopilot, and $team are workflow commands", expectedSkill: null }, { name: "slash-list-documentation", prompt: "- $ralplan / $autopilot are workflow commands", expectedSkill: null }, { name: "compact-slash-list-documentation", prompt: "- $ralplan/$autopilot are workflow commands", expectedSkill: null }, { name: "negative-then-positive", prompt: "Do not run $ralplan; instead $autopilot build issue #3140", expectedSkill: "autopilot" }, { name: "comma-negative-then-positive", prompt: "Do not run $ralplan, instead $autopilot build it", expectedSkill: "autopilot" }, { name: "use-negative-then-positive", prompt: "Do not run $ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "but-use-negative-then-positive", prompt: "Do not run $ralplan but use $autopilot build it", expectedSkill: "autopilot" }, { name: "but-instead-use-negative-then-positive", prompt: "Do not run $ralplan but instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "em-dash-instead-use-positive", prompt: "Do not run $ralplan — instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-positive-contrast", prompt: "Do not use deep interview, but use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-but-instead-positive", prompt: "Do not use deep interview but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-list-verb-positive", prompt: "List files and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-dont-stop-positive", prompt: "No, don't stop.", expectedSkill: "ralph" }, { name: "implicit-doc-then-positive", prompt: "Autopilot mode is workflow documentation.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-then-positive", prompt: "Use /prompts:architect.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-negative-explicit-positive-implicit", prompt: "Do not run $ralplan but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-quoted-explicit-positive-implicit", prompt: "Ignore \"$ralplan\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-positive-implicit", prompt: "Ignore \\/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-prompts-positive-implicit", prompt: "See https://example.com/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-used", prompt: "Autopilot mode should be used.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-enabled", prompt: "Autopilot mode must be enabled.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-run", prompt: "Autopilot mode can be run.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-possessive-positive", prompt: "User's request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "markdown-prompts-link-positive", prompt: "See [/prompts:architect](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-subordinate-positive", prompt: "Use autopilot mode, while deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-sentence-then-positive", prompt: "Read the docs. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-semicolon-then-positive", prompt: "The docs are stale; use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-comma-directive-positive", prompt: "Ignore the docs, use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-explicit-followup", prompt: "Ignore \\/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-explicit-followup", prompt: "See https://example.com/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-explicit-followup", prompt: "See [/prompts:architect](./docs.md)\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "linked-explicit-implicit-positive", prompt: "See [$ralplan](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-explicit-implicit-positive", prompt: "See https://example.com/$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-and-directive-positive", prompt: "Ignore the docs and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-same-line-explicit", prompt: "Ignore \\/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-same-line-explicit", prompt: "See https://example.com/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-same-line-explicit", prompt: "See [/prompts:architect](./docs.md); use $ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-prompts-same-line-explicit", prompt: "See [/prompts:architect][docs]; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-prompts-explicit-followup", prompt: "## /prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "table-prompts-explicit-followup", prompt: "| /prompts:architect |\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-explicit-implicit-positive", prompt: "## $ralplan\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "table-explicit-implicit-positive", prompt: "| $ralplan |\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "reference-explicit-implicit-positive", prompt: "See [$ralplan][docs] and use autopilot mode.", expectedSkill: "autopilot" }, { name: "windows-path-explicit-implicit-positive", prompt: "See C:\\docs\\$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-plural-possessive-positive", prompt: "Users' request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-modal-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview should be avoided.", expectedSkill: "autopilot" }, { name: "docs-but-directive-positive", prompt: "Ignore the docs but use autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-markdown-definition-explicit-followup", prompt: "[/prompts:architect]: ./docs\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "prompts-markdown-shortcut-explicit-followup", prompt: "[/prompts:architect]\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-markdown-definition-implicit-positive", prompt: "[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-shortcut-implicit-positive", prompt: "[$ralplan]\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-setext-implicit-positive", prompt: "$ralplan\n===\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-pipe-table-implicit-positive", prompt: "$ralplan | workflow\n--- | ---\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "heading-explicit-later-explicit", prompt: "## $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "linked-explicit-later-explicit", prompt: "See [$ralplan](./docs.md); use $autopilot build it", expectedSkill: "autopilot" }, { name: "windows-path-explicit-later-explicit", prompt: "See C:\\docs\\$ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-body-explicit-later-explicit", prompt: "Mode | Meaning\n--- | ---\n$ralplan | planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "embedded-shortcut-explicit-implicit-positive", prompt: "See [$ralplan] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "quoted-explicit-later-explicit", prompt: "Ignore \"$ralplan\" and use $autopilot build it", expectedSkill: "autopilot" }, { name: "inline-code-explicit-later-explicit", prompt: "Ignore `$ralplan` and use $autopilot build it", expectedSkill: "autopilot" }, { name: "normalized-shortcut-explicit-implicit-positive", prompt: "See [$ralplan ] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "intro-frame-sentence-reset-positive", prompt: "For instance: manual mode is slower. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "inline-link-overlap-later-explicit", prompt: "See [`$ralplan`](./docs.md) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-exclusion-later-positive", prompt: "Ignore deep interview and use autopilot mode.", expectedSkill: "autopilot" }, { name: "link-destination-later-explicit", prompt: "See [docs](https://example.com/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "link-title-later-explicit", prompt: "See [docs](./docs.md \"$ralplan reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-destination-later-explicit", prompt: "See [docs](https://example.com/(v1)/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-link-text-later-explicit", prompt: "See [$ralplan](https://example.com/(v1)) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "quoted-title-parenthesis-later-explicit", prompt: "See [docs](./docs.md \"$ralplan (reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "blockquote-code-definition-boundary", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: "autopilot" }, { name: "explicit-list-tab-content-boundary", prompt: "-\t $ralplan", expectedSkill: "ralplan" }, { name: "postposed-negative-but-later-explicit", prompt: "$ralplan is prohibited but use $autopilot build it", expectedSkill: "autopilot" }, { name: "postposed-negative-and-later-explicit", prompt: "$ralplan is prohibited and use $autopilot build it", expectedSkill: "autopilot" }, { name: "inline-negative-then-positive", prompt: "Quoted inline-code `$ralplan`; use $autopilot build it", expectedSkill: "autopilot" }, { name: "negative-line-then-positive", prompt: "Without $ralplan.\n$autopilot build it", expectedSkill: "autopilot" }, { name: "quoted-then-positive", prompt: "Quoted example: \"$ralplan plan it\".\n$autopilot build it", expectedSkill: "autopilot" }, { name: "inert-prompts-then-positive", prompt: "\"Use /prompts:architect\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "list-doc-then-positive", prompt: "- $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "prompt-doc-then-positive", prompt: "- /prompts:architect is the prompt command documentation\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "conjunction-list-doc-then-positive", prompt: "- $ralplan and $autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "oxford-list-doc-then-positive", prompt: "- $ralplan, $autopilot, and $team are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "slash-list-doc-then-positive", prompt: "- $ralplan / $autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "compact-slash-list-doc-then-positive", prompt: "- $ralplan/$autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "escaped-quote-range", prompt: "\"$ralplan \\\"; $autopilot build it", expectedSkill: null }, { name: "blockquote-fence-closer", prompt: "```\n$ralplan\n> ```\n$autopilot build it", expectedSkill: null }, { name: "double-negative-control", prompt: "Do not run $ralplan; do not run $autopilot", expectedSkill: null }, { name: "comma-negative-control", prompt: "Do not run $ralplan, $autopilot", expectedSkill: null }, { name: "unseparated-control", prompt: "Do not run $ralplan and use $autopilot build it", expectedSkill: null }, { name: "even-escape-control", prompt: "\"$ralplan \\\\\"; use $autopilot build it", expectedSkill: "autopilot" }, { name: "matching-fence-control", prompt: "> ```\n> $ralplan\n> ```\n$autopilot build it", expectedSkill: "autopilot" }, { name: "invalid-fence-closer", prompt: "```\n$ralplan\n``` still code\n$autopilot build it", expectedSkill: null }, { name: "documentation-clause-control", prompt: "Do not run $ralplan. We only document $autopilot behavior", expectedSkill: null }, { name: "punctuation-multi-workflow", prompt: "$ralplan, $autopilot build issue #3140", expectedSkill: "ralplan", expectedDeferredSkills: ["autopilot"], }, ] as const; for (const [caseIndex, testCase] of cases.entries()) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-compiled-classification-${caseIndex}-`)); const sessionId = `sess-compiled-${caseIndex}`; const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_SESSION_ID: "", CODEX_SESSION_ID: "", OMX_TEAM_STATE_ROOT: "", OMX_TEAM_WORKER: "", OMX_TEAM_INTERNAL_WORKER: "", OMX_TEAM_LEADER_CWD: "", OMX_TEAM_MODE: "insideTmux" in testCase && testCase.insideTmux ? "enabled" : "", SESSION_ID: "", OMX_QUESTION_RETURN_PANE: "", OMX_LEADER_PANE_ID: "", OMX_TMUX_HUD_OWNER: "", TMUX: "insideTmux" in testCase && testCase.insideTmux ? "/tmp/tmux-pr3140-regression" : "", TMUX_PANE: "insideTmux" in testCase && testCase.insideTmux ? "%3140" : "", }; try { const submit = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${caseIndex}`, turn_id: `turn-${caseIndex}`, prompt: testCase.prompt, }, { cwd, env })); assert.equal(submit.continue, undefined, testCase.name); const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); const skillStatePath = join(sessionDir, "skill-active-state.json"); const expectsAutopilotDenial = testCase.expectedSkill === "autopilot"; if (testCase.expectedSkill === null) { assert.equal(existsSync(skillStatePath), false, testCase.name); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false, testCase.name); assert.equal(existsSync(join(sessionDir, "autopilot-state.json")), false, testCase.name); } else { assert.equal(existsSync(skillStatePath), true, testCase.name); const skillState = JSON.parse(await readFile(skillStatePath, "utf-8")) as { active?: boolean; skill?: string; phase?: string; error?: string; deferred_skills?: string[]; active_skills?: Array<{ skill?: string }>; }; if (expectsAutopilotDenial) { assert.equal(skillState.active, false, testCase.name); assert.equal(skillState.skill, "autopilot", testCase.name); assert.equal(skillState.phase, "failed", testCase.name); assert.equal(skillState.error, "documented_host_consensus_receipt_unavailable", testCase.name); assert.deepEqual(skillState.active_skills, [], testCase.name); const guidance = String((submit.hookSpecificOutput as { additionalContext?: string } | undefined)?.additionalContext ?? ""); assert.match(guidance, /documented_host_consensus_receipt_unavailable/, testCase.name); assert.doesNotMatch(guidance, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal/, testCase.name); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false, testCase.name); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false, testCase.name); } else { assert.equal(skillState.active, true, testCase.name); assert.equal(skillState.skill, testCase.expectedSkill, testCase.name); } if ("expectedDeferredSkills" in testCase) { assert.deepEqual(skillState.deferred_skills ?? [], testCase.expectedDeferredSkills, testCase.name); } if ("expectedActiveSkills" in testCase) { assert.deepEqual(skillState.active_skills?.map((entry) => entry.skill) ?? [], testCase.expectedActiveSkills, testCase.name); } if ("expectedActiveDetailSkills" in testCase) { const activeDetailSkills = (await Promise.all(["ralplan", "autopilot"].map(async (skill) => { const detailPath = join(sessionDir, `${skill}-state.json`); if (!existsSync(detailPath)) return null; const detailState = JSON.parse(await readFile(detailPath, "utf-8")) as { active?: boolean }; return detailState.active ? skill : null; }))).filter((skill): skill is string => skill !== null); assert.deepEqual(activeDetailSkills, testCase.expectedActiveDetailSkills, testCase.name); } } const stop = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${caseIndex}`, turn_id: `stop-${caseIndex}`, }, { cwd, env })); if (testCase.expectedSkill === null || expectsAutopilotDenial) assert.deepEqual(stop, {}, testCase.name); else if ("expectedStopBlock" in testCase && !testCase.expectedStopBlock) { assert.notEqual(stop.decision, "block", testCase.name); } else assert.equal(stop.decision, "block", testCase.name); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps terminal Autopilot failed and inactive when disabled-Team restart lacks host receipt verification", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-disabled-team-terminal-restart-")); const sessionId = "sess-disabled-team-terminal-restart"; const threadId = "thread-disabled-team-terminal-restart"; const turnId = "turn-disabled-team-terminal-restart"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); const autopilotPath = join(sessionDir, "autopilot-state.json"); try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled", }); await writeJson(autopilotPath, { mode: "autopilot", active: false, current_phase: "complete", completed_at: "2026-07-12T23:00:00.000Z", session_id: sessionId, thread_id: threadId, turn_id: turnId, }); const before = await readFile(autopilotPath); const restart = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: turnId, prompt: "$team $autopilot retry", }, { cwd }); assert.equal(restart.skillState?.skill, "autopilot"); assert.equal(restart.skillState?.active, false); assert.equal(restart.skillState?.phase, "failed"); assert.equal(restart.skillState?.error, "documented_host_consensus_receipt_unavailable"); assert.deepEqual(restart.skillState?.active_skills, []); const restartGuidance = String((restart.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""); assert.match(restartGuidance, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(restartGuidance, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal/); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); const restartedState = JSON.parse(await readFile(autopilotPath, "utf-8")) as { active?: boolean; current_phase?: string; error?: string; }; assert.notDeepEqual(await readFile(autopilotPath), before); assert.equal(restartedState.active, false); assert.equal(restartedState.current_phase, "failed"); assert.equal(restartedState.error, "documented_host_consensus_receipt_unavailable"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); const stop = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: "stop-disabled-team-terminal-restart", }, { cwd, env: { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "" }, })); assert.deepEqual(stop, {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); async function assertG2aDocumentationReferenceIsInert(transport: "raw" | "compiled"): Promise { const name = `g2a-${transport}`; const cwd = await mkdtemp(join(tmpdir(), `omx-native-${name}-`)); const sessionId = `sess-${name}`; const threadId = `thread-${name}`; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); const skillDetailPaths = [ join(stateDir, "skill-active-state.json"), join(stateDir, "ralplan-state.json"), join(sessionDir, "skill-active-state.json"), join(sessionDir, "ralplan-state.json"), ]; const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_TEAM_MODE: "" }; const payload = { hook_event_name: "UserPromptSubmit" as const, cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: `turn-${name}`, prompt: "use $ralplan is the consensus-planning command" }; try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled" }); assert.deepEqual(skillDetailPaths.map(existsSync), [false, false, false, false], name); if (transport === "raw") { const submit = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(submit.skillState, null, name); assert.equal(submit.outputJson, null, name); } else { assert.deepEqual(parseSingleJsonStdout(runNativeHookCli(payload, { cwd, env })), {}, name); } assert.deepEqual(skillDetailPaths.map(existsSync), [false, false, false, false], name); const stopPayload = { ...payload, hook_event_name: "Stop" as const, turn_id: `stop-${name}` }; if (transport === "raw") { assert.equal((await dispatchCodexNativeHook(stopPayload, { cwd })).outputJson, null, name); } else { assert.notEqual(parseSingleJsonStdout(runNativeHookCli(stopPayload, { cwd, env })).decision, "block", name); } assert.deepEqual(skillDetailPaths.map(existsSync), [false, false, false, false], name); } finally { await rm(cwd, { recursive: true, force: true }); } } async function assertG2bTerminalStateIsInert(transport: "raw" | "compiled"): Promise { const name = `g2b-${transport}`; const cwd = await mkdtemp(join(tmpdir(), `omx-native-${name}-`)); const sessionId = `sess-${name}`; const threadId = `thread-${name}`; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); const paths = [ join(stateDir, "skill-active-state.json"), join(stateDir, "autopilot-state.json"), join(sessionDir, "skill-active-state.json"), join(sessionDir, "autopilot-state.json"), join(stateDir, "session.json"), ]; const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_TEAM_MODE: "" }; const payload = { hook_event_name: "UserPromptSubmit" as const, cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: `turn-${name}`, prompt: "do not start $autopilot — café" }; try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled" }); await writeJson(paths[0], { active: false, skill: "ralplan", phase: "complete", session_id: "root-skill-session", thread_id: "root-skill-thread", turn_id: "root-skill-turn", completed_at: "2026-07-01T00:00:00.000Z", updated_at: "2026-07-01T00:00:01.000Z" }); await writeJson(paths[1], { active: false, mode: "autopilot", current_phase: "complete", session_id: "root-detail-session", thread_id: "root-detail-thread", turn_id: "root-detail-turn", completed_at: "2026-07-02T00:00:00.000Z", updated_at: "2026-07-02T00:00:01.000Z" }); await writeJson(paths[2], { active: false, skill: "team", phase: "complete", session_id: "session-skill-session", thread_id: "session-skill-thread", turn_id: "session-skill-turn", completed_at: "2026-07-03T00:00:00.000Z", updated_at: "2026-07-03T00:00:01.000Z" }); await writeJson(paths[3], { active: false, mode: "autopilot", current_phase: "complete", session_id: "session-detail-session", thread_id: "session-detail-thread", turn_id: "session-detail-turn", completed_at: "2026-07-04T00:00:00.000Z", updated_at: "2026-07-04T00:00:01.000Z" }); await writeJson(paths[4], { session_id: sessionId, native_session_id: `native-${name}`, thread_id: "pointer-thread", turn_id: "pointer-turn", created_at: "2026-07-05T00:00:00.000Z", updated_at: "2026-07-05T00:00:01.000Z", cwd }); const buffers = await Promise.all(paths.map((path) => readFile(path))); if (transport === "raw") { const submit = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(submit.skillState, null, name); assert.equal(submit.outputJson, null, name); } else { assert.deepEqual(parseSingleJsonStdout(runNativeHookCli(payload, { cwd, env })), {}, name); } assert.deepEqual(await Promise.all(paths.map((path) => readFile(path))), buffers, name); const stopPayload = { ...payload, hook_event_name: "Stop" as const, turn_id: `stop-${name}` }; if (transport === "raw") { assert.equal((await dispatchCodexNativeHook(stopPayload, { cwd })).outputJson, null, name); } else { assert.notEqual(parseSingleJsonStdout(runNativeHookCli(stopPayload, { cwd, env })).decision, "block", name); } assert.deepEqual(await Promise.all(paths.map((path) => readFile(path))), buffers, name); } finally { await rm(cwd, { recursive: true, force: true }); } } it("keeps G2a documentation reference state-free through raw UserPromptSubmit and Stop", async () => { await assertG2aDocumentationReferenceIsInert("raw"); }); it("keeps G2a documentation reference state-free through compiled UserPromptSubmit and Stop", async () => { await assertG2aDocumentationReferenceIsInert("compiled"); }); it("keeps G2b negated terminal root/session conflict byte-identical through raw UserPromptSubmit and Stop", async () => { await assertG2bTerminalStateIsInert("raw"); }); it("keeps G2b negated terminal root/session conflict byte-identical through compiled UserPromptSubmit and Stop", async () => { await assertG2bTerminalStateIsInert("compiled"); }); async function assertG1bURestart(transport: "raw" | "compiled"): Promise { const cwd = await mkdtemp(join(tmpdir(), `omx-native-${transport}-g1bu-restart-`)); const sessionId = `sess-g1bu-${transport}`; const threadId = `thread-g1bu-${transport}`; const priorTurnId = `turn-g1bu-${transport}-old`; const turnId = `turn-g1bu-${transport}-new`; const prompt = "$team $autopilot restart — café"; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); const sessionSkillPath = join(sessionDir, "skill-active-state.json"); const sessionAutopilotPath = join(sessionDir, "autopilot-state.json"); const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_TEAM_MODE: "", TMUX: "", TMUX_PANE: "" }; try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled" }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "completing", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "root-skill", active_skills: [{ skill: "autopilot", phase: "completing", active: true, session_id: sessionId, thread_id: threadId, turn_id: priorTurnId }] }); await writeJson(join(stateDir, "autopilot-state.json"), { active: false, mode: "autopilot", current_phase: "complete", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "root-autopilot" }); await writeJson(sessionSkillPath, { active: true, skill: "autopilot", phase: "completing", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "session-skill", active_skills: [{ skill: "autopilot", phase: "completing", active: true, session_id: sessionId, thread_id: threadId, turn_id: priorTurnId }] }); await writeJson(sessionAutopilotPath, { active: false, mode: "autopilot", current_phase: "complete", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "session-autopilot" }); const payload = { hook_event_name: "UserPromptSubmit" as const, cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: turnId, prompt }; let output: Record | null; if (transport === "raw") { const result = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, true); output = result.outputJson; } else { const input = Buffer.from(JSON.stringify(payload), "utf-8"); assert.equal(input.toString("utf-8"), JSON.stringify(payload)); output = parseSingleJsonStdout(runNativeHookCli(input.toString("utf-8"), { cwd, env })); } assert.doesNotMatch(JSON.stringify(output), /\$team" -> team/); const skillState = JSON.parse(await readFile(sessionSkillPath, "utf-8")) as { active?: boolean; skill?: string; turn_id?: string; active_skills?: Array<{ skill?: string }> }; assert.equal(skillState.active, true); assert.equal(skillState.skill, "autopilot"); assert.equal(skillState.turn_id, turnId); assert.deepEqual(skillState.active_skills?.map((entry) => entry.skill), ["autopilot"]); const autopilotState = JSON.parse(await readFile(sessionAutopilotPath, "utf-8")) as { active?: boolean; current_phase?: string; turn_id?: string }; assert.equal(autopilotState.active, true); assert.equal(autopilotState.current_phase, "deep-interview"); assert.equal(autopilotState.turn_id, turnId); assert.equal(existsSync(join(stateDir, "team-state.json")), false); const stopPayload = { ...payload, hook_event_name: "Stop" as const, turn_id: `stop-g1bu-${transport}` }; if (transport === "raw") { assert.equal((await dispatchCodexNativeHook(stopPayload, { cwd })).outputJson?.decision, "block"); } else { assert.equal(parseSingleJsonStdout(runNativeHookCli(stopPayload, { cwd, env })).decision, "block"); } } finally { await rm(cwd, { recursive: true, force: true }); } } it("restarts terminal Autopilot through disabled-Team raw UserPromptSubmit for G1b-U", async () => { await assertG1bURestart("raw"); }); it("restarts terminal Autopilot through disabled-Team compiled UserPromptSubmit for G1b-U", async () => { await assertG1bURestart("compiled"); }); it("does not crash Stop hook dispatch when the exec follow-up queue is malformed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-exec-followup-corrupt-")); try { const session = await writeSessionStart(cwd, "sess-exec-followup-corrupt"); const queuePath = join(cwd, ".omx", "state", "sessions", session.session_id, "exec-followups.json"); await mkdir(dirname(queuePath), { recursive: true }); await writeFile(queuePath, '{"version":1,"records":[', "utf-8"); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: session.session_id, }); assert.equal(result.hookEventName, "Stop"); assert.equal(result.outputJson, null); const queueDirEntries = await readdir(dirname(queuePath)); assert.ok( queueDirEntries.some((entry) => entry.startsWith("exec-followups.json.corrupt-"), ), ); const auditPath = join( cwd, ".omx", "logs", `exec-followups-${new Date().toISOString().slice(0, 10)}.jsonl`, ); assert.match( await readFile(auditPath, "utf-8"), /exec_followup_queue_corrupt_recovered/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop on stale session autopilot mirror when canonical skill state is inactive", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-stale-autopilot-mirror-"), ); try { const sessionId = "sess-stale-autopilot-mirror"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(cwd, ".omx", "state", "skill-active-state.json"), { version: 1, active: false, skill: "", phase: "cancelled", active_skills: [], }); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json", ), { active: true, mode: "autopilot", current_phase: "ultragoal", session_id: sessionId, }, ); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json", ), { version: 1, active: true, skill: "autopilot", phase: "ultragoal", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "ultragoal", active: true, session_id: sessionId, }, ], }, ); const stdout = runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stale-autopilot-mirror", }, { cwd }, ); assert.deepEqual(parseSingleJsonStdout(stdout), {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("includes blocking state source and canonical agreement in Stop diagnostics", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-diagnostic-"), ); try { const sessionId = "sess-autopilot-diagnostic"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(cwd, ".omx", "state", "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ultragoal", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "ultragoal", active: true, session_id: sessionId, }, ], }); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json", ), { active: true, mode: "autopilot", current_phase: "ultragoal", session_id: sessionId, }, ); const output = parseSingleJsonStdout( runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-autopilot-diagnostic", }, { cwd }, ), ); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_ultragoal"); assert.match( String(output.reason ?? ""), /state: \.omx\/state\/sessions\/sess-autopilot-diagnostic\/autopilot-state\.json/, ); assert.match(String(output.reason ?? ""), /canonical: canonical_agrees/); assert.match( String(output.systemMessage ?? ""), /state: \.omx\/state\/sessions\/sess-autopilot-diagnostic\/autopilot-state\.json/, ); assert.match( String(output.systemMessage ?? ""), /canonical: canonical_agrees/, ); assert.deepEqual(Object.keys(output).sort(), [ "decision", "reason", "stopReason", "systemMessage", ]); assert.equal("statePath" in output, false); assert.equal("canonicalDisagreement" in output, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps canonical phase disagreements in active Autopilot Stop diagnostics", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-canonical-phase-"), ); try { const sessionId = "sess-autopilot-canonical-phase"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(cwd, ".omx", "state", "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "execution", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "execution", active: true, session_id: sessionId, }, ], }); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json", ), { active: true, mode: "autopilot", current_phase: "ultragoal", session_id: sessionId, }, ); const output = parseSingleJsonStdout( runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-autopilot-canonical-phase", }, { cwd }, ), ); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_ultragoal"); assert.match( String(output.reason ?? ""), /canonical: canonical_phase:execution/, ); assert.match( String(output.systemMessage ?? ""), /canonical: canonical_phase:execution/, ); assert.deepEqual(Object.keys(output).sort(), [ "decision", "reason", "stopReason", "systemMessage", ]); assert.equal("statePath" in output, false); assert.equal("canonicalDisagreement" in output, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits exactly one parseable JSON object for active Stop CLI continuation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-json-")); try { await writeActiveAutopilotSession(cwd, "sess-cli-stop-json"); const stdout = runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-json", thread_id: "thread-cli-stop-json", turn_id: "turn-cli-stop-json", }, { cwd }, ); const output = parseSingleJsonStdout(stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_execution"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps noisy Stop hook plugin stdout out of native Stop CLI stdout", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-noisy-plugin-"), ); try { await writeActiveAutopilotSession(cwd, "sess-cli-stop-noisy-plugin"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "noisy.mjs"), `export async function onHookEvent(event) { if (event.event === "stop") console.log("PLUGIN_NOISE"); } `, "utf-8", ); const stdout = runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-noisy-plugin", thread_id: "thread-cli-stop-noisy-plugin", turn_id: "turn-cli-stop-noisy-plugin", }, { cwd }); assert.doesNotMatch(stdout, /PLUGIN_NOISE/); const output = parseSingleJsonStdout(stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_execution"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits deterministic Stop JSON stdout when Stop dispatch fails", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-dispatch-failure-")); try { const stdout = runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-dispatch-failure", thread_id: "thread-cli-stop-dispatch-failure", turn_id: "turn-cli-stop-dispatch-failure", }, { cwd, env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_STOP_DISPATCH: "1", }, }); const output = parseSingleJsonStdout(stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "native_stop_dispatch_failure"); assert.match(String(output.reason), /failed before normal continuation handling/); assert.match(String(output.systemMessage), /test-induced Stop dispatch failure/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("logs Stop dispatch failures without foreground stderr noise", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-dispatch-silent-")); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-dispatch-silent", thread_id: "thread-cli-stop-dispatch-silent", turn_id: "turn-cli-stop-dispatch-silent", }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_STOP_DISPATCH: "1", }, }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.stopReason, "native_stop_dispatch_failure"); const logFiles = await readdir(join(cwd, ".omx", "logs")); assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps non-Stop dispatch failures fail-closed without foreground stderr noise", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-pretool-dispatch-silent-")); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-dispatch-silent", thread_id: "thread-cli-pretool-dispatch-silent", turn_id: "turn-cli-pretool-dispatch-silent", tool_name: "Bash", tool_input: { command: "pwd" }, }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1", }, }); assert.equal(result.status, 1); assert.equal(result.stdout, ""); assert.equal(result.stderr, ""); const logFiles = await readdir(join(cwd, ".omx", "logs")); assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("maps Codex events onto OMX logical surfaces", () => { assert.equal(mapCodexHookEventToOmxEvent("SessionStart"), "session-start"); assert.equal(mapCodexHookEventToOmxEvent("UserPromptSubmit"), "keyword-detector"); assert.equal(mapCodexHookEventToOmxEvent("PreToolUse"), "pre-tool-use"); assert.equal(mapCodexHookEventToOmxEvent("PostToolUse"), "post-tool-use"); assert.equal(mapCodexHookEventToOmxEvent("PreCompact"), "pre-compact"); assert.equal(mapCodexHookEventToOmxEvent("PostCompact"), "post-compact"); assert.equal(mapCodexHookEventToOmxEvent("Stop"), "stop"); }); it("does not write PreCompact stdout that Codex rejects as hook JSON", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-")); try { writePage(cwd, { filename: "architecture.md", frontmatter: { title: "Architecture", tags: ["architecture"], created: "2026-05-08T00:00:00.000Z", updated: "2026-05-08T00:00:00.000Z", sources: [], links: [], category: "architecture", confidence: "high", schemaVersion: WIKI_SCHEMA_VERSION, }, content: "\n# Architecture\n\nCompaction-relevant architecture note.\n", }); const result = await dispatchCodexNativeHook({ hook_event_name: "PreCompact", cwd, session_id: "sess-precompact", }); assert.equal(result.hookEventName, "PreCompact"); assert.equal(result.omxEventName, "pre-compact"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no CLI stdout for PreCompact when no Codex action is needed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-cli-")); try { writePage(cwd, { filename: "architecture.md", frontmatter: { title: "Architecture", tags: ["architecture"], created: "2026-05-08T00:00:00.000Z", updated: "2026-05-08T00:00:00.000Z", sources: [], links: [], category: "architecture", confidence: "high", schemaVersion: WIKI_SCHEMA_VERSION, }, content: "\n# Architecture\n\nCompaction-relevant architecture note.\n", }); const stdout = runNativeHookCli({ hook_event_name: "PreCompact", cwd, session_id: "sess-precompact-cli", }); assert.equal(stdout, ""); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not write PostCompact stdout that Codex rejects as hook JSON", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-")); try { const result = await dispatchCodexNativeHook({ hook_event_name: "PostCompact", cwd, session_id: "sess-postcompact", }); assert.equal(result.hookEventName, "PostCompact"); assert.equal(result.omxEventName, "post-compact"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no CLI stdout for PostCompact when no Codex action is needed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-cli-")); try { const stdout = runNativeHookCli({ hook_event_name: "PostCompact", cwd, session_id: "sess-postcompact-cli", }); assert.equal(stdout, ""); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("writes SessionStart state against the long-lived session owner pid and injects environment context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-1", }, { cwd, sessionOwnerPid: 43210, }, ); assert.equal(result.omxEventName, "session-start"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /native-hook \/ Codex App outside tmux/); assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) need an attached tmux OMX CLI shell|omx team and omx hud need an attached tmux OMX CLI shell/); assert.match(additionalContext, /not available from this outside-tmux surface/); const sessionState = JSON.parse( await readFile(join(cwd, ".omx", "state", "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string; pid?: number; launch_lineage_token?: string }; assert.equal(sessionState.session_id, "sess-start-1"); assert.equal(sessionState.native_session_id, "sess-start-1"); assert.equal(sessionState.pid, 43210); assert.equal(sessionState.launch_lineage_token, undefined, 'native SessionStart must never mint or backfill wrapper lineage authority'); const ownerState = JSON.parse( await readFile( join(cwd, ".omx", "state", "sessions", "sess-start-1", "session-owner.json"), "utf-8", ), ) as { session_id?: string; native_session_id?: string; pid?: number }; assert.equal(ownerState.session_id, "sess-start-1"); assert.equal(ownerState.native_session_id, "sess-start-1"); assert.equal(ownerState.pid, 43210); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves wrapper lineage and detached metadata through shipped native SessionStart reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-wrapper-lineage-")); try { const established = await establishLaunchSessionBinding(cwd, "omx-launch-lineage"); assert.equal(established.kind, "committed-released"); if (established.kind !== "committed-released") return; const metadata = await updateDetachedSessionMetadata(established.binding, { tmuxSessionName: "omx-detached-lineage", tmuxPaneId: "%3202", }); assert.equal(metadata.kind, "committed-released"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const ownedPointer = JSON.parse(await readFile(pointerPath, "utf-8")) as Record; await writeFile(pointerPath, JSON.stringify({ ...ownedPointer, owner_omx_session_id: "omx-launch-lineage" }), "utf-8"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-native-lineage" }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse( await readFile(join(cwd, ".omx", "state", "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; started_at?: string; launch_lineage_token?: string; tmux_session_name?: string; tmux_pane_id?: string; }; assert.equal(sessionState.session_id, "omx-launch-lineage"); assert.equal(sessionState.native_session_id, "codex-native-lineage"); assert.equal(sessionState.owner_omx_session_id, "omx-launch-lineage"); assert.equal(typeof sessionState.started_at, "string"); assert.equal(sessionState.launch_lineage_token, established.binding.launchLineageToken); assert.equal(sessionState.tmux_session_name, "omx-detached-lineage"); assert.equal(sessionState.tmux_pane_id, "%3202"); const firstFinalization = finalizeBoundOnce(established.binding, "test"); const secondFinalization = finalizeBoundOnce(established.binding, "duplicate"); assert.equal(firstFinalization, secondFinalization); assert.equal((await firstFinalization).finalized, true); assert.equal((await closeLaunchSessionBindingOnce(established.binding)).status, "closed"); assert.equal((await closeLaunchSessionBindingOnce(established.binding)).status, "closed"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it('does not advertise a neutralized Ralplan seed in SessionStart context', async () => { const cwd = await mkdtemp(join(tmpdir(), 'omx-native-hook-neutralized-ralplan-')); const previousSessionId = process.env.OMX_SESSION_ID; try { const sessionId = 'neutralized-session'; await writeSessionStart(cwd, sessionId); const directory = join(cwd, '.omx', 'state', 'sessions', sessionId); await mkdir(directory, { recursive: true }); await writeFile(join(directory, 'ralplan-state.json'), JSON.stringify({ active: true, mode: 'ralplan', current_phase: 'planning', started_at: '2026-01-01T00:00:00.000Z', updated_at: '2026-01-01T00:00:00.000Z', session_id: sessionId })); await writeFile(join(directory, 'skill-active-state.json'), JSON.stringify({ version: 1, active: true, skill: 'ralplan', keyword: '$RALPLAN', phase: 'planning', activated_at: '2026-01-01T00:00:00.000Z', updated_at: '2026-01-01T00:00:00.000Z', source: 'keyword-detector', session_id: sessionId, initialized_mode: 'ralplan', initialized_state_path: `.omx/state/sessions/${sessionId}/ralplan-state.json`, active_skills: [{ skill: 'ralplan', active: true, phase: 'planning', activated_at: '2026-01-01T00:00:00.000Z', updated_at: '2026-01-01T00:00:00.000Z', session_id: sessionId }] })); process.env.OMX_SESSION_ID = sessionId; assert.equal(await neutralizeOwnedRoutingRalplan(cwd), true); const result = await dispatchCodexNativeHook({ hook_event_name: 'SessionStart', cwd, session_id: sessionId }, { cwd }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ''); assert.doesNotMatch(context, /- ralplan phase:/); } finally { previousSessionId === undefined ? delete process.env.OMX_SESSION_ID : process.env.OMX_SESSION_ID = previousSessionId; await rm(cwd, { recursive: true, force: true }); } }); it("reconciles native SessionStart on Windows EPERM with one degraded-durability warning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-windows-eperm-")); const originalWrite = process.stderr.write; const warnings: string[] = []; process.stderr.write = ((value: string) => { warnings.push(value); return true; }) as typeof process.stderr.write; try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-windows-eperm" }, { cwd, sessionStartOptions: { platform: "win32", regularFileSync: async () => { throw Object.assign(new Error("EPERM"), { code: "EPERM" }); }, }, }, ); assert.deepEqual(warnings, [ "[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer start/reconcile; operation succeeded with degraded durability.\n", ]); } finally { process.stderr.write = originalWrite; await rm(cwd, { recursive: true, force: true }); } }); it("adds resume-by-id instructions for persisted subagents on SessionStart resume", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-reopen-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-reopen-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reopen", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-reopen", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-reopen": { thread_id: "codex-leader-reopen", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-architect-reopen": { thread_id: "thread-architect-reopen", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 2, role: "architect", lane_id: "plan-review", scope: "SessionStart reopen", status: "available", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-reopen", direct_child_parent_id: "codex-leader-reopen", last_handoff_summary: "reviewed the restart plan", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-reopen", source: "resume", }, { cwd, sessionOwnerPid: process.pid }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Persisted subagent reopen\]/); assert.match(additionalContext, /resume_agent\("thread-architect-reopen"\)/); assert.match(additionalContext, /role: architect; lane: plan-review; scope: SessionStart reopen; status: available/); assert.match(additionalContext, /saved subagent ids found: 1/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")) as { sessions?: Record }>; }; assert.match( tracking.sessions?.[sessionId]?.threads?.["thread-architect-reopen"]?.resume_requested_at ?? "", /^\d{4}-\d{2}-\d{2}T/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const status of ["closed", "available"] as const) { it(`issue #3284 excludes the current root when persisted as a ${status} subagent`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-root-self-${status}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-${status}`; const rootNativeSessionId = `codex-root-${status}`; const childId = `codex-child-${status}`; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: `turn-like-${status}`, updated_at: "2026-07-23T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status, first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, [childId]: { thread_id: childId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, first_seen_at: "2026-07-23T00:01:00.000Z", last_seen_at: "2026-07-23T00:01:00.000Z", turn_count: 1 }, } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, leader_thread_id: `turn-like-correlation-${status}`, updated_at: "2026-07-23T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status, provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, resume_requested_at: "2026-07-23T00:02:00.000Z", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(rootNativeSessionId)})`), false); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), true); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].resume_requested_at, undefined); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[canonicalSessionId].leader_thread_id, rootNativeSessionId); assert.equal(tracking.sessions[rootNativeSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[rootNativeSessionId].threads[rootNativeSessionId].resume_requested_at, undefined); assert.equal(tracking.sessions[rootNativeSessionId].threads[rootNativeSessionId].direct_child_root_id, undefined); assert.equal(tracking.sessions[rootNativeSessionId].leader_thread_id, rootNativeSessionId); assert.match(tracking.sessions[canonicalSessionId].threads[childId].resume_requested_at, /^\d{4}-\d{2}-\d{2}T/); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 excludes a delegated top-level root labelled thread_source subagent with no parent evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-delegated-root-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-delegated"; const rootNativeSessionId = "codex-delegated-root"; const childId = "codex-delegated-child"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); // Evidence comment 5069022871: rollout provenance marks the task // thread_source "subagent", yet it has no parent_thread_id/forked_from_id // and is the root of its current collaboration tree. const transcriptPath = join(cwd, "rollout-delegated-root.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: rootNativeSessionId, source: { thread_source: "subagent" } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "closed", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, [childId]: { thread_id: childId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, first_seen_at: "2026-07-24T00:01:00.000Z", last_seen_at: "2026-07-24T00:01:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume", transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(rootNativeSessionId)})`), false); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), true); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[canonicalSessionId].leader_thread_id, rootNativeSessionId); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].resume_requested_at, undefined); assert.match(tracking.sessions[canonicalSessionId].threads[childId].resume_requested_at, /^\d{4}-\d{2}-\d{2}T/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 repairs a root inversion on a non-reopen SessionStart source without emitting reopen output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-repair-other-source-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-other-source"; const rootNativeSessionId = "codex-root-other-source"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "closed", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "clear" }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /\[Persisted subagent reopen\]/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[canonicalSessionId].leader_thread_id, rootNativeSessionId); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const source of ["startup", "resume"] as const) { it(`issue #3284 quarantines root reopen authority without asserting leader identity for a self-parented child SessionStart on ${source}`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-self-parented-${source}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-self-parented-${source}`; const nativeRoleThreadId = `codex-role-thread-${source}`; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: nativeRoleThreadId, pid: process.pid }); const transcriptPath = join(cwd, `rollout-self-parented-${source}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: nativeRoleThreadId, source: { subagent: { thread_spawn: { parent_thread_id: nativeRoleThreadId, depth: 1, agent_nickname: "Architect", agent_role: "architect" } } } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [nativeRoleThreadId]: { thread_id: nativeRoleThreadId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: nativeRoleThreadId, direct_child_parent_id: nativeRoleThreadId, resume_requested_at: "2026-07-24T00:02:00.000Z", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: nativeRoleThreadId, source, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /\[Persisted subagent reopen\]/); assert.doesNotMatch(context, /resume_agent\(/); // The transcript marker names this very root as its own child, so it is // not distinct-child evidence and cannot override the authenticated // pointer identity. The root's reopen authority is quarantined without // asserting leader identity, so descriptive subagent evidence survives. const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); const rootRecord = tracking.sessions[canonicalSessionId].threads[nativeRoleThreadId]; assert.equal(rootRecord.kind, "subagent"); assert.equal(rootRecord.reopen_authority_revoked, true); assert.equal(rootRecord.reopen_authority_conflict_reason, "contradictory_root_child_evidence"); assert.equal(rootRecord.resume_requested_at, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 suppresses root handling only for an authenticated direct-child SessionStart", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-distinct-child-suppression-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-distinct-child"; const rootNativeSessionId = "root-3284-distinct-child"; const childId = "child-3284-distinct"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const transcriptPath = join(cwd, "rollout-distinct-child.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId, depth: 1 } } } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "available", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); // The event's own unambiguous native id IS the marker's child, and the // marker's parent is exactly the authenticated native root, so this is a // real direct-child session start and must not act as a root observation. const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: childId, sessionId: childId, source: "resume", transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /resume_agent\(/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "subagent"); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].reopen_authority_revoked, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const marker of [ { name: "arbitrary-nonexistent-child", childId: "made-up-child", parentId: "made-up-parent" }, { name: "nested-grandchild", childId: "grandchild-3284", parentId: "intermediate-3284" }, { name: "foreign-root-child", childId: "foreign-child-3284", parentId: "foreign-root-3284" }, ] as const) { it(`issue #3284 does not let an unauthenticated ${marker.name} marker suppress root quarantine`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-unauth-marker-${marker.name}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-unauth-${marker.name}`; const rootNativeSessionId = `root-3284-unauth-${marker.name}`; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const transcriptPath = join(cwd, `unauth-${marker.name}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: marker.childId, source: { subagent: { thread_spawn: { parent_thread_id: marker.parentId, depth: 1 } } } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, resume_requested_at: "2026-07-24T00:02:00.000Z", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume", transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /resume_agent\(/); // The marker is not bound to the authenticated event identity, so the // pointer-authoritative root never gains reopen authority: it is denied // either by the dispatcher's foreign-parent revocation or by the // contradictory-evidence quarantine. Both are fail-closed. const rootRecord = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")) .sessions[canonicalSessionId].threads[rootNativeSessionId]; assert.equal(rootRecord.reopen_authority_revoked, true); assert.ok( ["contradictory_root_child_evidence", "foreign_parent", "identity_untrusted"].includes(rootRecord.reopen_authority_conflict_reason), `unexpected conflict reason: ${rootRecord.reopen_authority_conflict_reason}`, ); // Reopen eligibility is what matters: the revoked root can never be // emitted regardless of leftover descriptive bookkeeping. assert.equal(rootRecord.direct_child_root_id !== undefined && rootRecord.reopen_authority_revoked !== true, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 fails closed when SessionStart native id aliases conflict", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-alias-conflict-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-alias"; const rootNativeSessionId = "codex-root-alias"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { child: { thread_id: "child", kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, } }, } }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, sessionId: "foreign-root", source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /\[Persisted subagent reopen\]/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads.child.resume_requested_at, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 enforces the native alias truth table and root-context failure precedence", () => { assert.deepEqual(readUnambiguousSessionStartNativeId(undefined), { ok: false, reason: "missing" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: "root" }), { ok: true, value: "root" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: "foreign" }), { ok: false, reason: "conflict" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: undefined }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: 42, sessionId: "root" } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root" }), { ok: true, value: "root" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ sessionId: "root" }), { ok: true, value: "root" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "foreign", sessionId: "root" }), { ok: false, reason: "conflict" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: undefined, sessionId: "root" }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: 42 } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "", sessionId: 42 } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ sessionId: "" }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: 42 } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "", sessionId: "" }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: " root ", sessionId: "root" }), { ok: true, value: "root" }); const cwd = resolve("/tmp/omx-3284-root-context"); const baseState = { session_id: "canonical", native_session_id: "root", cwd, pid: process.pid, started_at: "2026-07-23T00:00:00.000Z" } as any; assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, null), { ok: false, reason: "pointer_not_usable" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, cwd: "" }), { ok: false, reason: "pointer_cwd_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, session_id: "" }), { ok: false, reason: "pointer_session_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, native_session_id: "" }), { ok: false, reason: "pointer_native_root_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, cwd: resolve("/tmp/foreign") }), { ok: false, reason: "pointer_cwd_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "", { session_id: "root" }, baseState), { ok: false, reason: "selected_canonical_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "foreign", { session_id: "root" }, baseState), { ok: false, reason: "canonical_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", {}, baseState), { ok: false, reason: "event_native_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "" }, baseState), { ok: false, reason: "event_native_malformed" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "other" }, baseState), { ok: false, reason: "native_root_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, baseState), { ok: true, sessionId: "canonical", rootNativeSessionId: "root" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root", sessionId: "foreign" }, baseState), { ok: false, reason: "event_native_conflict" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root", sessionId: "foreign" }, null), { ok: false, reason: "pointer_not_usable" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "foreign", {}, baseState), { ok: false, reason: "canonical_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, owner_omx_session_id: "foreign", owner_codex_session_id: "foreign", codex_session_id: "foreign", previous_native_session_id: "foreign" }), { ok: true, sessionId: "canonical", rootNativeSessionId: "root" }); }); it("issue #3284 withholds authority when transcript and hook child identities disagree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-child-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-child-mismatch"; const rootNativeSessionId = "root-3284-child-mismatch"; const hookChildId = "hook-child-3284"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: hookChildId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [hookChildId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [hookChildId]: cachedThread } }, } }); const transcriptPath = join(cwd, "child-mismatch.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: "transcript-child-3284", source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: hookChildId, sessionId: hookChildId, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); const child = tracking.sessions[canonicalSessionId].threads[hookChildId]; assert.equal(child.kind, "subagent"); assert.equal(child.reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(hookChildId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 revokes cached authority across conflicting child aliases", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-child-alias-revoke-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-child-alias-revoke"; const rootNativeSessionId = "root-3284-child-alias-revoke"; const realChildId = "real-child-3284"; const fakeChildId = "fake-child-3284"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: realChildId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [realChildId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [realChildId]: cachedThread } }, } }); const transcriptPath = join(cwd, "child-alias-revoke.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: realChildId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: fakeChildId, sessionId: realChildId, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[realChildId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(realChildId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 revokes cached authority when the child reappears under a foreign parent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-foreign-parent-revoke-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-foreign-parent-revoke"; const rootNativeSessionId = "root-3284-foreign-parent-revoke"; const childId = "child-3284-foreign-parent"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, } }); const transcriptPath = join(cwd, "foreign-parent-revoke.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: "foreign-parent" } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: childId, sessionId: childId, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[childId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const malformedFirstAlias of ["", 42] as const) { it(`issue #3284 revokes cached authority when the first child alias is ${JSON.stringify(malformedFirstAlias)}`, async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-child-malformed-first-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-child-malformed-first"; const rootNativeSessionId = "root-3284-child-malformed-first"; const childId = "child-3284-malformed-first"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, } }); const transcriptPath = join(cwd, "child-malformed-first.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: malformedFirstAlias, sessionId: childId, transcript_path: transcriptPath } as never, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[childId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); } for (const scenario of [ { name: "valid-first-blank-second", aliases: { session_id: "child-3284-no-candidate", sessionId: "" } }, { name: "both-blank", aliases: { session_id: "", sessionId: "" } }, { name: "both-non-string", aliases: { session_id: 42, sessionId: false } }, { name: "both-absent", aliases: {} }, ] as const) { it(`issue #3284 revokes transcript-identified cached authority for ${scenario.name}`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-no-candidate-${scenario.name}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-no-candidate-${scenario.name}`; const rootNativeSessionId = `root-3284-no-candidate-${scenario.name}`; const childId = "child-3284-no-candidate"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, } }); const transcriptPath = join(cwd, `no-candidate-${scenario.name}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, ...scenario.aliases, transcript_path: transcriptPath } as never, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[childId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), false); const afterRootResume = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(afterRootResume.sessions[canonicalSessionId].threads[childId].resume_requested_at, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 preserves ordinary transcript-bearing SessionStart when native aliases are unusable", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-ordinary-transcript-")); try { const canonicalSessionId = "omx-3284-ordinary-transcript"; const rootNativeSessionId = "root-3284-ordinary-transcript"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const sessionPath = join(cwd, ".omx", "state", "session.json"); const before = await readFile(sessionPath, "utf-8"); const transcriptPath = join(cwd, "ordinary-root.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: rootNativeSessionId, source: "user" } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "", sessionId: 42, transcript_path: transcriptPath } as never, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(sessionPath, "utf-8"), before); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 fresh-reads the pointer created by root SessionStart reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-fresh-pointer-")); try { const stateDir = join(cwd, ".omx", "state"); const rootNativeSessionId = "root-3284-fresh-pointer"; const childId = "child-3284-fresh-pointer"; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, } }, } }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suggest duplicate same-role subagent spawns when reopen ids exist", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-duplicates-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-reuse-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reuse", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-reuse", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-reuse": { thread_id: "codex-leader-reuse", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-critic-reuse": { thread_id: "thread-critic-reuse", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 1, role: "critic", lane_id: "risk-review", status: "closed", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-reuse", direct_child_parent_id: "codex-leader-reuse", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-reuse", source: "startup", }, { cwd, sessionOwnerPid: process.pid }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /resume_agent\("thread-critic-reuse"\)/); assert.match(additionalContext, /avoid duplicate same-type subagent spawns/); assert.match(additionalContext, /do not spawn a new agent solely because reopen failed/); assert.doesNotMatch(additionalContext, /spawn.*critic.*replacement/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("surfaces clear warnings for unavailable persisted subagents without spawning replacements", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-warning-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-warning-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-warning", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-warning", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-warning": { thread_id: "codex-leader-warning", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-executor-unavailable": { thread_id: "thread-executor-unavailable", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 1, role: "executor", lane_id: "implementation", status: "unavailable", resume_failed_at: "2026-07-09T00:02:00.000Z", resume_failure_reason: "Codex reported missing thread id", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-warning", direct_child_parent_id: "codex-leader-warning", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-warning", source: "resume", }, { cwd, sessionOwnerPid: process.pid }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /No compatible saved subagent id is currently marked reopenable/); assert.match(additionalContext, /Reopen warnings:/); assert.match(additionalContext, /thread-executor-unavailable/); assert.match(additionalContext, /last failure: Codex reported missing thread id/); assert.doesNotMatch(additionalContext, /resume_agent\("thread-executor-unavailable"\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves canonical OMX session scope when native SessionStart arrives with a different id", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-reconcile-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-launch-1"; const nativeSessionId = "codex-native-1"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId); await writeJson(join(stateDir, "sessions", canonicalSessionId, "hud-state.json"), { last_turn_at: "2026-04-10T00:00:00.000Z", turn_count: 1, }); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId, }, { cwd, sessionOwnerPid: process.pid, }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string; pid?: number }; assert.equal(sessionState.session_id, canonicalSessionId); assert.equal(sessionState.native_session_id, nativeSessionId); assert.equal(sessionState.pid, process.pid); const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-1", turn_id: "turn-1", prompt: "$ralplan fix hud scope drift", }, { cwd }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "skill-active-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json")), false); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3138 converges owner-env terminal write and native Stop on one canonical scope", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-3138-")); const fakeBinDir = join(root, "fake-bin"); const tmuxPath = join(fakeBinDir, "tmux"); const previousSessionId = process.env.OMX_SESSION_ID; const previousTmux = process.env.TMUX; const previousTmuxPane = process.env.TMUX_PANE; const previousPath = process.env.PATH; const setOwnerEvidence = async (instanceId: string, sessionInstanceId = ""): Promise => { await mkdir(fakeBinDir, { recursive: true }); await writeFile(tmuxPath, buildSessionOwnerEvidenceTmux(instanceId, sessionInstanceId), "utf-8"); await chmod(tmuxPath, 0o755); process.env.TMUX = "/tmp/omx-3138"; process.env.TMUX_PANE = "%3138"; process.env.PATH = `${fakeBinDir}:${previousPath ?? ""}`; }; try { const cwd = join(root, "bound"); const canonicalSessionId = "native-canonical-3138"; const nativeSessionId = "native-canonical-3138"; const ownerSessionId = "omx-owner-3138"; const stateDir = join(cwd, ".omx", "state"); await mkdir(cwd, { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId, pid: process.pid, tmuxSessionName: "omx-owner-evidence", tmuxPaneId: "%3138", }); process.env.OMX_SESSION_ID = ownerSessionId; await setOwnerEvidence(ownerSessionId, canonicalSessionId); const beforeAlias = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-owner-3138", turn_id: "turn-before-alias-3138", prompt: "$deep-interview establish canonical scope", }, { cwd }, ); assert.equal(beforeAlias.skillState?.session_id, canonicalSessionId); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "deep-interview-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "deep-interview-state.json")), false); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId }, { cwd, sessionOwnerPid: process.pid }, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }, ); const reboundPointer = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8")) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(reboundPointer.session_id, canonicalSessionId); assert.equal(reboundPointer.native_session_id, nativeSessionId); assert.equal(reboundPointer.owner_omx_session_id, ownerSessionId); const afterAlias = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-owner-3138", turn_id: "turn-after-alias-3138", prompt: "$deep-interview establish canonical scope", }, { cwd }, ); assert.equal(afterAlias.skillState?.session_id, canonicalSessionId); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "deep-interview-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "deep-interview-state.json")), false); const terminalWrite = await executeStateOperation("state_write", { mode: "deep-interview", active: false, current_phase: "complete", workingDirectory: cwd, }); assert.notEqual(terminalWrite.isError, true); assert.equal( (terminalWrite.payload as { path?: string }).path, join(realpathSync(stateDir), "sessions", canonicalSessionId, "deep-interview-state.json"), ); assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "deep-interview-state.json")), false); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, thread_id: "thread-owner-3138" }, { cwd }, ); assert.equal(stop.outputJson, null); const replacementCwd = join(root, "replacement"); const replacementStateDir = join(replacementCwd, ".omx", "state"); const replacementOwner = "omx-prior-3138"; const replacementCandidate = "omx-replacement-3138"; await mkdir(replacementCwd, { recursive: true }); await writeSessionStart(replacementCwd, replacementOwner, { nativeSessionId: "native-before-new-3138", pid: process.pid, }); process.env.OMX_SESSION_ID = replacementCandidate; await setOwnerEvidence(replacementCandidate); const replacementStart = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: replacementCwd, session_id: "native-after-new-3138" }, { cwd: replacementCwd, sessionOwnerPid: process.pid }, ); assert.equal(replacementStart.outputJson, null); const replacementPointer = JSON.parse(await readFile(join(replacementStateDir, "session.json"), "utf-8")) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(replacementPointer.session_id, replacementOwner); assert.equal(replacementPointer.native_session_id, "native-before-new-3138"); assert.equal(replacementPointer.owner_omx_session_id, undefined); assert.notEqual(replacementPointer.owner_omx_session_id, replacementCandidate); const nativeOnlyCwd = join(root, "native-only"); await mkdir(nativeOnlyCwd, { recursive: true }); delete process.env.OMX_SESSION_ID; await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: nativeOnlyCwd, session_id: "native-only-3138" }, { cwd: nativeOnlyCwd, sessionOwnerPid: process.pid }, ); const nativeOnlyPointer = JSON.parse( await readFile(join(nativeOnlyCwd, ".omx", "state", "session.json"), "utf-8"), ) as { session_id?: string; owner_omx_session_id?: string }; assert.equal(nativeOnlyPointer.session_id, "native-only-3138"); assert.equal(nativeOnlyPointer.owner_omx_session_id, undefined); const conflictingCwd = join(root, "conflicting"); const conflictingOwner = "omx-conflicting-3138"; await mkdir(conflictingCwd, { recursive: true }); await writeSessionStart(conflictingCwd, "native-conflicting-3138", { nativeSessionId: "native-conflicting-3138", pid: process.pid, }); process.env.OMX_SESSION_ID = conflictingOwner; await setOwnerEvidence("omx-foreign-3138"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: conflictingCwd, session_id: "native-conflicting-3138" }, { cwd: conflictingCwd, sessionOwnerPid: process.pid }, ); const conflictingPointer = JSON.parse( await readFile(join(conflictingCwd, ".omx", "state", "session.json"), "utf-8"), ) as { owner_omx_session_id?: string }; assert.equal(conflictingPointer.owner_omx_session_id, undefined); const conflictingActivation = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd: conflictingCwd, session_id: "native-conflicting-3138", prompt: "$deep-interview must not create an owner scope", }, { cwd: conflictingCwd }, ); assert.equal(conflictingActivation.skillState, null); assert.equal( existsSync(join(conflictingCwd, ".omx", "state", "sessions", "native-conflicting-3138", "deep-interview-state.json")), false, ); const staleCwd = join(root, "stale"); const staleStatePath = join(staleCwd, ".omx", "state", "session.json"); await writeJson(staleStatePath, { session_id: "native-stale-3138", native_session_id: "native-stale-3138", cwd: staleCwd, started_at: "2026-01-01T00:00:00.000Z", pid: 999_999, }); process.env.OMX_SESSION_ID = "omx-stale-3138"; await setOwnerEvidence("omx-stale-3138"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: staleCwd, session_id: "native-stale-3138" }, { cwd: staleCwd, sessionOwnerPid: process.pid }, ); const stalePointer = JSON.parse(await readFile(staleStatePath, "utf-8")) as { session_id?: string; owner_omx_session_id?: string; }; assert.equal(stalePointer.session_id, "native-stale-3138"); assert.equal(stalePointer.owner_omx_session_id, "omx-stale-3138"); const foreignCwd = join(root, "foreign"); const foreignStatePath = join(foreignCwd, ".omx", "state", "session.json"); await writeJson(foreignStatePath, { session_id: "native-foreign-3138", native_session_id: "native-foreign-3138", cwd: join(root, "other-worktree"), started_at: "2026-01-01T00:00:00.000Z", pid: process.pid, }); const foreignPointerBefore = await readFile(foreignStatePath, "utf-8"); process.env.OMX_SESSION_ID = "omx-foreign-3138"; await setOwnerEvidence("omx-foreign-3138"); const foreignStart = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: foreignCwd, session_id: "native-foreign-3138" }, { cwd: foreignCwd, sessionOwnerPid: process.pid }, ); assert.equal(foreignStart.outputJson, null); assert.equal(await readFile(foreignStatePath, "utf-8"), foreignPointerBefore); const foreignPointer = JSON.parse(await readFile(foreignStatePath, "utf-8")) as { owner_omx_session_id?: string }; assert.equal(foreignPointer.owner_omx_session_id, undefined); const foreignActivation = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd: foreignCwd, session_id: "native-unmatched-foreign-3138", prompt: "$deep-interview must not escape a foreign pointer", }, { cwd: foreignCwd }); assert.equal(foreignActivation.skillState, null); assert.equal(existsSync(join(foreignCwd, ".omx", "state", "sessions", "native-unmatched-foreign-3138")), false); assert.equal(existsSync(join(foreignCwd, ".omx", "state", "skill-active-state.json")), false); const foreignStopPayload = { hook_event_name: "Stop" as const, cwd: foreignCwd, session_id: "native-foreign-3138", }; const foreignStop = await dispatchCodexNativeHook(foreignStopPayload, { cwd: foreignCwd }); assert.equal(foreignStop.outputJson, null); const foreignReplay = await dispatchCodexNativeHook({ ...foreignStopPayload, stop_hook_active: true, }, { cwd: foreignCwd }); assert.equal(foreignReplay.outputJson, null); assert.equal(existsSync(join(foreignCwd, ".omx", "state", "native-stop-state.json")), false); const unmatchedStopPayload = { hook_event_name: "Stop" as const, cwd: conflictingCwd, session_id: "native-unmatched-stop-3138", }; const unmatchedOutputs = await Promise.all( Array.from({ length: 3 }, () => dispatchCodexNativeHook(unmatchedStopPayload, { cwd: conflictingCwd })), ); assert.deepEqual(unmatchedOutputs.map((result) => result.outputJson), [null, null, null]); const unmatchedCliResult = runNativeHookCliResult(unmatchedStopPayload, { cwd: conflictingCwd }); assert.equal(unmatchedCliResult.status, 0, unmatchedCliResult.stderr || unmatchedCliResult.stdout); assert.equal(unmatchedCliResult.stderr, ""); assert.deepEqual(parseSingleJsonStdout(unmatchedCliResult.stdout), {}); assert.equal(existsSync(join(conflictingCwd, ".omx", "state", "native-stop-state.json")), false); } finally { if (typeof previousSessionId === "string") process.env.OMX_SESSION_ID = previousSessionId; else delete process.env.OMX_SESSION_ID; if (typeof previousTmux === "string") process.env.TMUX = previousTmux; else delete process.env.TMUX; if (typeof previousTmuxPane === "string") process.env.TMUX_PANE = previousTmuxPane; else delete process.env.TMUX_PANE; if (typeof previousPath === "string") process.env.PATH = previousPath; else delete process.env.PATH; await rm(root, { recursive: true, force: true }); } }); it("authorizes a live unmatched Stop from its exact session owner sidecar without changing the selected pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-sidecar-stop-")); try { const stateDir = join(cwd, ".omx", "state"); const selectedSessionId = "native-selected-owner"; const independentSessionId = "native-independent-owner"; await writeSessionStart(cwd, selectedSessionId, { nativeSessionId: selectedSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, independentSessionId); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const rootGoalPath = join(cwd, ".omx", "ultragoal", "goals.json"); await writeJson(rootGoalPath, { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const rootGoalBefore = await readFile(rootGoalPath, "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: independentSessionId, thread_id: independentSessionId, last_user_message: "get_goal reports a completed Codex goal still attached to this thread; do not call create_goal until cleanup is explicit.", last_assistant_message: "I am starting another run now; create_goal payload follows.", }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(await readFile(rootGoalPath, "utf-8"), rootGoalBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("uses the unmatched sidecar session workflow as the Stop blocker", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-sidecar-skill-")); try { const stateDir = join(cwd, ".omx", "state"); const selectedSessionId = "native-selected-skill"; const independentSessionId = "native-independent-skill"; await writeSessionStart(cwd, selectedSessionId, { nativeSessionId: selectedSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, independentSessionId); await writeSessionSkillActiveState(stateDir, independentSessionId, "ralplan", "planning"); await writeJson( join(stateDir, "sessions", independentSessionId, "ralplan-state.json"), { active: true, current_phase: "planning", session_id: independentSessionId, workingDirectory: cwd, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: independentSessionId, thread_id: independentSessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.stopReason ?? ""), /^skill_ralplan_planning_/); assert.notEqual(result.outputJson?.stopReason, "session_scope_unmatched"); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const mode of ["autopilot", "ultrawork", "ultraqa"] as const) { it(`uses the unmatched sidecar ${mode} state as the Stop blocker`, async () => { await withIndependentNativeSession(mode, async ({ cwd, stateDir, sessionId, pointerBefore, }) => { await writeJson( join(stateDir, "sessions", sessionId, `${mode}-state.json`), { active: true, mode, current_phase: "executing", session_id: sessionId, workingDirectory: cwd, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: sessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.stopReason ?? ""), new RegExp(`^${mode}_`)); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); }); }); } it("uses the unmatched sidecar team state as the Stop blocker", async () => { await withIndependentNativeSession("team", async ({ cwd, stateDir, sessionId, pointerBefore, }) => { await writeJson( join(stateDir, "sessions", sessionId, "team-state.json"), { active: true, mode: "team", team_name: "sidecar-team", current_phase: "team-exec", session_id: sessionId, owner_codex_thread_id: sessionId, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: sessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_team-exec"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); }); }); it("uses the unmatched sidecar deep-interview question state as the Stop blocker", async () => { await withIndependentNativeSession("deep-interview", async ({ cwd, stateDir, sessionId, pointerBefore, }) => { await writeSessionSkillActiveState( stateDir, sessionId, "deep-interview", "intent-first", ); await writeJson( join(stateDir, "sessions", sessionId, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, thread_id: sessionId, question_enforcement: { obligation_id: "sidecar-question", source: "omx-question", status: "pending", requested_at: "2026-07-18T00:00:00.000Z", }, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: sessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "deep_interview_question_required"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); }); }); it("uses a live parent sidecar when a nested selected pointer is stale-dead", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-sidecar-stale-root-")); try { const stateDir = join(cwd, ".omx", "state"); const parentSessionId = "native-live-parent"; await writeSessionStart(cwd, parentSessionId, { nativeSessionId: parentSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, parentSessionId); await writeJson(join(stateDir, "session.json"), { session_id: "native-dead-nested", native_session_id: "native-dead-nested", started_at: "2026-01-01T00:00:00.000Z", cwd, pid: 999_999, platform: process.platform, }); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: parentSessionId, thread_id: parentSessionId, }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps subagent SessionStart from replacing the canonical leader session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session"; const leaderNativeSessionId = "codex-leader-thread"; const childNativeSessionId = "codex-child-thread"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", iteration: 1, max_iterations: 5, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event, context: event.context })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, depth: 1, agent_nickname: "Hegel", agent_role: "critic", }, }, }, agent_nickname: "Hegel", agent_role: "critic", }, })}\n`, ); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, canonicalSessionId); assert.equal(sessionState.native_session_id, leaderNativeSessionId); assert.equal( existsSync(join(stateDir, "sessions", childNativeSessionId, "ralph-state.json")), false, ); assert.ok(result.outputJson); const leaderRalph = JSON.parse( await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string }; assert.equal(leaderRalph.active, true); assert.equal(leaderRalph.current_phase, "executing"); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "subagent SessionStart must not independently dispatch session-start hook notifications", ); const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record; }>; }; assert.equal(tracking.sessions?.[canonicalSessionId]?.leader_thread_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent"); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.mode, "critic"); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.direct_child_root_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.direct_child_parent_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent"); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.mode, "critic"); const rootResume = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: leaderNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }, ); const rootResumeContext = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(rootResumeContext.includes(`resume_agent(${JSON.stringify(childNativeSessionId)})`), true); await rm(join(cwd, "hook-events.jsonl"), { force: true }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "child-stop-turn", }, { cwd }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "subagent Stop must not independently dispatch stop hook notifications", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("keeps an authoritative Team worker lifecycle scoped without replacing the live leader pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-pointer-")); try { const stateDir = join(cwd, ".omx", "state"); await writeSessionStart(cwd, "leader-session", { nativeSessionId: "leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "pointer-team"); const leaderPointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const leaderRalphPath = join(stateDir, "sessions", "leader-session", "ralph-state.json"); await writeJson(leaderRalphPath, { active: true, mode: "ralph", current_phase: "executing", session_id: "leader-session", owner_codex_session_id: "leader-native", }); const leaderRalphBeforeWorkerStop = await readFile(leaderRalphPath, "utf-8"); const rootNativeStopPath = join(stateDir, "native-stop-state.json"); assert.equal(existsSync(rootNativeStopPath), false); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "", sessionId: "worker-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), leaderPointerBefore); const workerStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "worker-native", thread_id: "worker-native", turn_id: "worker-stop-turn", }, { cwd }); assert.notEqual(workerStop.outputJson?.stopReason, "session_scope_unmatched"); assert.notEqual(workerStop.outputJson?.stopReason, "session_pointer_unusable"); assert.equal(await readFile(leaderRalphPath, "utf-8"), leaderRalphBeforeWorkerStop); assert.equal(existsSync(rootNativeStopPath), false); delete process.env.OMX_TEAM_INTERNAL_WORKER; delete process.env.OMX_TEAM_WORKER; process.env.TMUX_PANE = "%42"; const leaderStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "leader-native", thread_id: "leader-native", turn_id: "leader-stop-turn", }, { cwd }); assert.notEqual(leaderStop.outputJson?.stopReason, "session_scope_unmatched"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), leaderPointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("rejects leader aliases from foreign-cwd pointer evidence in a worker worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-foreign-alias-")); try { await writeSessionStart(cwd, "foreign-alias-leader", { nativeSessionId: "foreign-alias-leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "foreign-alias-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const pointer = JSON.parse(await readFile(pointerPath, "utf-8")) as Record; pointer.cwd = join(cwd, "leader-worktree"); await writeJson(pointerPath, pointer); const pointerBefore = await readFile(pointerPath, "utf-8"); for (const sessionId of ["foreign-alias-leader", "foreign-alias-leader-native"]) { await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: sessionId, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId)), false); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps declared Team workers fail closed before startup authority is materialized", async () => { for (const pointerKind of ["absent", "live"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-team-worker-race-${pointerKind}-`)); try { const pointerPath = join(cwd, ".omx", "state", "session.json"); if (pointerKind === "live") { await writeSessionStart(cwd, "race-leader-session", { nativeSessionId: "race-leader-native", pid: process.pid, }); } const pointerBefore = existsSync(pointerPath) ? await readFile(pointerPath, "utf-8") : null; process.env.OMX_TEAM_WORKER = "race-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "race-team/worker-1"; process.env.TMUX = "1"; process.env.TMUX_PANE = "%10"; await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "race-worker-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); for (const declaration of [ { internal: "malformed", external: "" }, { internal: "race-team/worker-1", external: "other-team/worker-1" }, ]) { process.env.OMX_TEAM_INTERNAL_WORKER = declaration.internal; process.env.OMX_TEAM_WORKER = declaration.external; await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "race-worker-malformed-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "race-worker-native", thread_id: "race-worker-native", turn_id: "race-worker-stop", }, { cwd }); assert.match(String(stop.outputJson?.stopReason ?? ""), /^team_worker_/); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("suppresses malformed or public-only Team identity across SessionStart and PreToolUse", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-identity-gate-")); try { await configureAuthoritativeTeamWorker(cwd, "identity-gate-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); assert.equal(existsSync(pointerPath), false); const declarations = [ { internal: "", external: "identity-gate-team/worker-1" }, { internal: "malformed-internal", external: "identity-gate-team/worker-1" }, { internal: "identity-gate-team/worker-1", external: "other-team/worker-2" }, ] as const; for (const [index, declaration] of declarations.entries()) { process.env.OMX_TEAM_INTERNAL_WORKER = declaration.internal; process.env.OMX_TEAM_WORKER = declaration.external; const sessionId = `untrusted-worker-session-${index}`; const sessionStart = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: sessionId, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(sessionStart.outputJson, null); assert.equal(existsSync(pointerPath), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId)), false); const preToolUse = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, tool_name: "Write", tool_use_id: `untrusted-worker-write-${index}`, tool_input: { file_path: "src/untrusted-worker.ts", content: "export {}\\n" }, }, { cwd }); assert.equal(preToolUse.outputJson, null); assert.equal(existsSync(pointerPath), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId)), false); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for invalid Team worker lifecycle identity across pointer states", async () => { for (const pointerKind of ["absent", "live", "stale", "malformed"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-team-worker-invalid-${pointerKind}-`)); try { delete process.env.OMX_TEAM_INTERNAL_WORKER; delete process.env.OMX_TEAM_WORKER; delete process.env.OMX_TEAM_STATE_ROOT; delete process.env.OMX_TEAM_LEADER_CWD; delete process.env.OMX_SESSION_ID; delete process.env.TMUX; delete process.env.TMUX_PANE; const pointerPath = join(cwd, ".omx", "state", "session.json"); await configureAuthoritativeTeamWorker(cwd, "invalid-identity-team"); if (pointerKind === "live" || pointerKind === "stale") { await writeSessionStart(cwd, "leader-session", { nativeSessionId: "invalid-test-leader-native", pid: pointerKind === "live" ? process.pid : 2_147_483_647, }); } else if (pointerKind === "malformed") { await mkdir(dirname(pointerPath), { recursive: true }); await writeFile(pointerPath, "{ malformed missing-identity evidence", "utf-8"); } if (pointerKind === "live") { await writeJson(join(cwd, ".omx", "state", "subagent-tracking.json"), { schemaVersion: 1, sessions: { "leader-session": { session_id: "leader-session", leader_thread_id: "invalid-test-leader-native", threads: { "invalid-test-leader-native": { thread_id: "invalid-test-leader-native", kind: "leader" }, "invalid-worker-thread": { thread_id: "invalid-worker-thread", kind: "subagent" }, }, }, }, }); } const pointerBefore = existsSync(pointerPath) ? await readFile(pointerPath, "utf-8") : null; const payloads: Array> = [ {}, { session_id: "" }, { session_id: "../escape" }, { session_id: "worker-one", sessionId: "worker-two" }, ...(pointerKind === "live" ? [{ session_id: "leader-session" }, { session_id: "invalid-test-leader-native" }] : []), ]; for (const payload of payloads) { await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, ...payload, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, ...payload, thread_id: "invalid-worker-thread", turn_id: `invalid-worker-${pointerKind}-stop`, }, { cwd }); assert.match(String(stop.outputJson?.stopReason ?? ""), /^team_worker_/); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps authoritative worker payload scope independent of malformed selected-pointer evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-malformed-pointer-")); try { await configureAuthoritativeTeamWorker(cwd, "malformed-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); await writeFile(pointerPath, "{ malformed leader evidence", "utf-8"); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "worker-malformed-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed leader evidence"); const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "worker-malformed-native", thread_id: "worker-malformed-native", turn_id: "worker-malformed-stop", }, { cwd }); assert.notEqual(stop.outputJson?.stopReason, "session_pointer_unusable"); assert.notEqual(stop.outputJson?.stopReason, "session_scope_unmatched"); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed leader evidence"); for (const payload of [ { session_id: "../escape" }, { session_id: "worker-one", sessionId: "worker-two" }, ]) { const rejected = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, ...payload, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(rejected.outputJson, null); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed leader evidence"); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "escape")), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "worker-one")), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "worker-two")), false); } } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const [name, mutate] of [ ["foreign", async (cwd: string) => { process.env.OMX_TEAM_STATE_ROOT = join(cwd, "foreign-state"); }], ["nested", async (cwd: string) => { process.env.OMX_TEAM_INTERNAL_WORKER = "other-team/worker-1"; }], ["non-Team", async () => { delete process.env.OMX_TEAM_INTERNAL_WORKER; delete process.env.OMX_TEAM_WORKER; }], ] as const) { it(`does not grant the Team worker pointer bypass to ${name} hook context`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-${name}-pointer-`)); try { await configureAuthoritativeTeamWorker(cwd, "boundary-team"); await mutate(cwd); const pointerPath = join(cwd, ".omx", "state", "session.json"); await writeFile(pointerPath, "{ malformed boundary evidence", "utf-8"); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "boundary-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed boundary evidence"); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("does not let a Team worker adopt a stale leader-selected pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-stale-leader-")); try { await writeSessionStart(cwd, "stale-leader-session", { nativeSessionId: "stale-leader-native", pid: 2_147_483_647, }); await configureAuthoritativeTeamWorker(cwd, "stale-leader-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const pointerBefore = await readFile(pointerPath, "utf-8"); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "worker-after-stale-leader", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "worker-after-stale-leader", thread_id: "worker-after-stale-leader", turn_id: "worker-after-stale-stop", }, { cwd }); assert.notEqual(stop.outputJson?.stopReason, "session_pointer_unusable"); assert.notEqual(stop.outputJson?.stopReason, "session_scope_unmatched"); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves leader pointer ownership through the packed native-hook entrypoint", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-packed-")); try { await writeSessionStart(cwd, "leader-session", { nativeSessionId: "packed-leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "packed-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const pointerBefore = await readFile(pointerPath, "utf-8"); const env = { ...process.env }; parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "SessionStart", cwd, session_id: "packed-worker-native", }, { cwd, env })); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); const stopOutput = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "packed-worker-native", thread_id: "packed-worker-native", turn_id: "packed-worker-stop", }, { cwd, env })); assert.notEqual(stopOutput.stopReason, "session_scope_unmatched"); assert.notEqual(stopOutput.stopReason, "session_pointer_unusable"); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses child-agent SessionStart hook dispatch at minimal verbosity", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-minimal-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "minimal", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-minimal"; const leaderNativeSessionId = "codex-leader-thread-minimal"; const childNativeSessionId = "codex-child-thread-minimal"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "minimal-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "verifier", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "subagent SessionStart must be suppressed at minimal verbosity", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("allows explicit child-agent lifecycle hook dispatch when includeChildAgents is enabled", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-include-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", includeChildAgents: true, telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-include"; const leaderNativeSessionId = "codex-leader-thread-include"; const childNativeSessionId = "codex-child-thread-include"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "included-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "verifier", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "included-child-stop-turn", }, { cwd }, ); const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8"); assert.match(hookEvents, /"event":"session-start"/); assert.match(hookEvents, /"event":"stop"/); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("allows child-agent lifecycle hook dispatch at agent verbosity", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-agent-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "agent", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-agent"; const leaderNativeSessionId = "codex-leader-thread-agent"; const childNativeSessionId = "codex-child-thread-agent"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "agent-verbosity-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "verifier", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8"); assert.match(hookEvents, /"event":"session-start"/); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("suppresses child-agent SessionStart and Stop before the canonical leader session is reconciled (#2831)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-canonical-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const leaderNativeSessionId = "codex-leader-thread-no-canonical"; const childNativeSessionId = "codex-child-thread-no-canonical"; await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "no-canonical-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "explorer", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "child SessionStart must be suppressed even before the canonical leader session is reconciled", ); assert.equal( existsSync(join(stateDir, "session.json")), false, "child SessionStart must not be promoted into a root/leader session", ); const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record; }>; }; assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent"); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "no-canonical-child-stop-turn", }, { cwd }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "child Stop must be suppressed when the start was recognized as subagent-scoped", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("preserves root/leader SessionStart dispatch at session verbosity (#2831)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-session-start-preserved-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-root-thread-preserved", }, { cwd, sessionOwnerPid: process.pid }, ); const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8"); assert.match( hookEvents, /"event":"session-start"/, "root/leader SessionStart must still dispatch at session verbosity", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("keeps a self-parented native role thread as subagent evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-self-parented-subagent-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-autopilot-session"; const nativeRoleThreadId = "codex-architect-thread"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: nativeRoleThreadId, }); const transcriptPath = join(cwd, "architect-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: nativeRoleThreadId, source: { subagent: { thread_spawn: { parent_thread_id: nativeRoleThreadId, depth: 1, agent_nickname: "Architect", agent_role: "architect", }, }, }, agent_nickname: "Architect", agent_role: "architect", }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeRoleThreadId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record; }>; }; assert.equal(tracking.sessions?.[canonicalSessionId]?.leader_thread_id, undefined); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[nativeRoleThreadId]?.kind, "subagent"); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[nativeRoleThreadId]?.mode, "architect"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not attach a subagent SessionStart to an unrelated canonical leader", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-a"; const leaderNativeSessionId = "codex-leader-thread-a"; const unrelatedParentNativeSessionId = "codex-leader-thread-b"; const childNativeSessionId = "codex-child-thread-b"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", iteration: 1, max_iterations: 5, }); const transcriptPath = join(cwd, "unrelated-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: unrelatedParentNativeSessionId, depth: 1, agent_nickname: "Spinoza", agent_role: "critic", }, }, }, agent_nickname: "Spinoza", agent_role: "critic", }, })}\n`, ); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, canonicalSessionId); assert.equal(sessionState.native_session_id, leaderNativeSessionId); assert.equal(existsSync(join(stateDir, "subagent-tracking.json")), false); assert.equal(existsSync(join(stateDir, "sessions", childNativeSessionId)), false); assert.equal(result.outputJson, null); const leaderRalph = JSON.parse( await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string }; assert.equal(leaderRalph.active, true); assert.equal(leaderRalph.current_phase, "executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("describes attached tmux runtime in SessionStart context when TMUX is present", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-tmux-")); process.env.TMUX = "/tmp/tmux-attached"; process.env.TMUX_PANE = "%11"; try { const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-tmux-1", }, { cwd, sessionOwnerPid: process.pid, }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /attached tmux runtime/); assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) are directly usable in this session/); assert.match(additionalContext, /visible temporary renderer available from the current pane; primary success JSON is answers\[\]/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("describes direct CLI outside tmux in SessionStart context when the launch source is cli", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-cli-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-cli-1", source: "cli", }, { cwd, sessionOwnerPid: process.pid, }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /direct CLI outside tmux/); assert.doesNotMatch(additionalContext, /native-hook \/ Codex App outside tmux/); assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) need an attached tmux OMX CLI shell|omx team and omx hud need an attached tmux OMX CLI shell/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves the OMX owner HUD session when an unrelated native SessionStart is rejected", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-owner-session-revive-")); try { const stateDir = join(cwd, ".omx", "state"); const ownerSessionId = "omx-launch-owner-hud"; const oldNativeSessionId = "codex-native-hud-old"; const nativeSessionId = "codex-native-hud-new"; await mkdir(stateDir, { recursive: true }); await writeSessionStart(cwd, ownerSessionId, { nativeSessionId: oldNativeSessionId, pid: process.pid, }); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId, }, { cwd, sessionOwnerPid: process.pid, }, ); const sessionState = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8")) as { session_id?: string; native_session_id?: string; previous_native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(sessionState.session_id, ownerSessionId); assert.equal(sessionState.native_session_id, oldNativeSessionId); assert.equal(sessionState.previous_native_session_id, undefined); assert.equal(sessionState.owner_omx_session_id, undefined); let reconcileCall: { cwd: string; sessionId?: string; sessionIds?: string[] } | null = null; const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: ownerSessionId, thread_id: "thread-hud-owner", turn_id: "turn-hud-owner", prompt: "$ralplan fix native new hud owner handoff", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 }; }, }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: ownerSessionId, sessionIds: [ownerSessionId, oldNativeSessionId], }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("falls back to the canonical session id for malformed HUD owner ids", async () => { for (const [index, invalidOwnerSessionId] of ["codex-native-hud-owner", "omx-../../stale"].entries()) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-invalid-owner-revive-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-launch-hud-safe"; const nativeSessionId = "codex-native-hud-safe"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId }); const sessionStatePath = join(stateDir, "session.json"); const sessionState = JSON.parse(await readFile(sessionStatePath, "utf-8")) as Record; sessionState.owner_omx_session_id = invalidOwnerSessionId; await writeJson(sessionStatePath, sessionState); let reconcileCall: { cwd: string; sessionId?: string; sessionIds?: string[] } | null = null; const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: `thread-hud-invalid-owner-${index}`, turn_id: "turn-hud-invalid-owner", prompt: "$ralplan fix malformed hud owner handoff", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 }; }, }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId, sessionIds: [canonicalSessionId, nativeSessionId], }); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("passes the canonical OMX session id when UserPromptSubmit revives HUD", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-session-revive-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-launch-hud"; const nativeSessionId = "codex-native-hud"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId }); let reconcileCall: { cwd: string; sessionId?: string } | null = null; const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-hud", turn_id: "turn-hud", prompt: "$ralplan fix orphaned hud session handoff", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId }; return { status: 'recreated', paneId: '%9', desiredHeight: 3, duplicateCount: 0 }; }, }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId }); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "skill-active-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json")), false); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("adds .omx/ to git info/exclude during SessionStart instead of mutating repo .gitignore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-gitignore-")); try { await writeFile(join(cwd, ".gitignore"), "node_modules/\n"); execFileSync("git", ["init"], { cwd, stdio: "pipe" }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-gitignore-1", }, { cwd, sessionOwnerPid: 43210 }, ); assert.equal(result.omxEventName, "session-start"); const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8"); assert.equal(gitignore, "node_modules/\n"); const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8"); assert.match(exclude, /(?:^|\n)\.omx\/\n/); assert.match( JSON.stringify(result.outputJson), /Added \.omx\/ to .*\.git[\/]info[\/]exclude/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps SessionStart quiet when .omx/ is already ignored by repo-level gitignore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-existing-ignore-")); try { await writeFile(join(cwd, ".gitignore"), "node_modules/\n.omx/\n"); execFileSync("git", ["init"], { cwd, stdio: "pipe" }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-gitignore-existing", }, { cwd, sessionOwnerPid: 43210 }, ); assert.equal(result.omxEventName, "session-start"); const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8"); assert.equal(gitignore, "node_modules/\n.omx/\n"); const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8"); assert.doesNotMatch(exclude, /(?:^|\n)\.omx\/\n/); assert.doesNotMatch(JSON.stringify(result.outputJson), /Added \.omx\//); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("respects existing Git ignore resolution before writing local excludes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-global-ignore-")); const excludesFile = join(cwd, "global-ignore"); try { await writeFile(join(cwd, ".gitignore"), "node_modules/\n"); await writeFile(excludesFile, ".omx/\n"); execFileSync("git", ["init"], { cwd, stdio: "pipe" }); execFileSync("git", ["config", "core.excludesfile", excludesFile], { cwd, stdio: "pipe" }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-gitignore-global", }, { cwd, sessionOwnerPid: 43210 }, ); assert.equal(result.omxEventName, "session-start"); const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8"); assert.equal(gitignore, "node_modules/\n"); const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8"); assert.doesNotMatch(exclude, /(?:^|\n)\.omx\/\n/); assert.doesNotMatch(JSON.stringify(result.outputJson), /Added \.omx\//); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("includes persisted project-memory summary in SessionStart context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-memory-")); try { await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "TypeScript + Node.js", build: "npm test", conventions: "small diffs, verify before claim", directives: [ { directive: "Keep native Stop bounded to real continuation decisions.", priority: "high" }, ], notes: [ { category: "env", content: "Requires LOCAL_API_BASE for smoke tests", timestamp: new Date().toISOString() }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-memory-1", }, { cwd, sessionOwnerPid: 43210 }, ); const serialized = JSON.stringify(result.outputJson); assert.match(serialized, /\[Project memory\]/); assert.match(serialized, /TypeScript \+ Node\.js/); assert.match(serialized, /small diffs, verify before claim/); assert.match(serialized, /Keep native Stop bounded to real continuation decisions\./); assert.match(serialized, /Requires LOCAL_API_BASE for smoke tests/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("includes repo-local .omx project-memory during SessionStart when OMX_ROOT is boxed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-boxed-memory-")); const boxedRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-root-")); const previousOmxRoot = process.env.OMX_ROOT; try { process.env.OMX_ROOT = boxedRoot; await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "Repo-local CLI memory", conventions: "SessionStart should load CLI-written project memory", directives: [ { directive: "Prefer repo-local .omx project memory over boxed runtime fallback.", priority: "high" }, ], }); await writeJson(join(boxedRoot, ".omx", "project-memory.json"), { techStack: "Boxed runtime memory should not win", notes: [{ category: "runtime", content: "stale boxed runtime note", timestamp: new Date().toISOString() }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-boxed-memory-1", }, { cwd, sessionOwnerPid: 43210 }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Project memory\]/); assert.match(additionalContext, /source: \.omx\/project-memory\.json/); assert.match(additionalContext, /Repo-local CLI memory/); assert.match(additionalContext, /SessionStart should load CLI-written project memory/); assert.match(additionalContext, /Prefer repo-local \.omx project memory over boxed runtime fallback\./); assert.doesNotMatch(additionalContext, /Boxed runtime memory should not win/); assert.doesNotMatch(additionalContext, /stale boxed runtime note/); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; await rm(cwd, { recursive: true, force: true }); await rm(boxedRoot, { recursive: true, force: true }); } }); it("prefers repository project-memory.json during SessionStart while preserving legacy wiki guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-root-memory-legacy-wiki-")); try { const now = new Date().toISOString(); const legacyWikiDir = getLegacyWikiDir(cwd); await mkdir(legacyWikiDir, { recursive: true }); await writeFile(join(legacyWikiDir, "legacy.md"), serializePage({ filename: "legacy.md", frontmatter: { title: "Legacy", tags: ["legacy"], created: now, updated: now, sources: [], links: [], category: "reference", confidence: "medium", schemaVersion: WIKI_SCHEMA_VERSION, }, content: "\n# Legacy\n\nLegacy wiki context must remain visible.\n", })); await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "Legacy runtime memory should not win", notes: [{ category: "legacy", content: "stale legacy note", timestamp: now }], }); await writeJson(join(cwd, "project-memory.json"), { techStack: "Canonical root memory", build: "npm run build && node --test dist/scripts/__tests__/codex-native-hook.test.js", conventions: "prefer repository-visible project memory at startup", directives: [ { directive: "Load root project-memory.json before legacy .omx memory.", priority: "high", timestamp: now }, ], notes: [ { category: "issue", content: "Regression fixture for issue #2273.", timestamp: now }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-root-memory-legacy-wiki", }, { cwd, sessionOwnerPid: 43210 }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Project memory\]/); assert.match(additionalContext, /source: project-memory\.json/); assert.match(additionalContext, /Canonical root memory/); assert.match(additionalContext, /Load root project-memory\.json before legacy \.omx memory\./); assert.match(additionalContext, /Regression fixture for issue #2273\./); assert.doesNotMatch(additionalContext, /Legacy runtime memory should not win/); assert.match(additionalContext, /legacy pages at \.omx\/wiki\//); assert.match(additionalContext, /Legacy wiki fallback is read-only/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("starts a fresh native session without inheriting stale task-scoped context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-isolation-")); try { const stateDir = join(cwd, ".omx", "state"); const priorSessionId = "omx-old-session"; await mkdir(join(stateDir, "sessions", priorSessionId), { recursive: true }); await writeSessionStart(cwd, priorSessionId, { nativeSessionId: "codex-native-old", pid: 999_999_999, }); await writeJson(join(stateDir, "sessions", priorSessionId, "ralph-state.json"), { active: true, current_phase: "executing", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [priorSessionId]: { session_id: priorSessionId, leader_thread_id: "leader-1", updated_at: new Date().toISOString(), threads: { "leader-1": { thread_id: "leader-1", kind: "leader", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, "sub-1": { thread_id: "sub-1", kind: "subagent", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, }, }, }, }); await writeFile( join(cwd, ".omx", "notepad.md"), [ "# OMX Notepad", "", "## PRIORITY", "Preserve durable project guidance.", "", "## WORKING MEMORY", "[2026-04-06T00:33:44Z] stale UI rework context snapshot .omx/context/ui-rework-plan-01-20260406T003344Z.md", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-native-new", }, { cwd, sessionOwnerPid: process.pid, }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, "codex-native-new"); assert.equal(sessionState.native_session_id, "codex-native-new"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /native-hook \/ Codex App outside tmux/); assert.match(additionalContext, /\[Priority notes\]/); assert.match(additionalContext, /Preserve durable project guidance/); assert.doesNotMatch(additionalContext, /stale UI rework context snapshot/); assert.doesNotMatch(additionalContext, /\[Subagents\]/); assert.doesNotMatch(additionalContext, /ralph phase: executing/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows same-session/current-thread Ralph Stop continuation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-current-session-")); try { const stateDir = join(cwd, ".omx", "state"); await writeNativeMappedSessionState(cwd, stateDir, "omx-current-ralph", "codex-current-ralph"); await writeJson(join(stateDir, "sessions", "omx-current-ralph", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_omx_session_id: "omx-current-ralph", owner_codex_session_id: "codex-current-ralph", owner_codex_thread_id: "thread-current-ralph", task_description: "Finish issue 2974 stale Ralph Stop-hook guard", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-current-ralph", thread_id: "thread-current-ralph", last_user_message: "continue the current ralph issue 2974 task", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); assert.match(String(result.outputJson?.systemMessage), /Ralph is still active/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not resume stale different-session global Ralph Stop state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-stale-global-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_session_id: "codex-old-ralph", owner_codex_thread_id: "thread-old-ralph", task_description: "Finish issue 2974 stale Ralph Stop-hook guard", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-new-ralph", last_user_message: "continue the current ralph issue 2974 task", }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not resume global Ralph Stop state for low-overlap unrelated tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-low-overlap-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", task_description: "Refactor documentation navigation and README examples", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, last_user_message: "continue auditing billing webhooks and payment retries", }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows explicit current user continuation for live-risk Ralph tasks when owner context is current", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-live-current-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-live-ralph", task_description: "Deploy billing migration rollback guard for production payments", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-live-ralph", last_user_message: "continue the current ralph deploy billing migration rollback guard", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows current-owner global Ralph Stop continuation with generic current-task wording", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-current-owner-generic-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-current-owner-generic", task_description: "Refactor documentation navigation and README examples", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-current-owner-generic", last_user_message: "continue the current ralph task", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows same-thread global Ralph Stop continuation without payload text for non-live tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-same-thread-no-text-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-same-thread-no-text", task_description: "Refactor documentation navigation and README examples", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-same-thread-no-text", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks no-text global Ralph Stop continuation for live-risk tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-live-no-text-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-live-no-text", task_description: "Deploy billing migration rollback guard for production payments", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-live-no-text", }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks no-text global Ralph Stop continuation for issue 2974 operational live-risk tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-issue-2974-live-no-text-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", task_description: "Implement Telegram assistant GPT switch plan on VPS service with cron restart send notify workflow", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resolves the Codex owner from ancestry without mistaking codex-native-hook wrappers for Codex", () => { const commands = new Map([ [2100, 'sh -c node "/repo/dist/scripts/codex-native-hook.js"'], [1100, 'node /usr/local/bin/codex.js'], [900, 'bash'], ]); const parents = new Map([ [2100, 1100], [1100, 900], [900, 1], ]); const resolved = resolveSessionOwnerPidFromAncestry(2100, { readParentPid: (pid) => parents.get(pid) ?? null, readProcessCommand: (pid) => commands.get(pid) ?? "", }); assert.equal(resolved, 1100); }); it("resolves the Codex owner from Windows process lineage without Unix ps", () => { const resolved = resolveSessionOwnerPidFromAncestry(1700, { platform: "win32", readProcessLineage: () => [ { pid: 1700, name: "powershell.exe", command: 'powershell.exe -File "C:\\Users\\user\\.codex\\hooks\\omx-native-hook-windows-shim.ps1"', }, { pid: 19072, name: "pwsh.exe", command: 'pwsh.exe -Command "verify expected codex.exe owner"', }, { pid: 14440, name: "codex.exe", command: '"C:\\Users\\user\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\codex.exe"', }, { pid: 14400, name: "node.exe", command: "node.exe codex.js" }, ], }); assert.equal(resolved, 14440); }); it("records keyword activation from UserPromptSubmit payloads", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-1", thread_id: "thread-1", turn_id: "turn-1", prompt: "$ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); assert.ok(result.outputJson, "UserPromptSubmit should emit developer context"); assert.match(JSON.stringify(result.outputJson), /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, "session-scoped keyword activation should not write root skill-active-state.json", ); const statePath = join(cwd, ".omx", "state", "sessions", "sess-1", "skill-active-state.json"); assert.equal(existsSync(statePath), true); const state = JSON.parse(await readFile(statePath, "utf-8")) as { skill?: string; active?: boolean; initialized_mode?: string; }; assert.equal(state.skill, "ralplan"); assert.equal(state.active, true); assert.equal(state.initialized_mode, "ralplan"); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-1", "ralplan-state.json")), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects deep-interview config overrides into UserPromptSubmit developer context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 enableChallengeModes = false `, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deep-interview-config", thread_id: "thread-1", turn_id: "turn-1", prompt: "$deep-interview prove config reflection", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const serializedOutput = JSON.stringify(result.outputJson); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /threshold=0\.05/); assert.match(serializedOutput, /max_rounds=15/); assert.match(serializedOutput, /enableChallengeModes=false/); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", "sess-deep-interview-config", "deep-interview-state.json", ), "utf-8", ), ) as { threshold?: number; max_rounds?: number; profile?: string }; assert.equal(modeState.profile, "standard"); assert.equal(modeState.threshold, 0.05); assert.equal(modeState.max_rounds, 15); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("proves UserPromptSubmit context changes before and after adding deep-interview config", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-before-after-"), ); const sessionId = "sess-deep-interview-config-before-after"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const before = await withIsolatedHome( "deep-interview-config-before-after", async () => dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-before-after", turn_id: "turn-before", prompt: "$deep-interview prove before config context", }, { cwd }, ), ); const beforeOutput = JSON.stringify(before.outputJson); const beforeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: unknown; threshold?: number; max_rounds?: number; }; assert.equal(before.skillState?.skill, "deep-interview"); assert.doesNotMatch( beforeOutput, /Deep-interview config override active/, ); assert.equal(before.skillState?.deep_interview_config, undefined); assert.equal(beforeState.deep_interview_config, undefined); assert.equal(beforeState.threshold, undefined); assert.equal(beforeState.max_rounds, undefined); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 `, ); const after = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-before-after", turn_id: "turn-after", prompt: "$deep-interview prove after config context", }, { cwd }, ); const afterOutput = JSON.stringify(after.outputJson); const afterState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: { profile?: string; threshold?: number; maxRounds?: number; }; threshold?: number; max_rounds?: number; }; assert.equal( after.skillState?.deep_interview_config?.profile, "standard", ); assert.match(afterOutput, /Deep-interview config override active/); assert.match(afterOutput, /threshold=0\.05/); assert.match(afterOutput, /max_rounds=15/); assert.equal(afterState.deep_interview_config?.profile, "standard"); assert.equal(afterState.threshold, 0.05); assert.equal(afterState.max_rounds, 15); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects deep-interview config for mixed workflow prompts that defer execution modes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-mixed-"), ); const sessionId = "sess-deep-interview-config-mixed"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "deep" deepThreshold = 0.13 deepMaxRounds = 21 enableChallengeModes = false `, ); const result = await withIsolatedHome( "deep-interview-config-mixed", async () => dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-mixed-config", turn_id: "turn-mixed-config", prompt: "$autopilot $deep-interview prove mixed config context", }, { cwd }, ), ); const serializedOutput = JSON.stringify(result.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: { profile?: string; threshold?: number; maxRounds?: number; enableChallengeModes?: boolean; }; profile?: string; threshold?: number; max_rounds?: number; enable_challenge_modes?: boolean; }; assert.equal(result.skillState?.skill, "deep-interview"); assert.deepEqual(result.skillState?.deferred_skills, ["autopilot"]); assert.equal(result.skillState?.deep_interview_config?.profile, "deep"); assert.equal(result.skillState?.deep_interview_config?.threshold, 0.13); assert.equal(result.skillState?.deep_interview_config?.maxRounds, 21); assert.equal( result.skillState?.deep_interview_config?.enableChallengeModes, false, ); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /profile=deep/); assert.match(serializedOutput, /threshold=0\.13/); assert.match(serializedOutput, /max_rounds=21/); assert.match(serializedOutput, /enableChallengeModes=false/); assert.equal(modeState.deep_interview_config?.profile, "deep"); assert.equal(modeState.profile, "deep"); assert.equal(modeState.threshold, 0.13); assert.equal(modeState.max_rounds, 21); assert.equal(modeState.enable_challenge_modes, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps deep-interview config override context on continuation prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-continuation-"), ); const sessionId = "sess-deep-interview-config-continuation"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 `, ); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-continuation", turn_id: "turn-start", prompt: "$deep-interview prove config continuation", }, { cwd }, ); const continued = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-continuation", turn_id: "turn-continue", prompt: "continue", }, { cwd }, ); const serializedOutput = JSON.stringify(continued.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { threshold?: number; max_rounds?: number; profile?: string }; assert.equal(continued.skillState?.skill, "deep-interview"); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /threshold=0\.05/); assert.match(serializedOutput, /max_rounds=15/); assert.equal(modeState.profile, "standard"); assert.equal(modeState.threshold, 0.05); assert.equal(modeState.max_rounds, 15); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps explicit deep-interview profile flags reflected on continuation prompts", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-deep-interview-config-profile-continuation-", ), ); const sessionId = "sess-deep-interview-config-profile-continuation"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.22 standardMaxRounds = 13 deepThreshold = 0.13 deepMaxRounds = 21 `, ); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-profile-continuation", turn_id: "turn-start", prompt: "$deep-interview --deep prove explicit profile continuation", }, { cwd }, ); const continued = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-profile-continuation", turn_id: "turn-continue", prompt: "continue", }, { cwd }, ); const serializedOutput = JSON.stringify(continued.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { threshold?: number; max_rounds?: number; profile?: string; deep_interview_config?: { profile?: string }; }; assert.equal(continued.skillState?.skill, "deep-interview"); assert.equal( continued.skillState?.deep_interview_config?.profile, "deep", ); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /profile=deep/); assert.match(serializedOutput, /threshold=0\.13/); assert.match(serializedOutput, /max_rounds=21/); assert.equal(modeState.deep_interview_config?.profile, "deep"); assert.equal(modeState.profile, "deep"); assert.equal(modeState.threshold, 0.13); assert.equal(modeState.max_rounds, 21); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the documented deep-interview Suggested Config reflected in UserPromptSubmit context", async () => { const skillDoc = await readFile( join(process.cwd(), "skills", "deep-interview", "SKILL.md"), "utf-8", ); const markerIndex = skillDoc.indexOf("## Suggested Config (optional)"); assert.notEqual(markerIndex, -1); const configMatch = skillDoc .slice(markerIndex) .match(/```toml\n([\s\S]*?)\n```/); assert.ok(configMatch); const documentedConfig = configMatch[1]?.trimEnd(); assert.ok(documentedConfig); assert.match(documentedConfig, /standardThreshold = 0\.20/); assert.match(documentedConfig, /standardMaxRounds = 12/); const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-doc-config-"), ); const sessionId = "sess-deep-interview-doc-config"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `${documentedConfig}\n`, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-doc-config", turn_id: "turn-doc-config", prompt: "$deep-interview prove documented config context", }, { cwd }, ); const serializedOutput = JSON.stringify(result.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: { profile?: string; threshold?: number; maxRounds?: number; }; profile?: string; threshold?: number; max_rounds?: number; }; assert.equal( result.skillState?.deep_interview_config?.profile, "standard", ); assert.equal(result.skillState?.deep_interview_config?.threshold, 0.2); assert.equal(result.skillState?.deep_interview_config?.maxRounds, 12); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /profile=standard/); assert.match(serializedOutput, /threshold=0\.2/); assert.match(serializedOutput, /max_rounds=12/); assert.equal(modeState.deep_interview_config?.profile, "standard"); assert.equal(modeState.profile, "standard"); assert.equal(modeState.threshold, 0.2); assert.equal(modeState.max_rounds, 12); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects deep-interview config overrides when state is boxed under OMX_ROOT", async () => { const root = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-boxed-"), ); const cwd = join(root, "source"); const omxRoot = join(root, "box"); const sessionId = "sess-boxed-deep-interview-config"; const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 `, ); process.env.OMX_ROOT = omxRoot; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-boxed", turn_id: "turn-boxed", prompt: "$deep-interview prove boxed config reflection", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal( result.skillState?.initialized_state_path, `.omx/state/sessions/${sessionId}/deep-interview-state.json`, ); const boxedStatePath = join( omxRoot, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ); assert.equal(existsSync(boxedStatePath), true); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), ), false, ); const serializedOutput = JSON.stringify(result.outputJson); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /threshold=0\.05/); assert.match(serializedOutput, /max_rounds=15/); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot; else delete process.env.OMX_STATE_ROOT; if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(root, { recursive: true, force: true }); } }); it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-")); const cwd = join(root, "source"); const omxRoot = join(root, "box"); const sessionId = "sess-boxed-ralplan"; const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const previousOmxSessionId = process.env.OMX_SESSION_ID; try { await mkdir(cwd, { recursive: true }); process.env.OMX_ROOT = omxRoot; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; process.env.OMX_SESSION_ID = sessionId; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-boxed", turn_id: "turn-boxed", prompt: "$ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); const boxedSessionDir = join( omxRoot, ".omx", "state", "sessions", sessionId, ); assert.equal( existsSync(join(boxedSessionDir, "skill-active-state.json")), true, ); assert.equal( existsSync(join(boxedSessionDir, "ralplan-state.json")), true, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json", ), ), false, ); const hudState = await readAllState(cwd); assert.equal(hudState.ralplan?.active, true); assert.equal(hudState.ralplan?.current_phase, "planning"); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot; else delete process.env.OMX_STATE_ROOT; if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(root, { recursive: true, force: true }); } }); it("records native keyword activation mode detail and skill state under OMX_TEAM_STATE_ROOT", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-")); const cwd = join(root, "source"); const teamStateRoot = join(root, "team-state"); const sessionId = "sess-team-root-ralplan"; const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const previousOmxSessionId = process.env.OMX_SESSION_ID; try { await mkdir(cwd, { recursive: true }); delete process.env.OMX_ROOT; delete process.env.OMX_STATE_ROOT; process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; process.env.OMX_SESSION_ID = sessionId; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-team-root", turn_id: "turn-team-root", prompt: "$ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); const teamSessionDir = join(teamStateRoot, "sessions", sessionId); assert.equal( existsSync(join(teamSessionDir, "skill-active-state.json")), true, ); assert.equal( existsSync(join(teamSessionDir, "ralplan-state.json")), true, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json", ), ), false, ); const hudState = await readAllState(cwd); assert.equal(hudState.ralplan?.active, true); assert.equal(hudState.ralplan?.current_phase, "planning"); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot; else delete process.env.OMX_STATE_ROOT; if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(root, { recursive: true, force: true }); } }); it("classifies only actionable goal completion wording", () => { const actionable = [ "complete this goal now", 'Performance goal complete; next call update_goal({status: "complete"}).', "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.", "omx ultragoal checkpoint --goal-id G001-demo --status complete --codex-goal-json goal.json", 'Call update_goal({status: "complete"}) after verification.', "Goal complete.", "The goal is complete.", "Goal complete: verified with tests.", "Goal complete — verified with tests.", "The goal is complete: verified.", "The goal is complete — verified.", ]; const ordinary = [ "my goal is to complete the migration without regressions", "Our goal is to finish this carefully after tests pass.", "The goal of this patch is to close a review gap.", "A goal can be complete only after a human review.", ]; for (const text of actionable) { assert.equal(looksLikeGoalCompletionPrompt(text), true, text); } for (const text of ordinary) { assert.equal(looksLikeGoalCompletionPrompt(text), false, text); } }); it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-")); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-goal-warning", thread_id: "thread-goal-warning", prompt: "complete this goal now", }, { cwd }, ); assert.match( JSON.stringify(result.outputJson), /requires Codex goal snapshot reconciliation/, ); assert.match(JSON.stringify(result.outputJson), /get_goal/); assert.match(JSON.stringify(result.outputJson), /--codex-goal-json/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop when a completion-like final answer skips active goal snapshot reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-stop-")); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "validation_passed", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-goal-stop", thread_id: "thread-goal-stop", last_assistant_message: 'Performance goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); assert.match( JSON.stringify(result.outputJson), /omx performance-goal complete --slug latency/, ); assert.match( JSON.stringify(result.outputJson), /Hooks must not mutate Codex goal state/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not repeat performance-goal reconciliation after a recorded objective mismatch blocker", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-performance-mismatch-blocked-stop-"), ); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "blocked", lastValidation: { status: "blocked", evidence: 'omx performance-goal complete rejected the fresh get_goal snapshot: Codex goal objective mismatch: expected "reduce latency", got "legacy objective".', recordedAt: "2026-05-20T00:00:00.000Z", }, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-performance-mismatch-blocked-stop", thread_id: "thread-performance-mismatch-blocked-stop", last_assistant_message: 'Performance goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx performance-goal complete --slug latency/, ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for an already complete performance-goal state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-performance-complete-stop-"), ); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-performance-complete-stop", thread_id: "thread-performance-complete-stop", last_assistant_message: 'Performance goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx performance-goal complete --slug latency/, ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("nudges next-goal prompts when a completed Codex goal cleanup step remains", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-prompt-"), ); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-completed-goal-prompt", thread_id: "thread-completed-goal-prompt", prompt: "Start the next performance goal now", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.match(output, /run \/goal clear/); assert.match(output, /before calling create_goal/); assert.match( output, /hooks only nudge and must not mutate Codex goal state/, ); assert.doesNotMatch(output, /cleared Codex goal state/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block ordinary Stop after completed Ultragoal cleanup remains", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-ultragoal-ordinary-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-completed-ultragoal-ordinary-stop", thread_id: "thread-completed-ultragoal-ordinary-stop", last_assistant_message: "Implemented the requested fix and verified the focused tests.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.doesNotMatch(output, /run \/goal clear/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat explanatory create_goal warnings as completed-goal cleanup attempts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-explainer-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); for (const [index, last_assistant_message] of [ "Do not call create_goal until the user has explicitly cleared the old goal state.", "No create_goal attempt was made; this is only the final summary.", "I am not calling create_goal; the completed work is summarized above.", "I am not starting another goal; cleanup is already documented.", "Do not start another goal until cleanup is explicit.", "Do not create a new ultragoal; this is only a final summary.", ].entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: `sess-completed-goal-explainer-stop-${index}`, thread_id: `thread-completed-goal-explainer-stop-${index}`, last_assistant_message, }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.doesNotMatch(output, /run \/goal clear/); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks explicit create_goal attempts when fresh native-goal cleanup evidence remains", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-mixed-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-completed-goal-mixed-stop", thread_id: "thread-completed-goal-mixed-stop", last_user_message: "get_goal reports a completed Codex goal still attached to this thread; do not call create_goal until cleanup is explicit.", last_assistant_message: "I am starting another run now; create_goal payload follows.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.equal(result.outputJson?.decision, "block"); assert.equal( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.match(output, /run \/goal clear/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when completed durable history alone precedes another goal", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-completed-goal-stop", thread_id: "thread-completed-goal-stop", last_assistant_message: "Starting another ultragoal now; create_goal payload follows after /goal clear already cleared native state.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.doesNotMatch(output, /run \/goal clear/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks ultragoal Stop for concise generic goal completion claims", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-generic-complete-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-generic-complete-stop", thread_id: "thread-ultragoal-generic-complete-stop", last_assistant_message: "Goal complete.", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("gives finite Stop guidance for paused ultragoals", async () => { for (const status of ["review_blocked", "needs_user_decision"]) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-ultragoal-${status}-stop-`)); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-paused", goals: [{ id: "G001-paused", status, objective: "Paused goal" }], }); const payload = { hook_event_name: "Stop", cwd, session_id: `sess-ultragoal-${status}-stop`, thread_id: `thread-ultragoal-${status}-stop`, turn_id: `turn-ultragoal-${status}-stop`, last_assistant_message: "Goal complete.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(first.outputJson?.decision, "block"); assert.equal(first.outputJson?.stopReason, "ultragoal_paused"); assert.match(String(first.outputJson?.systemMessage), new RegExp(`status ${status}`)); assert.doesNotMatch(JSON.stringify(first.outputJson), /ultragoal checkpoint/); const replayed = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true }, { cwd }, ); assert.equal(replayed.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not block ultragoal Stop for ordinary prose about a goal to complete work", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-ordinary-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-ordinary-stop", thread_id: "thread-ultragoal-ordinary-stop", last_assistant_message: "My goal is to complete the migration without regressions, so I will keep testing.", }, { cwd }, ); assert.notEqual( result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required", ); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks ultragoal Stop with blocked checkpoint and available-goal-context remediation for completed legacy snapshots", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-legacy-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-legacy-stop", thread_id: "thread-ultragoal-legacy-stop", last_assistant_message: "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.equal(result.outputJson?.decision, "block"); assert.match( output, /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); assert.match(output, /--status blocked/); assert.match(output, /Codex goal context/); assert.match(output, /no such table: thread_goals/); assert.match(output, /unavailable get_goal error JSON or path/); assert.match(output, /safe-recovery blocker/); assert.doesNotMatch(output, /fresh (?:Codex )?(?:thread|session)s?/i); assert.match(output, /Hooks must not mutate Codex goal state/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not repeat ultragoal Stop recovery after a safe completed-aggregate microgoal blocker is recorded", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-aggregate-blocked-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, codexGoalMode: "aggregate", activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal", failureReason: "aggregate Codex goal already complete and unreconcilable while repo-native .omx/ultragoal/goals.json still has an in-progress microgoal; stop the recovery loop", }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-aggregate-blocked-stop", thread_id: "thread-ultragoal-aggregate-blocked-stop", stop_hook_active: true, last_assistant_message: "Goal complete.", }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required", ); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, codexGoalMode: "aggregate", codexObjective: "Complete the durable ultragoal plan in .omx/ultragoal/goals.json, including later accepted/appended stories, under the original brief constraints; use .omx/ultragoal/ledger.jsonl as the audit trail.", activeGoalId: "G001-micro", aggregateCompletion: { status: "complete", completedAt: "2026-05-04T10:04:00.000Z", evidence: "planned work done; validation complete; reviews clean", }, goals: Array.from({ length: 136 }, (_, index) => ({ id: `G${String(index + 1).padStart(3, "0")}-micro`, status: index === 0 ? "in_progress" : "pending", objective: `Synthetic slice ${index + 1}.`, })), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-reconciled-stop", thread_id: "thread-ultragoal-reconciled-stop", last_assistant_message: "Yes — planned implementation work is done; ultragoal bookkeeping reconciled complete.", }, { cwd }, ); assert.notEqual( result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required", ); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => { for (const verdict of ["blocked", "fail", "failed"]) { const cwd = await mkdtemp( join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`), ); const slug = `${verdict}-mission`; try { await writeJson( join(cwd, ".omx", "goals", "autoresearch", slug, "mission.json"), { version: 1, workflow: "autoresearch-goal", slug, topic: "Blocked research", status: verdict === "blocked" ? "blocked" : "failed", }, ); await writeJson( join(cwd, ".omx", "goals", "autoresearch", slug, "completion.json"), { verdict, passed: false, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: `sess-autoresearch-${verdict}-stop`, thread_id: `thread-autoresearch-${verdict}-stop`, last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), new RegExp(`autoresearch-goal complete --slug ${slug}`), ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("blocks Stop for passing autoresearch-goal professor-critic verdicts that need reconciliation", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-pass-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "passing-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "passing-mission", topic: "Passing research", status: "validation_passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "passing-mission", "completion.json", ), { verdict: "fail", passed: true, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-pass-stop", thread_id: "thread-autoresearch-pass-stop", last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); assert.match( JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug passing-mission/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for autoresearch-goal verdict=pass even when passed is omitted", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-verdict-pass-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "verdict-pass-mission", topic: "Passing research", status: "validation_passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "completion.json", ), { verdict: "pass", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-verdict-pass-stop", thread_id: "thread-autoresearch-verdict-pass-stop", last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug verdict-pass-mission/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not repeat Stop block when the last autoresearch-goal completion attempt reported objective mismatch", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-mismatch-reported-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "mismatched-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "mismatched-mission", topic: "Passing research bound to another Codex goal", status: "passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "mismatched-mission", "completion.json", ), { verdict: "pass", passed: true, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-mismatch-reported-stop", thread_id: "thread-autoresearch-mismatch-reported-stop", last_assistant_message: [ "I called get_goal and ran omx autoresearch-goal complete --slug mismatched-mission --codex-goal-json /tmp/snapshot.json.", "The autoresearch-goal completion failed with Codex goal objective mismatch, so I will not repeat the same complete command blindly in this thread.", ].join("\n"), }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), /autoresearch-goal complete --slug mismatched-mission/, ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks later autoresearch-goal completion claims after an objective mismatch if no mismatch is reported in the final answer", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-mismatch-later-retry-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "retryable-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "retryable-mission", topic: "Passing research that can still retry with the correct snapshot", status: "passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "retryable-mission", "completion.json", ), { verdict: "pass", passed: true, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-mismatch-later-retry-stop", thread_id: "thread-autoresearch-mismatch-later-retry-stop", last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); assert.match( JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug retryable-mission/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats workflow keywords in native subagent prompt text as literal delegation text", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-subagent-keyword-literal-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-parent"; const leaderNativeSessionId = "native-parent-thread"; const childNativeSessionId = "native-child-thread"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-child-1", prompt: "$ralplan Architect review step. Review the draft plan and return APPROVE or ITERATE.", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync(join(stateDir, "skill-active-state.json")), false, ); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json"), ), false, ); assert.equal( existsSync( join( stateDir, "sessions", childNativeSessionId, "ralplan-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate Conductor guidance for typed agent-role prompts without native subagent tracking", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-typed-agent-role-autopilot-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-typed-executor", thread_id: "thread-typed-executor", agent_role: "executor", turn_id: "turn-typed-executor", prompt: "$autopilot continue the current implementation lane", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-typed-executor", "autopilot-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-typed-executor", "skill-active-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats installed custom native agent roles as typed lanes for prompt submit", async () => { await withIsolatedHome("custom-native-agent-role", async (homeDir) => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-custom-agent-role-autopilot-"), ); try { const agentsDir = join(homeDir, ".codex", "agents"); await mkdir(agentsDir, { recursive: true }); await writeFile( join(agentsDir, "custom-executor.toml"), [ "# oh-my-codex agent: custom-executor", 'name = "custom-executor"', 'description = "Custom executor lane"', ].join("\n"), "utf-8", ); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-custom-executor", thread_id: "thread-custom-executor", agent_type: "custom-executor", turn_id: "turn-custom-executor", prompt: "$autopilot continue the current implementation lane", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-custom-executor", "autopilot-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-custom-executor", "skill-active-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); it("does not inject Conductor guidance into typed agent-role autopilot continuations", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-typed-agent-role-active-autopilot-"), ); try { const sessionId = "sess-typed-executor-active"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "planning", initialized_mode: "autopilot", initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`, session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "execution", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-typed-executor-active", turn_id: "turn-typed-executor-active", agent_role: "executor", prompt: "keep going now", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.equal(result.omxEventName, "keyword-detector"); assert.doesNotMatch(message, /Conductor mode contract:/); assert.doesNotMatch( message, /Golden Rule: When the Main agent is acting in Conductor mode/, ); assert.doesNotMatch(message, /Conductor reuse and ledger guidance:/); assert.doesNotMatch(message, /typed subagents never receive this block/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat a corrupt leader kind=subagent tracker entry as native subagent prompt scope", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-corrupt-leader-subagent-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-corrupt-leader"; const leaderNativeSessionId = "native-corrupt-leader"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 2, }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: leaderNativeSessionId, turn_id: "turn-corrupt-leader", prompt: "$autopilot continue this review blocker fix", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets the current canonical leader boundary beat stale global subagent tracking with a distinct prompt thread id", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-current-leader-stale-global-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-current-leader"; const leaderNativeSessionId = "native-current-leader"; const staleSessionId = "sess-stale-subagent"; const staleLeaderNativeSessionId = "native-stale-leader"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, }, }, [staleSessionId]: { session_id: staleSessionId, leader_thread_id: staleLeaderNativeSessionId, updated_at: nowIso, threads: { [staleLeaderNativeSessionId]: { thread_id: staleLeaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: "thread-current-turn-not-native-session", turn_id: "turn-current-leader", prompt: "$autopilot continue", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); assert.equal( existsSync( join(stateDir, "sessions", staleSessionId, "autopilot-state.json"), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets the current session native leader beat stale global subagent tracking without a canonical summary and with a distinct prompt thread id", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-current-native-leader-stale-global-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-current-native-leader"; const leaderNativeSessionId = "native-current-leader-no-summary"; const staleSessionId = "sess-stale-native-subagent"; const staleLeaderNativeSessionId = "native-stale-parent"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [staleSessionId]: { session_id: staleSessionId, leader_thread_id: staleLeaderNativeSessionId, updated_at: nowIso, threads: { [staleLeaderNativeSessionId]: { thread_id: staleLeaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "critic", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: "thread-current-turn-not-native-session", turn_id: "turn-current-native-leader", prompt: "$autopilot continue", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); assert.equal( existsSync( join(stateDir, "sessions", staleSessionId, "autopilot-state.json"), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets the current session native leader beat a malformed canonical subagent entry", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-current-native-leader-malformed-canonical-", ), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-current-native-leader-malformed"; const leaderNativeSessionId = "native-current-leader-malformed"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: leaderNativeSessionId, turn_id: "turn-current-native-leader-malformed", prompt: "$autopilot continue", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still treats mixed child and leader payload identities as native subagent scope", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-mixed-child-leader-identity-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-mixed-child-leader"; const leaderNativeSessionId = "native-mixed-leader"; const childNativeSessionId = "native-mixed-child"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "critic", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: leaderNativeSessionId, turn_id: "turn-mixed-child-leader", prompt: "$ralplan review this as delegated text", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync( join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json"), ), false, ); assert.equal( existsSync( join( stateDir, "sessions", childNativeSessionId, "ralplan-state.json", ), ), false, ); const reversedResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-mixed-leader-child", prompt: "$autopilot review this as delegated text", }, { cwd }, ); assert.equal(reversedResult.omxEventName, "keyword-detector"); assert.equal(reversedResult.skillState, null); assert.equal(reversedResult.outputJson, null); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), false, ); assert.equal( existsSync( join( stateDir, "sessions", childNativeSessionId, "autopilot-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records plugin-prefixed keyword activation from UserPromptSubmit payloads", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-plugin-prefixed-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-plugin-1", thread_id: "thread-plugin-1", turn_id: "turn-plugin-1", prompt: "$oh-my-codex:ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-plugin-1", "ralplan-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies fresh autopilot activation at host-receipt preflight before protocol guidance", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-ralplan-gate-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-ralplan-gate", thread_id: "thread-autopilot-ralplan-gate", turn_id: "turn-autopilot-ralplan-gate", prompt: "$autopilot implement issue #2430", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); assert.deepEqual(result.skillState?.active_skills, []); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(message, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-ralplan-gate"); const autopilotState = JSON.parse( await readFile(join(sessionDir, "autopilot-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string; error?: string }; assert.equal(autopilotState.active, false); assert.equal(autopilotState.current_phase, "failed"); assert.equal(autopilotState.error, "documented_host_consensus_receipt_unavailable"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies fresh autopilot activation after capacity-only native subagent evidence", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-capacity-native-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-autopilot-capacity-native", thread_id: "thread-autopilot-capacity-native", turn_id: "turn-autopilot-capacity-native-spawn", tool_name: "multi_agent_v1.spawn_agent", tool_response: { error: "collab spawn failed: agent thread limit reached", }, }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-capacity-native", thread_id: "thread-autopilot-capacity-native", turn_id: "turn-autopilot-capacity-native-prompt", prompt: "$autopilot implement issue #3078", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(message, /Autopilot protocol:|Conductor mode contract:|deep-interview -> ralplan -> ultragoal/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-capacity-native"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies unsupported fresh autopilot activation at host-receipt preflight", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-unsupported-native-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-unsupported-native", thread_id: "thread-autopilot-unsupported-native", turn_id: "turn-autopilot-unsupported-native", prompt: "$autopilot implement issue #3078", capabilities: { native_subagents: false, multi_agent_v1: false }, }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(message, /Autopilot protocol:|Conductor mode contract:|deep-interview -> ralplan -> ultragoal/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-unsupported-native"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records ultragoal prompt skill activation with goal-tool handoff guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-1", thread_id: "thread-ultragoal-1", turn_id: "turn-ultragoal-1", prompt: "$ultragoal split this launch into durable goals", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ultragoal"); assert.equal(result.skillState?.initialized_mode, "ultragoal"); assert.equal( result.skillState?.initialized_state_path, ".omx/state/sessions/sess-ultragoal-1/ultragoal-state.json", ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /"\$ultragoal" -> ultragoal/); assert.match(message, /Ultragoal protocol:/); assert.match(message, /get_goal/); assert.match(message, /create_goal/); assert.match(message, /update_goal/); assert.match(message, /does not call `\/goal clear`/); assert.match(message, /multiple sequential ultragoal runs/); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ultragoal-1", "ultragoal-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps deep-interview active when ultragoal bypasses required planning handoff", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-deep-interview-handoff-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-ultragoal-handoff"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-ultragoal-handoff", native_session_id: "sess-ultragoal-handoff", leader_thread_id: "thread-ultragoal-handoff", }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-ultragoal-handoff", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-ultragoal-handoff", }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-ultragoal-handoff", question_enforcement: { obligation_id: "obligation-ultragoal-handoff", source: "omx-question", status: "pending", requested_at: "2026-05-21T03:00:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-handoff", thread_id: "thread-ultragoal-handoff", turn_id: "turn-ultragoal-handoff", prompt: "$ultragoal turn the clarified spec into goals", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.outputJson, null); const completed = JSON.parse( await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string; question_enforcement?: { status?: string; clear_reason?: string }; }; assert.equal(completed.active, true); assert.equal(completed.current_phase, "intent-first"); assert.equal(completed.question_enforcement?.status, "pending"); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); const edit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ultragoal-handoff", thread_id: "thread-ultragoal-handoff", tool_name: "Edit", tool_use_id: "tool-ultragoal-post-handoff-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b", }, }, { cwd }, ); assert.equal(edit.outputJson?.decision, "block"); assert.match( String(edit.outputJson?.reason ?? ""), /Deep-interview is active \(phase: intent-first\)/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("applies only explicit structured UserPromptSubmit ultragoal steering directives", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-steer-"), ); try { await createUltragoalPlan(cwd, { brief: "G002-cli-and-prompt-submit-bridge .omx/ultragoal hook steering fixture", goals: [ { title: "First", objective: "Complete first milestone with tests." }, ], }); const prose = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-steer-1", prompt: "Please add a subgoal for docs later; this is normal prose, not a directive.", }, { cwd }, ); assert.equal(prose.outputJson, null); assert.equal((await readUltragoalPlan(cwd)).goals.length, 1); const jsonExample = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-steer-1", prompt: `Here is an inert example:\n\`\`\`json\n${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Example JSON should not mutate .omx/ultragoal.", rationale: "Only explicit steering fences or labels are executable.", title: "Inert JSON example", objective: "This example must not be added.", })}\n\`\`\``, }, { cwd }, ); assert.equal(jsonExample.outputJson, null); assert.equal((await readUltragoalPlan(cwd)).goals.length, 1); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-steer-1", prompt: `OMX_ULTRAGOAL_STEER: ${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Prompt-submit supplied a structured .omx/ultragoal directive for G002-cli-and-prompt-submit-bridge.", rationale: "Add bounded hook regression work while preserving all completion gates.", title: "Prompt bridge regression", objective: "Verify UserPromptSubmit bounded steering bridge with tests.", })}`, }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /bounded \.omx\/ultragoal steering/); assert.match(message, /G002-cli-and-prompt-submit-bridge/); assert.match(message, /accepted/); const plan = await readUltragoalPlan(cwd); assert.equal(plan.goals.length, 2); assert.equal(plan.goals[1]?.title, "Prompt bridge regression"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not apply UserPromptSubmit ultragoal steering from native subagent prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-steer-subagent-"), ); try { await createUltragoalPlan(cwd, { brief: "G002-cli-and-prompt-submit-bridge .omx/ultragoal subagent steering fixture", goals: [ { title: "First", objective: "Complete first milestone with tests." }, ], }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-ultragoal-parent"; const leaderNativeSessionId = "native-ultragoal-parent"; const childNativeSessionId = "native-ultragoal-child"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-ultragoal-child-1", prompt: `OMX_ULTRAGOAL_STEER: ${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Subagent prompt text must be literal delegated context.", rationale: "Subagent prompts should not mutate the parent .omx/ultragoal ledger.", title: "Subagent should not add this", objective: "This must remain literal prompt text.", })}`, }, { cwd }, ); assert.equal(result.outputJson, null); const plan = await readUltragoalPlan(cwd); assert.equal(plan.goals.length, 1); const ledger = await readFile( join(cwd, ".omx/ultragoal/ledger.jsonl"), "utf-8", ); assert.equal( (ledger.match(/"event":"steering_accepted"/g) ?? []).length, 0, ); assert.equal( (ledger.match(/"event":"steering_rejected"/g) ?? []).length, 0, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("dedupes repeated UserPromptSubmit ultragoal steering directives by prompt signature", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-steer-dedupe-"), ); try { await createUltragoalPlan(cwd, { brief: "G002-cli-and-prompt-submit-bridge .omx/ultragoal dedupe fixture", goals: [ { title: "First", objective: "Complete first milestone with tests." }, ], }); const prompt = `\`\`\`omx-ultragoal-steer ${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Structured prompt-submit directive adds exactly one deduped goal.", rationale: "Use idempotent bridge semantics for repeated hook delivery.", title: "Deduped bridge regression", objective: "Verify repeated UserPromptSubmit steering does not duplicate goals.", })} \`\`\``; await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-dedupe", prompt, }, { cwd }, ); const second = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-dedupe", prompt, }, { cwd }, ); const message = String( ( second.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /deduped/); const plan = await readUltragoalPlan(cwd); assert.equal( plan.goals.filter((goal) => goal.title === "Deduped bridge regression") .length, 1, ); const ledger = await readFile( join(cwd, ".omx/ultragoal/ledger.jsonl"), "utf-8", ); assert.equal( (ledger.match(/"event":"steering_accepted"/g) ?? []).length, 1, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("normalizes the Korean keyboard typo for ulw during UserPromptSubmit activation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ulw-ko-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ulw-ko", thread_id: "thread-ulw-ko", turn_id: "turn-ulw-ko", prompt: "ㅕㅣㅈ로 병렬 처리해줘", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ultrawork"); assert.equal(result.skillState?.keyword, "ulw"); const additionalContext = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(additionalContext, /workflow keyword \"ulw\" -> ultrawork/); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ulw-ko", "ultrawork-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("adds ultrawork-specific activation guidance only for true ultrawork workflow activation", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultrawork-routing-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultrawork-msg", thread_id: "thread-ultrawork-msg", turn_id: "turn-ultrawork-msg", prompt: "$ultrawork fan out the regression checks", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ultrawork"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$ultrawork" -> ultrawork/); assert.match(message, /ground the task before editing/i); assert.match(message, /define pass\/fail acceptance criteria/i); assert.match(message, /direct-tool plus background evidence lanes/i); assert.match( message, /Ralph owns persistence and the full verified-completion promise/i, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate Ralph workflow state from a plain conversational mention", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ralph-plain-text-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ralph-plain-text", thread_id: "thread-ralph-plain-text", turn_id: "turn-ralph-plain-text", prompt: "why does ralph keep blocking stop?", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); // Triage may inject advisory LIGHT/explore context for the question-shaped // prompt, but the invariant this test guards is that no Ralph workflow state // is seeded and no Ralph-activation message is emitted. const advisoryContext = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.doesNotMatch(advisoryContext, /skill:\s*ralph/i); assert.doesNotMatch(advisoryContext, /ralph-state\.json/i); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ralph-plain-text", "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ralph-plain-text", "ralph-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("adds execution handoff context for non-keyword prompts that authorize implementation", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-execution-handoff-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const prompts = [ "按照这个plan开始执行优化", "开始执行", "继续优化", "直接修复", ]; for (const [index, prompt] of prompts.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: `sess-exec-handoff-${index}`, thread_id: `thread-exec-handoff-${index}`, turn_id: `turn-exec-handoff-${index}`, prompt, }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /execution handoff/i, prompt); assert.match(message, /Do not restate the prior plan/i, prompt); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("adds latest-followup priority context for short same-thread follow-up prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-followup-priority-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-followup-priority", thread_id: "thread-followup-priority", turn_id: "turn-followup-priority", prompt: "这些优化都做了么", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /same-thread follow-up/i); assert.match(message, /prefer it over older unresolved prompts/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clarifies that prompt-side $ralph activation does not invoke the PRD-gated CLI path", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-routing-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ralph-msg", thread_id: "thread-ralph-msg", turn_id: "turn-ralph-msg", prompt: "$ralph continue verification", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralph"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$ralph" -> ralph/); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.match( message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./, ); assert.match( message, /Use `omx ralph --prd \.\.\.` only when you explicitly want the PRD-gated CLI startup path\./, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clarifies that plugin-prefixed prompt-side $ralph activation does not invoke the PRD-gated CLI path", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-plugin-ralph-routing-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-plugin-ralph-msg", thread_id: "thread-plugin-ralph-msg", turn_id: "turn-plugin-ralph-msg", prompt: "$oh-my-codex:ralph continue verification", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralph"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$oh-my-codex:ralph" -> ralph/); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.match( message, /Prompt-side `\$ralph` activation seeds Ralph workflow state only; it does not invoke `omx ralph`\./, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps bare keep-going continuation on the active autopilot skill instead of denying with generic ralph overlap", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-bare-continuation-"), ); try { const sessionId = "sess-autopilot-cont"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "planning", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "execution", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-autopilot-cont", turn_id: "turn-autopilot-cont", prompt: "\ keep going now", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /"keep going" -> ralph/); assert.match(message, /Autopilot protocol:/); assert.match( message, /structured question chain, not a one-question gate/, ); assert.match(message, /re-score ambiguity against the active threshold/); assert.match(message, /max_rounds as a cap/); assert.match( message, /Do not advance from deep-interview to ralplan merely because the first question was answered/, ); assert.doesNotMatch(message, /denied workflow keyword/i); assert.doesNotMatch( message, /Unsupported workflow overlap: autopilot \+ ralph\./, ); assert.doesNotMatch(message, /Prompt-side `\$ralph` activation/); assert.equal(existsSync(join(sessionDir, "ralph-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps omx question answers on the active autopilot skill so the interview chain guidance is injected", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-question-answer-continuation-"), ); try { const sessionId = "sess-autopilot-question-answer"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "deep-interview", initialized_mode: "autopilot", initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`, session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-autopilot-question-answer", turn_id: "turn-autopilot-question-answer", prompt: "[omx question answered] semantic_marker_expansion $ralplan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /continued active workflow skill "autopilot"/); assert.match(message, /Autopilot protocol:/); assert.match( message, /structured question chain, not a one-question gate/, ); assert.match(message, /This turn is a marked omx question answer/); assert.match(message, /then re-score/); assert.match(message, /write interview_complete evidence and hand off/); assert.match( message, /readiness gate remains unresolved and the answer would materially change execution/, ); assert.match( message, /Do not advance from deep-interview to ralplan merely because the first question was answered/, ); assert.doesNotMatch(message, /denied workflow keyword/i); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps deep-interview bridge guidance on marked question answers with workflow-like tokens", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-deep-interview-question-answer-continuation-", ), ); try { const sessionId = "sess-deep-interview-question-answer"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", keyword: "$deep-interview", phase: "planning", initialized_mode: "deep-interview", initialized_state_path: `.omx/state/sessions/${sessionId}/deep-interview-state.json`, session_id: sessionId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", started_at: "2026-04-21T10:00:00.000Z", updated_at: "2026-04-21T10:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-deep-interview-question-answer", turn_id: "turn-deep-interview-question-answer", prompt: "[omx question answered] answer text $ralplan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /continued active workflow skill "deep-interview"/); assert.match( message, /workflow-like tokens inside the marked omx question answer are treated as answer text/, ); assert.match( message, /Deep-interview is active, but this session is not attached to tmux/, ); assert.match(message, /native structured question tool when available/); assert.doesNotMatch( message, /detected workflow keyword "\$ralplan" -> ralplan/, ); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clarifies outside-tmux prompt-side deep-interview activation without pretending omx question is directly available", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-routing-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deep-interview-msg", thread_id: "thread-deep-interview-msg", turn_id: "turn-deep-interview-msg", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$deep-interview" -> deep-interview/); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.match( message, /Deep-interview is active, but this session is not attached to tmux/, ); assert.match( message, /Do not invoke `omx question`, `omx hud`, or `omx team`/, ); assert.match(message, /native structured question tool when available/); assert.match(message, /ask exactly one concise plain-text question/); assert.match( message, /no tmux question obligation should be created outside tmux/, ); assert.doesNotMatch(message, /OMX_QUESTION_RETURN_PANE=/); assert.doesNotMatch(message, /preserve the leader pane/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("uses native fallback deep-interview guidance on Windows outside tmux", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-routing-win32-"), ); const originalPlatform = Object.getOwnPropertyDescriptor( process, "platform", ); try { Object.defineProperty(process, "platform", { value: "win32", configurable: true, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deep-interview-msg-win32", thread_id: "thread-deep-interview-msg-win32", turn_id: "turn-deep-interview-msg-win32", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match( message, /Deep-interview is active, but this session is not attached to tmux/, ); assert.match(message, /native structured question tool when available/); assert.doesNotMatch(message, /OMX_QUESTION_RETURN_PANE=/); assert.doesNotMatch(message, /current-session CLI bridge command/); } finally { if (originalPlatform) Object.defineProperty(process, "platform", originalPlatform); await rm(cwd, { recursive: true, force: true }); } }); it("includes leader-pane preservation guidance when a pane hint is available", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-pane-hint-"), ); try { const sessionId = "sess-deep-interview-pane-hint"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", started_at: "2026-04-21T10:00:00.000Z", updated_at: "2026-04-21T10:00:00.000Z", tmux_pane_id: "%77", }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-deep-interview-pane-hint", turn_id: "turn-deep-interview-pane-hint", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /not attached to tmux/); assert.match(message, /native structured question tool when available/); assert.match(message, /tmux return bridge \(%77\) is recorded/); assert.doesNotMatch(message, /current-session CLI bridge command/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("uses native fallback guidance on Windows when a pane hint is available", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-pane-hint-win32-"), ); const originalPlatform = Object.getOwnPropertyDescriptor( process, "platform", ); try { Object.defineProperty(process, "platform", { value: "win32", configurable: true, }); const sessionId = "sess-deep-interview-pane-hint-win32"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", started_at: "2026-04-21T10:00:00.000Z", updated_at: "2026-04-21T10:00:00.000Z", tmux_pane_id: "%77", }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-deep-interview-pane-hint-win32", turn_id: "turn-deep-interview-pane-hint-win32", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /not attached to tmux/); assert.match(message, /native structured question tool when available/); assert.match(message, /tmux return bridge \(%77\) is recorded/); assert.doesNotMatch(message, /OMX_QUESTION_RETURN_PANE=/); assert.doesNotMatch(message, /PowerShell\/background-terminal/); } finally { if (originalPlatform) Object.defineProperty(process, "platform", originalPlatform); await rm(cwd, { recursive: true, force: true }); } }); it("keeps bare keep-going continuation on the active ralph skill without resetting through generic keep-going routing", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ralph-bare-continuation-"), ); try { const sessionId = "sess-ralph-cont"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralph", keyword: "$ralph", phase: "executing", session_id: sessionId, active_skills: [ { skill: "ralph", phase: "executing", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", iteration: 4, max_iterations: 50, session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-ralph-cont", turn_id: "turn-ralph-cont", prompt: "keep going now", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralph"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /"keep going" -> ralph/); assert.doesNotMatch(message, /denied workflow keyword/i); assert.doesNotMatch(message, /mode transiting:/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores generic wrapper fields so metadata cannot trigger workflow routing or Stop blocking", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-wrapper-metadata-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-wrapper-meta-1", thread_id: "thread-wrapper-meta-1", turn_id: "turn-wrapper-meta-1", input: "$ralplan hidden wrapper text should stay non-routing", text: JSON.stringify({ hook_run_id: "native-stop-wrapper-1", note: "cancel stop wrapper metadata must not be treated like user intent", }), }, { cwd }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.equal(promptResult.skillState, null); assert.equal(promptResult.outputJson, null); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); const stopResult = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-wrapper-meta-1", thread_id: "thread-wrapper-meta-1", turn_id: "turn-wrapper-meta-2", }, { cwd }, ); assert.equal(stopResult.omxEventName, "stop"); assert.equal(stopResult.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not expose submitted prompt text to keyword-detector hook plugins", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-prompt-sanitized-"), ); try { await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "capture-keyword-context.mjs"), `import { mkdir, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; export async function onHookEvent(event) { if (event.event !== "keyword-detector") return; const outPath = join(process.cwd(), ".omx", "captured-keyword-context.json"); await mkdir(dirname(outPath), { recursive: true }); await writeFile(outPath, JSON.stringify(event.context, null, 2)); } `, "utf-8", ); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-sanitized-1", thread_id: "thread-sanitized-1", turn_id: "turn-sanitized-1", prompt: "$ralplan approve this blocker-sensitive request", }, { cwd }, ); const captured = JSON.parse( await readFile( join(cwd, ".omx", "captured-keyword-context.json"), "utf-8", ), ) as { prompt?: string; payload?: Record }; assert.equal(captured.prompt, undefined); assert.equal(captured.payload?.prompt, undefined); assert.equal(captured.payload?.input, undefined); assert.equal(captured.payload?.user_prompt, undefined); assert.equal(captured.payload?.userPrompt, undefined); assert.equal(captured.payload?.text, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not emit UserPromptSubmit routing context for unknown $tokens", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unknown-token-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-unknown-1", thread_id: "thread-unknown-1", turn_id: "turn-unknown-1", prompt: "$maer-thinking 다시 설명해봐", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not emit UserPromptSubmit routing context for unknown plugin-prefixed $tokens", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-unknown-plugin-token-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-unknown-plugin-1", thread_id: "thread-unknown-plugin-1", turn_id: "turn-unknown-plugin-1", prompt: "$oh-my-codex:maer-thinking 다시 설명해봐", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("rejects inert and reserved direct-looking submits across native and CLI surfaces without Stop blockers", async () => { const cases = [ { source: "codex-app", sessionId: "sess-inert-native", prompt: "Do not run $autopilot" }, { source: "cli", sessionId: "sess-reserved-cli", prompt: "/prompts:architect $autopilot" }, { source: "codex-app", sessionId: "sess-marked-native", prompt: "[omx question answered] $autopilot" }, ] as const; for (const testCase of cases) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-${testCase.sessionId}-`)); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const submit = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: testCase.source, session_id: testCase.sessionId, thread_id: `thread-${testCase.sessionId}`, prompt: testCase.prompt, }, { cwd }, ); assert.equal(submit.skillState, null); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", testCase.sessionId, "skill-active-state.json")), false, ); assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", testCase.sessionId, "autopilot-state.json")), false, ); assert.doesNotMatch( String((submit.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""), /detected workflow keyword|Autopilot protocol|denied workflow keyword/i, ); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, source: testCase.source, session_id: testCase.sessionId }, { cwd }, ); assert.equal(stop.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps issue #3133 negated, multilingual, and quoted ralplan mentions inert on the native entrypoint", async () => { const rejectedInputs = [ { source: "codex-app", sessionId: "sess-3133-negated", prompt: "Do not run $ralplan and do not repeat the review." }, { source: "cli", sessionId: "sess-3133-russian", prompt: "Не запускай $ralplan" }, { source: "codex-app", sessionId: "sess-3133-quoted", prompt: "Logged review text: \"$ralplan plan this change\"." }, ] as const; for (const testCase of rejectedInputs) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-${testCase.sessionId}-`)); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", testCase.sessionId); await mkdir(stateDir, { recursive: true }); const submit = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: testCase.source, session_id: testCase.sessionId, thread_id: `thread-${testCase.sessionId}`, turn_id: `turn-${testCase.sessionId}`, prompt: testCase.prompt, }, { cwd }, ); assert.equal(submit.skillState, null); assert.equal(existsSync(join(sessionDir, "skill-active-state.json")), false); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false); assert.equal(existsSync(join(stateDir, "skill-active-state.json")), false); assert.doesNotMatch( String((submit.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""), /ralplan/i, ); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, source: testCase.source, session_id: testCase.sessionId, thread_id: `thread-${testCase.sessionId}`, turn_id: `stop-${testCase.sessionId}`, }, { cwd }, ); assert.equal(stop.outputJson, null); assert.doesNotMatch(JSON.stringify(stop.outputJson ?? {}), /ralplan/i); } finally { await rm(cwd, { recursive: true, force: true }); } } const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3133-positive-")); const sessionId = "sess-3133-positive"; try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(stateDir, { recursive: true }); const submit = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: "thread-3133-positive", turn_id: "turn-3133-positive", prompt: "$ralplan plan this change", }, { cwd }, ); assert.equal(submit.skillState?.skill, "ralplan"); assert.equal(submit.skillState?.active, true); assert.equal(existsSync(join(sessionDir, "skill-active-state.json")), true); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), true); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: "thread-3133-positive", turn_id: "stop-3133-positive", }, { cwd }, ); assert.equal(stop.outputJson?.decision, "block"); assert.match(String(stop.outputJson?.reason ?? ""), /ralplan is still active/i); assert.match(String(stop.outputJson?.reason ?? ""), /continue from the current ralplan artifact/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies fresh leading Autopilot invocations on native and CLI prompt submits", async () => { for (const source of ["codex-app", "cli"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-direct-${source}-`)); const sessionId = `sess-direct-${source}`; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source, session_id: sessionId, thread_id: `thread-${source}`, prompt: "$autopilot resume this task", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json")), true, ); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("uses the first effective Team match for native outside-tmux blocking", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-first-effective-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const ralphFirst = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "sess-ralph-first-team", thread_id: "thread-ralph-first-team", prompt: "$ralph $team ship this fix", }, { cwd }, ); assert.equal(ralphFirst.skillState?.skill, "ralph"); assert.equal(ralphFirst.skillState?.active, true); assert.doesNotMatch(JSON.stringify(ralphFirst.outputJson), /cannot activate the tmux-only `team` workflow directly/); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "sess-ralph-first-team", "ralph-state.json")), true, ); assert.equal(ralphFirst.skillState?.active_skills?.some((entry) => entry.skill === "team"), false); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); assert.doesNotMatch(JSON.stringify(ralphFirst.outputJson), /Use the durable OMX team runtime via `omx team \.\.\.`/); const teamFirst = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "sess-team-first-ralph", thread_id: "thread-team-first-ralph", prompt: "$team $ralph ship this fix", }, { cwd }, ); assert.equal(teamFirst.skillState?.skill, "team"); assert.equal(teamFirst.skillState?.active, false); assert.match(JSON.stringify(teamFirst.outputJson), /cannot activate the tmux-only `team` workflow directly/); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "sess-team-first-ralph", "ralph-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies direct $team prompt activation from Codex App/native outside tmux", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-native-block-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "sess-team-1", thread_id: "thread-team-1", turn_id: "turn-team-1", prompt: "$team ship this fix with verification", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "team"); assert.equal(result.skillState?.active, false); assert.match( String(result.skillState?.transition_error || ""), /cannot activate the tmux-only `team` workflow directly/, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } | null )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(message, /denied workflow keyword "\$team" -> team/); assert.match(message, /attached tmux shell first/); assert.equal( existsSync(join(cwd, ".omx", "state", "team-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still denies direct $team prompt activation from Codex App/native outside tmux when a tmux return bridge exists", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-team-native-bridge-block-"), ); try { await mkdir(join(cwd, ".omx", "state", "sessions", "sess-team-bridge"), { recursive: true, }); await writeJson( join( cwd, ".omx", "state", "sessions", "sess-team-bridge", "ralph-state.json", ), { mode: "ralph", active: true, tmux_pane_id: "%42", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "sess-team-bridge", thread_id: "thread-team-bridge", turn_id: "turn-team-bridge", prompt: "$team ship this fix with verification", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "team"); assert.equal(result.skillState?.active, false); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } | null )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(message, /attached tmux shell first/); assert.equal( existsSync(join(cwd, ".omx", "state", "team-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps direct CLI outside-tmux $team prompt guidance compatible with manual shell launch", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-team-cli-guidance-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "cli", session_id: "sess-team-cli-guidance", thread_id: "thread-team-cli-guidance", turn_id: "turn-team-cli-guidance", prompt: "$team ship this fix with verification", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } | null )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(message, /run `omx team \.\.\.` yourself from shell/); assert.doesNotMatch(message, /not directly available here/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps $team prompt-submit routing directly tmux-capable when already inside tmux", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-tmux-")); process.env.TMUX = "/tmp/tmux-live"; process.env.TMUX_PANE = "%5"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-team-tmux-1", thread_id: "thread-team-tmux-1", turn_id: "turn-team-tmux-1", prompt: "$team ship this fix with verification", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match( message, /Use the durable OMX team runtime via `omx team \.\.\.`/, ); assert.match(message, /run `omx team --help` yourself/); assert.doesNotMatch(message, /not directly available here/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns actionable denial guidance for unsupported workflow overlaps on prompt submit", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-transition-deny-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deny-1", thread_id: "thread-deny-1", turn_id: "turn-deny-1", prompt: "$team ship this fix", }, { cwd }, ); const denied = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deny-1", thread_id: "thread-deny-1", turn_id: "turn-deny-2", prompt: "$autopilot also run this", }, { cwd }, ); assert.match( JSON.stringify(denied.outputJson), /denied workflow keyword/i, ); assert.match( JSON.stringify(denied.outputJson), /Unsupported workflow overlap: team \+ autopilot\./, ); assert.match( JSON.stringify(denied.outputJson), /omx state clear --input/, ); assert.match(JSON.stringify(denied.outputJson), /mode\\":\\"/); assert.match(JSON.stringify(denied.outputJson), /--json/); assert.match( JSON.stringify(denied.outputJson), /explicit MCP compatibility is enabled/, ); assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` tools/); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-deny-1", "autopilot-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("surfaces transition success output for allowlisted prompt-submit handoffs", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-transition-success-"), ); try { const sessionDir = join( cwd, ".omx", "state", "sessions", "sess-handoff-1", ); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", deep_interview_gate: { status: "complete", rationale: "Requirements are clarified and ready for ralplan consensus.", }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: "sess-handoff-1", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-handoff-1", }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-handoff-1", thread_id: "thread-handoff-1", turn_id: "turn-handoff-1", prompt: "$ralplan implement the approved contract", }, { cwd }, ); assert.match( JSON.stringify(result.outputJson), /mode transiting: deep-interview -> ralplan/, ); const completed = JSON.parse( await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string; }; assert.equal(completed.active, false); assert.equal(completed.current_phase, "completed"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the planning skill active when planning and execution workflows are invoked together", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-planning-precedence-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-multi-1", thread_id: "thread-multi-1", turn_id: "turn-multi-1", prompt: "$ralplan $team $ralph ship this fix", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$ralplan" -> ralplan/); assert.match(message, /\$team" -> team/); assert.match(message, /\$ralph" -> ralph/); assert.doesNotMatch(message, /mode transiting:/); assert.match( message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./, ); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.doesNotMatch( message, /Use the durable OMX team runtime via `omx team \.\.\.`/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the planning skill active for mixed plugin-prefixed and bare workflow invocations together", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-plugin-planning-precedence-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-plugin-multi-1", thread_id: "thread-plugin-multi-1", turn_id: "turn-plugin-multi-1", prompt: "$oh-my-codex:ralplan $team $oh-my-codex:ralph ship this fix", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/); assert.match(message, /\$team" -> team/); assert.match(message, /\$oh-my-codex:ralph" -> ralph/); assert.doesNotMatch(message, /mode transiting:/); assert.match( message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./, ); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("skips prompt-submit HUD reconciliation for confirmed team worker panes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-team-worker-skip-"), ); try { const teamName = "hud-worker-skip"; await configureAuthoritativeTeamWorker(cwd, teamName); process.env.TMUX = "1"; process.env.TMUX_PANE = "%10"; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCalls = 0; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-team-worker", prompt: "$ralplan prepare plan", }, { cwd, reconcileHudForPromptSubmitFn: async () => { reconcileCalls += 1; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(reconcileCalls, 0); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves prompt-submit HUD reconciliation for team leader panes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-team-leader-preserve-"), ); try { const teamName = "hud-leader-keep"; await initTeamState( teamName, "preserve leader HUD reconcile", "executor", 1, cwd, ); await setTeamPaneIds(cwd, teamName, { leaderPaneId: "%42", workerPaneIds: { "worker-1": "%10" }, }); process.env.TMUX = "1"; process.env.TMUX_PANE = "%42"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCall: { cwd: string; sessionId?: string } | null = null; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-team-leader", prompt: "$ralplan prepare plan", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: "sess-hud-team-leader", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves prompt-submit HUD reconciliation when worker pane detection is ambiguous", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-team-worker-ambiguous-"), ); try { const teamName = "hud-worker-ambiguous"; await configureAuthoritativeTeamWorker(cwd, teamName); process.env.TMUX = "1"; process.env.TMUX_PANE = "%99"; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCalls = 0; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-team-worker-ambiguous", prompt: "$ralplan prepare plan", }, { cwd, reconcileHudForPromptSubmitFn: async () => { reconcileCalls += 1; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(reconcileCalls, 1); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves prompt-submit HUD reconciliation for native subagents even with worker pane env", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-subagent-worker-preserve-"), ); try { const teamName = "hud-subagent-keep"; await initTeamState( teamName, "preserve subagent HUD reconcile", "executor", 1, cwd, ); await setTeamPaneIds(cwd, teamName, { leaderPaneId: "%42", workerPaneIds: { "worker-1": "%10" }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-subagent-hud-parent"; const leaderNativeSessionId = "native-subagent-hud-parent"; const childNativeSessionId = "native-subagent-hud-child"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "verifier", }, }, }, }, }); process.env.TMUX = "1"; process.env.TMUX_PANE = "%10"; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCall: { cwd: string; sessionId?: string } | null = null; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-subagent-hud-child", prompt: "Review the worker patch literally; do not activate $ralplan.", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.outputJson, null); assert.equal(reconcileCall, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("runs prompt-submit HUD reconciliation as a best-effort tmux-only side effect", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reconcile-")); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalPath = process.env.PATH; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; const originalArgv = process.argv; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%1"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "hud-config.json"), JSON.stringify( { preset: "focused", git: { display: "branch" } }, null, 2, ), ); const binDir = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-reconcile-bin-"), ); const tmuxLog = join(cwd, "tmux.log"); await writeFile( join(binDir, "tmux"), `#!/usr/bin/env bash set -euo pipefail printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)} options_file=${JSON.stringify(join(cwd, "tmux-options"))} state_file=${JSON.stringify(join(cwd, "tmux-state"))} if [[ -f "$state_file" ]]; then IFS=$'\t' read -r panes marker < "$state_file" else panes='%1' marker='' fi case "$1" in list-panes) if [[ "$*" == *'#{pane_id} #{pane_dead} #{pane_pid}'* ]]; then printf '%%1 0 200\n' [[ "$panes" == *'%9'* ]] && printf '%%9 0 201\n' elif [[ "$*" == *'pane_start_command'* ]]; then printf '%%1\t/bin/codex\n' [[ "$panes" == *'%9'* ]] && printf '%%9\tOMX_TMUX_SPLIT_OPERATION_MARKER='"'"'"$marker'"'"'; export OMX_TMUX_SPLIT_OPERATION_MARKER; node dist/cli/omx.js hud --watch\n' elif [[ "$panes" == *'%9'* ]]; then printf '%%1\n%%9\n' else printf '%%1\n' fi ;; display-message) if [[ "$*" == *'#{pane_id}'*'#{pane_dead}'*'#{pane_pid}'*'#{session_id}'*'#{window_id}'* ]]; then printf '%%1\t0\t200\t$1\t@1\n' elif [[ "$*" == *'#{session_id}'*'#{window_id}'* ]]; then printf '$1\t@1\n' else printf '200\t60\n' fi ;; set-option) printf '%s\t%s\n' "$3" "$4" >> "$options_file" ;; show-options) value='' if [[ -f "$options_file" ]]; then while IFS=$'\t' read -r key stored; do [[ "$key" == "$4" ]] && value="$stored" done < "$options_file" fi printf '%s\n' "$value" ;; if-shell) success="$6" if [[ "$success" == *'split-window'* ]]; then if [[ "$success" =~ OMX_TMUX_SPLIT_OPERATION_MARKER=\\'([^\\']+)\\' ]]; then marker="\${BASH_REMATCH[1]}" fi panes='%1 %9' printf '%s\t%s\n' "$panes" "$marker" > "$state_file" fi receipt="\${success#*display-message -p }" if [[ "$receipt" != "$success" ]]; then receipt="\${receipt%%[[:space:]]*}" printf '%s\n' "$receipt" fi ;; resize-pane) ;; esac ` ); await chmod(join(binDir, "tmux"), 0o755); const tmuxSyntax = spawnSync("bash", ["-n", join(binDir, "tmux")], { encoding: "utf-8", }); assert.equal(tmuxSyntax.status, 0, tmuxSyntax.stderr); process.env.PATH = `${binDir}:${originalPath}`; process.argv = [originalArgv[0] || "node", "/tmp/codex-host-binary"]; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-1", prompt: "$ralplan prepare plan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); const tmuxCalls = await readFile(tmuxLog, "utf-8"); assert.match(tmuxCalls, /list-panes -t %1 -F/); assert.match( tmuxCalls, new RegExp(`if-shell -F -t %1 [^\\n]*#\\{pane_pid\\},200[^\\n]*#\\{session_id\\},\\$1[^\\n]*#\\{window_id\\},@1[^\\n]*split-window -v -l ${HUD_TMUX_HEIGHT_LINES} -d -t %1 [^\\n]*display-message -p __omx_hud_split_`), ); assert.match( tmuxCalls, /dist\/cli\/omx\.js' hud --watch '--preset=focused'/, ); assert.doesNotMatch(tmuxCalls, /\/tmp\/codex-host-binary' hud --watch/); } finally { if (originalTmux === undefined) { delete process.env.TMUX; } else { process.env.TMUX = originalTmux; } if (originalTmuxPane === undefined) { delete process.env.TMUX_PANE; } else { process.env.TMUX_PANE = originalTmuxPane; } if (originalHudOwner === undefined) { delete process.env[OMX_TMUX_HUD_OWNER_ENV]; } else { process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; } process.env.PATH = originalPath; process.argv = originalArgv; await rm(cwd, { recursive: true, force: true }); } }); it("skips prompt-submit HUD reconciliation during doctor smoke validation", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-doctor-smoke-hud-"), ); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; const originalDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%1"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = "1"; let reconcileCalled = false; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "omx-doctor-plugin-hook-smoke", prompt: "$ralplan doctor plugin hook smoke test", }, { cwd, reconcileHudForPromptSubmitFn: async () => { reconcileCalled = true; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(reconcileCalled, false); } finally { if (originalTmux === undefined) delete process.env.TMUX; else process.env.TMUX = originalTmux; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV]; else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; if (originalDoctorSmoke === undefined) delete process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE; else process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = originalDoctorSmoke; await rm(cwd, { recursive: true, force: true }); } }); it("recreates a leader-only HUD pane when UserPromptSubmit revives with the canonical session id", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-")); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalPath = process.env.PATH; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%1"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; const canonicalSessionId = "omx-canonical-hud-reuse"; const nativeSessionId = "codex-native-hud-reuse"; await mkdir(join(cwd, ".omx", "state", "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId }); const binDir = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-reuse-bin-"), ); const tmuxLog = join(cwd, "tmux.log"); await writeFile( join(binDir, "tmux"), `#!/usr/bin/env bash set -euo pipefail printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)} options_file=${JSON.stringify(join(cwd, "tmux-options"))} state_file=${JSON.stringify(join(cwd, "tmux-state"))} if [[ -f "$state_file" ]]; then IFS=$'\t' read -r panes marker < "$state_file" else panes='%1 %2' marker='' fi case "$1" in list-panes) if [[ "$*" == *'#{pane_id} #{pane_dead} #{pane_pid}'* ]]; then printf '%%1 0 200\n%%2 0 201\n' [[ "$panes" == *'%9'* ]] && printf '%%9 0 202\n' elif [[ "$*" == *'pane_start_command'* ]]; then printf '%%1\t/bin/codex\n' printf '%%2\texec env OMX_TMUX_HUD_OWNER='"'"'"1'"'"' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='"'"'"%%1'"'"' /node /omx.js hud --watch\n' [[ "$panes" == *'%9'* ]] && printf '%%9\tOMX_TMUX_SPLIT_OPERATION_MARKER='"'"'"$marker'"'"'; export OMX_TMUX_SPLIT_OPERATION_MARKER; node dist/cli/omx.js hud --watch\n' elif [[ "$panes" == *'%9'* ]]; then printf '%%1\n%%2\n%%9\n' else printf '%%1\n%%2\n' fi ;; display-message) if [[ "$*" == *'#{pane_id}'*'#{pane_dead}'*'#{pane_pid}'*'#{session_id}'*'#{window_id}'* ]]; then printf '%%1\t0\t200\t$1\t@1\n' elif [[ "$*" == *'#{session_id}'*'#{window_id}'* ]]; then printf '$1\t@1\n' else printf '200\t60\n' fi ;; set-option) printf '%s\t%s\n' "$3" "$4" >> "$options_file" ;; show-options) value='' if [[ -f "$options_file" ]]; then while IFS=$'\t' read -r key stored; do [[ "$key" == "$4" ]] && value="$stored" done < "$options_file" fi printf '%s\n' "$value" ;; if-shell) success="$6" if [[ "$success" == *'split-window'* ]]; then if [[ "$success" =~ OMX_TMUX_SPLIT_OPERATION_MARKER=\\'([^\\']+)\\' ]]; then marker="\${BASH_REMATCH[1]}" fi panes='%1 %2 %9' printf '%s\t%s\n' "$panes" "$marker" > "$state_file" fi receipt="\${success#*display-message -p }" if [[ "$receipt" != "$success" ]]; then receipt="\${receipt%%[[:space:]]*}" printf '%s\n' "$receipt" fi ;; resize-pane) ;; esac ` ); await chmod(join(binDir, "tmux"), 0o755); const tmuxSyntax = spawnSync("bash", ["-n", join(binDir, "tmux")], { encoding: "utf-8", }); assert.equal(tmuxSyntax.status, 0, tmuxSyntax.stderr); process.env.PATH = `${binDir}:${originalPath}`; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-hud-reuse", turn_id: "turn-hud-reuse", prompt: "$ralplan prepare plan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); const tmuxCalls = await readFile(tmuxLog, "utf-8"); assert.match(tmuxCalls, /list-panes -t %1 -F/); assert.match( tmuxCalls, /if-shell -F -t %1 [^\n]*#\{pane_pid\},200[^\n]*#\{session_id\},\$1[^\n]*#\{window_id\},@1[^\n]*split-window [^\n]*display-message -p __omx_hud_split_/, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json", ), ), true, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json", ), ), false, ); } finally { if (originalTmux === undefined) delete process.env.TMUX; else process.env.TMUX = originalTmux; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV]; else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; process.env.PATH = originalPath; await rm(cwd, { recursive: true, force: true }); } }); it("skips prompt-submit HUD reconciliation inside unowned tmux panes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-unowned-")); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalPath = process.env.PATH; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%claude"; delete process.env[OMX_TMUX_HUD_OWNER_ENV]; const binDir = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-unowned-bin-"), ); const tmuxLog = join(cwd, "tmux.log"); await writeFile( join(binDir, "tmux"), `#!/usr/bin/env bash printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)} exit 0 `, ); await chmod(join(binDir, "tmux"), 0o755); process.env.PATH = `${binDir}:${originalPath}`; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-unowned", prompt: "$ralplan prepare plan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(existsSync(tmuxLog), false); } finally { if (originalTmux === undefined) delete process.env.TMUX; else process.env.TMUX = originalTmux; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV]; else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; process.env.PATH = originalPath; await rm(cwd, { recursive: true, force: true }); } }); it("blocks Bash omx question when no leader-pane return hint is preserved", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-enforce-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-block", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( String( (result.outputJson as { systemMessage?: string } | null) ?.systemMessage || "", ), /OMX_QUESTION_RETURN_PANE=\$TMUX_PANE/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Bash commands that only mention omx question in quoted arguments", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-quoted-mention-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-quoted-mention", tool_input: { command: `omx ultragoal create-goals --brief "Deep interview says omx question failed in tmux"`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Bash heredocs that only document omx question text", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-heredoc-mention-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-heredoc-mention", tool_input: { command: `cat > issue-notes.md <<'EOF'\nomx question failed in the attached tmux pane\nEOF`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Bash omx question when the command preserves the leader-pane return hint", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-allow-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-allow", tool_input: { command: `OMX_QUESTION_RETURN_PANE=$TMUX_PANE omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows the quoted pane env assignment emitted by the deep-interview bridge command", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-quoted-allow-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-quoted-allow", tool_input: { command: `OMX_QUESTION_RETURN_PANE='%42' node ./dist/cli/omx.js question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows PowerShell env bridge forms for omx question return panes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-powershell-allow-"), ); try { const commands = [ `$env:OMX_QUESTION_RETURN_PANE=$env:TMUX_PANE; omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, `$env:OMX_QUESTION_RETURN_PANE='%42'; node ./dist/cli/omx.js question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, `$env:OMX_LEADER_PANE_ID="%43"; omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, ]; for (const [index, command] of commands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: `tool-question-powershell-allow-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Bash omx question when a valid inherited OMX_QUESTION_RETURN_PANE bridge is already exported", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-env-allow-"), ); const originalReturnPane = process.env.OMX_QUESTION_RETURN_PANE; try { process.env.OMX_QUESTION_RETURN_PANE = "%42"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-env-allow", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (originalReturnPane === undefined) delete process.env.OMX_QUESTION_RETURN_PANE; else process.env.OMX_QUESTION_RETURN_PANE = originalReturnPane; await rm(cwd, { recursive: true, force: true }); } }); it("allows Bash omx question when a valid inherited OMX_LEADER_PANE_ID bridge is already exported", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-leader-env-allow-"), ); const originalLeaderPane = process.env.OMX_LEADER_PANE_ID; try { process.env.OMX_LEADER_PANE_ID = "%43"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-leader-env-allow", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (originalLeaderPane === undefined) delete process.env.OMX_LEADER_PANE_ID; else process.env.OMX_LEADER_PANE_ID = originalLeaderPane; await rm(cwd, { recursive: true, force: true }); } }); it("still blocks Bash omx question when an inherited OMX_QUESTION_RETURN_PANE value is malformed", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-env-malformed-"), ); const originalReturnPane = process.env.OMX_QUESTION_RETURN_PANE; try { process.env.OMX_QUESTION_RETURN_PANE = "not-a-pane"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-env-malformed", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); } finally { if (originalReturnPane === undefined) delete process.env.OMX_QUESTION_RETURN_PANE; else process.env.OMX_QUESTION_RETURN_PANE = originalReturnPane; await rm(cwd, { recursive: true, force: true }); } }); it("blocks Bash node omx.js question when the command does not preserve the leader-pane return hint", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-node-block-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-node-block", tool_input: { command: `node ./dist/cli/omx.js question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks native/App Bash omx question with bridge-specific outside-tmux guidance", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-native-block-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "codex-app", session_id: "sess-question-native-block", tool_name: "Bash", tool_use_id: "tool-question-native-block", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.equal( (result.outputJson as { hookSpecificOutput?: unknown } | null) ?.hookSpecificOutput, undefined, ); assert.match( String((result.outputJson as { reason?: string } | null)?.reason || ""), /Codex App\/native outside-tmux Bash sessions/, ); assert.match( String( (result.outputJson as { systemMessage?: string } | null) ?.systemMessage || "", ), /native structured question tool/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks native/App Bash omx question even when the command preserves a tmux return bridge", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-native-allow-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "codex-app", session_id: "sess-question-native-bridge-block", tool_name: "Bash", tool_use_id: "tool-question-native-bridge-block", tool_input: { command: `OMX_QUESTION_RETURN_PANE=$TMUX_PANE omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( String( (result.outputJson as { systemMessage?: string } | null) ?.systemMessage || "", ), /native structured question tool/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks native/App Bash omx question when a valid inherited OMX_QUESTION_RETURN_PANE bridge is already exported", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-native-env-allow-"), ); const originalReturnPane = process.env.OMX_QUESTION_RETURN_PANE; try { process.env.OMX_QUESTION_RETURN_PANE = "%42"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "codex-app", session_id: "sess-question-native-env-allow", tool_name: "Bash", tool_use_id: "tool-question-native-env-allow", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); } finally { if (originalReturnPane === undefined) delete process.env.OMX_QUESTION_RETURN_PANE; else process.env.OMX_QUESTION_RETURN_PANE = originalReturnPane; await rm(cwd, { recursive: true, force: true }); } }); it("blocks Bash omx hud from Codex App/native outside tmux without PreToolUse additionalContext", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-hud-native-block-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "codex-app", session_id: "sess-hud-native-block", tool_name: "Bash", tool_use_id: "tool-hud-native-block", tool_input: { command: "omx hud --tmux" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.equal( (result.outputJson as { hookSpecificOutput?: unknown } | null) ?.hookSpecificOutput, undefined, ); assert.match( String( (result.outputJson as { systemMessage?: string } | null) ?.systemMessage || "", ), /attached tmux shell first/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Bash omx team from Codex App/native outside tmux", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-team-native-block-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "codex-app", session_id: "sess-team-native-block", tool_name: "Bash", tool_use_id: "tool-team-native-block", tool_input: { command: "omx team status my-team" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.equal( (result.outputJson as { hookSpecificOutput?: unknown } | null) ?.hookSpecificOutput, undefined, ); assert.match( String((result.outputJson as { reason?: string } | null)?.reason || ""), /cannot be launched directly from Codex App\/native outside-tmux Bash sessions/, ); assert.match( String( (result.outputJson as { systemMessage?: string } | null) ?.systemMessage || "", ), /launch OMX CLI from an attached tmux shell first/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Bash node omx.js team from Codex App/native outside tmux", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-team-node-native-block-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "codex-app", session_id: "sess-team-node-native-block", tool_name: "Bash", tool_use_id: "tool-team-node-native-block", tool_input: { command: "node ./dist/cli/omx.js team status my-team" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( String( (result.outputJson as { systemMessage?: string } | null) ?.systemMessage || "", ), /Codex App\/native outside-tmux sessions/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves direct CLI outside-tmux omx team Bash behavior", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-team-cli-outside-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "cli", session_id: "sess-team-cli-outside", tool_name: "Bash", tool_use_id: "tool-team-cli-outside", tool_input: { command: "omx team status my-team" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves source-less outside-tmux omx team Bash behavior when no native session evidence exists", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-team-cli-nosource-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-team-cli-nosource", tool_name: "Bash", tool_use_id: "tool-team-cli-nosource", tool_input: { command: "omx team status my-team" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation file edits while deep-interview remains active after a clarified answer", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-deep-interview-edit-block-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-edit-block"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-edit-block", cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-edit-block", thread_id: "thread-di-edit-block", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-edit-block", thread_id: "thread-di-edit-block", }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-edit-block", thread_id: "thread-di-edit-block", rounds: [ { answer: "Implement by editing src/hooks/keyword-detector.ts and add tests.", }, ], }); await writeCanonicalLeaderFixture( stateDir, "sess-di-edit-block", "thread-di-edit-block", cwd, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-edit-block", thread_id: "thread-di-edit-block", agent_id: "thread-di-edit-block", tool_name: "Edit", tool_use_id: "tool-di-edit-block", tool_input: { file_path: "src/hooks/keyword-detector.ts", old_string: "a", new_string: "b", }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( String((result.outputJson as { reason?: string } | null)?.reason ?? ""), /Deep-interview is active/, ); assert.match( JSON.stringify(result.outputJson), /requirements\/spec mode/, ); assert.match(JSON.stringify(result.outputJson), /\$ralplan/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not self-lock PreToolUse after completed deep-interview handoff leaves Autopilot in deep-interview phase", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-pretool-deep-interview-completed-autopilot-", ), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-di-completed-autopilot"; const threadId = "thread-di-completed-autopilot"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "deep-interview", initialized_mode: "autopilot", initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`, session_id: sessionId, thread_id: threadId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, thread_id: threadId, }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "completed", session_id: sessionId, thread_id: threadId, deep_interview_gate: { status: "complete", handoff_summary: "Requirements were clarified before the Autopilot handoff.", }, }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, thread_id: threadId, }); const implementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "Edit", tool_use_id: "tool-di-completed-autopilot-edit", tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b", }, }, { cwd }, ); assert.equal(implementationEdit.omxEventName, "pre-tool-use"); assert.equal(implementationEdit.outputJson, null); const stateRepair = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-completed-autopilot-state-repair", tool_input: { mode: "autopilot", current_phase: "ralplan", active: true, }, }, { cwd }, ); assert.equal(stateRepair.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3239 permits command-specific safe operations during Autopilot deep-interview without relaxing mutation guards", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-issue-3239-deep-interview-")); const originalQuestionReturnPane = process.env.OMX_QUESTION_RETURN_PANE; const originalTmuxPane = process.env.TMUX_PANE; try { const sessionId = "sess-issue-3239"; const threadId = "thread-issue-3239"; const childThreadId = "thread-issue-3239-child"; await writeIssue3239ActiveAutopilotDeepInterviewState(cwd, sessionId, threadId); await writeJson(join(cwd, ".omx", "state", "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", parent_thread_id: threadId }, }, }, }, }); await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(cwd, "src", "runtime.ts"), "export const runtime = true;\n"); process.env.TMUX_PANE = "%42"; delete process.env.OMX_QUESTION_RETURN_PANE; const dispatch = (toolName: string, toolInput: Record, toolUseId: string) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: threadId, tool_name: toolName, tool_use_id: toolUseId, tool_input: toolInput, }, { cwd }); const bash = (command: string, label: string) => dispatch("Bash", { command }, `tool-issue-3239-${label}`); const assertAllowed = async (label: string, result: Awaited>) => { assert.equal(result.omxEventName, "pre-tool-use", label); assert.equal(result.outputJson, null, label); }; const assertDenied = async (label: string, result: Awaited>, pattern?: RegExp) => { assert.equal(result.omxEventName, "pre-tool-use", label); assert.equal(result.outputJson?.decision, "block", label); if (pattern) assert.match(JSON.stringify(result.outputJson), pattern, label); }; await assertAllowed("valid attached-tmux omx question bridge", await bash( `OMX_QUESTION_RETURN_PANE=%42 omx question --input '{"question":"Question?","type":"single-answerable","options":[{"label":"A","value":"a"}],"allow_other":false,"source":"deep-interview"}' --json`, "omx-question-bridged", )); for (const [label, command] of [ ["bridged question chained write", `OMX_QUESTION_RETURN_PANE=%42 omx question --input '{"question":"Question?","options":["A"],"allow_other":false}' --json && printf x > src/generated.ts`], ["bridged question redirected", `OMX_QUESTION_RETURN_PANE=%42 omx question --input '{"question":"Question?","options":["A"],"allow_other":false}' --json > .omx/context/question.json`], ["bridged question command substitution", `OMX_QUESTION_RETURN_PANE=%42 omx question --input "$(cat .omx/state/session.json)" --json`], ["bridged question process substitution", `OMX_QUESTION_RETURN_PANE=%42 omx question --input <(printf '{}') --json`], ["bridged question script execution", `OMX_QUESTION_RETURN_PANE=%42 bash -c 'omx question --input "{}" --json'`], ["bridged question state mutation", `OMX_QUESTION_RETURN_PANE=%42 omx question --input '{"question":"Question?","options":["A"],"allow_other":false}' --json; omx state write --input '{"mode":"deep-interview","active":false}' --json`], ] as const) { await assertDenied(label, await bash(command, label), /omx question|Deep-interview is active|src\/generated\.ts|write intent|state/); } await assertDenied("missing omx question bridge keeps bridge-specific denial", await bash( `omx question --input '{"question":"Question?","options":["A"],"allow_other":false}' --json`, "omx-question-missing-bridge", ), /OMX_QUESTION_RETURN_PANE=\$TMUX_PANE/); await assertDenied("invalid inherited bridge keeps bridge-specific denial", await (async () => { process.env.OMX_QUESTION_RETURN_PANE = "not-a-pane"; try { return await bash(`omx question --input '{"question":"Question?","options":["A"],"allow_other":false}' --json`, "omx-question-invalid-bridge"); } finally { delete process.env.OMX_QUESTION_RETURN_PANE; } })(), /OMX_QUESTION_RETURN_PANE=\$TMUX_PANE/); await assertDenied("malformed inline bridge overrides inherited valid bridge", await (async () => { process.env.OMX_QUESTION_RETURN_PANE = "%42"; try { return await bash(`OMX_QUESTION_RETURN_PANE=not-a-pane omx question --input '{"question":"Question?","options":["A"],"allow_other":false}' --json`, "omx-question-inline-invalid-overrides-inherited"); } finally { delete process.env.OMX_QUESTION_RETURN_PANE; } })(), /OMX_QUESTION_RETURN_PANE=\$TMUX_PANE/); // Issue #3293 IR2 makes bare cancellation hook-owned only for active // Autopilot deep-interview; the external OMX command is not executed. // Issue #3280: inherited NODE_EXTRA_CA_CERTS must not poison that recovery path, // and must not bypass the force-cancel denial for Deep Interview. const directCancelResults = await withCleanRunnerNodeEnvironment(async () => { const workspacePackageCli = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const trustedBinDir = await mkdtemp(join(tmpdir(), "omx-di-trusted-bin-")); await symlink(workspacePackageCli, join(trustedBinDir, "omx")); const inheritedPath = process.env.PATH; const previousCaCerts = process.env.NODE_EXTRA_CA_CERTS; process.env.PATH = `${trustedBinDir}:${dirname(process.execPath)}`; try { const plain = await bash("omx cancel", "omx-cancel"); const plainAutopilotPhase = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json"), "utf8")).current_phase; const plainSkillActive = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json"), "utf8")).active; await writeIssue3239ActiveAutopilotDeepInterviewState(cwd, sessionId, threadId); process.env.NODE_EXTRA_CA_CERTS = "/nonexistent/enterprise-ca.pem"; const caPlain = await bash("omx cancel", "omx-cancel-ca"); await writeIssue3239ActiveAutopilotDeepInterviewState(cwd, sessionId, threadId); const caForce = await bash("omx cancel --force", "omx-cancel-ca-force"); return { plain, caPlain, caForce, plainAutopilotPhase, plainSkillActive }; } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; if (previousCaCerts === undefined) delete process.env.NODE_EXTRA_CA_CERTS; else process.env.NODE_EXTRA_CA_CERTS = previousCaCerts; await rm(trustedBinDir, { recursive: true, force: true }); } }); assert.match(JSON.stringify(directCancelResults.plain.outputJson), /cancelled_exact_session/); assert.equal(directCancelResults.plainAutopilotPhase, "cancelled"); assert.equal(directCancelResults.plainSkillActive, false); assert.match(JSON.stringify(directCancelResults.caPlain.outputJson), /cancelled_exact_session/); await assertDenied("inherited CA does not bypass Deep Interview force cancellation guard", directCancelResults.caForce, /Deep-interview is active|write intent|handoff|direct/); await writeIssue3239ActiveAutopilotDeepInterviewState(cwd, sessionId, threadId); await assertDenied("chained cancellation is not documented direct cancellation", await bash("printf ready && omx cancel", "omx-cancel-chained"), /Deep-interview is active|write intent|handoff|direct/); await assertDenied("force cancellation is ralplan/conductor-only", await bash("omx cancel --force", "omx-cancel-force"), /Deep-interview is active|write intent|handoff|direct/); await assertDenied("bom lookalike is not direct cancellation", await bash("\ufeffomx cancel", "omx-cancel-bom"), /Deep-interview is active|write intent|handoff|direct/); await assertDenied("inherited bash startup poisons direct cancellation", await (async () => { const previousBashEnv = process.env.BASH_ENV; process.env.BASH_ENV = "/tmp/prelude.sh"; try { return await bash("omx cancel", "omx-cancel-bash-env"); } finally { if (previousBashEnv === undefined) delete process.env.BASH_ENV; else process.env.BASH_ENV = previousBashEnv; } })(), /Deep-interview is active|write intent|handoff|direct/); await writeIssue3239ActiveAutopilotDeepInterviewState(cwd, sessionId, threadId); await assertDenied("inherited node coverage output poisons direct cancellation", await (async () => { const previousCoverage = process.env.NODE_V8_COVERAGE; process.env.NODE_V8_COVERAGE = "/tmp/coverage-out"; try { return await bash("omx cancel", "omx-cancel-coverage"); } finally { if (previousCoverage === undefined) delete process.env.NODE_V8_COVERAGE; else process.env.NODE_V8_COVERAGE = previousCoverage; } })(), /Deep-interview is active|write intent|handoff|direct/); await writeIssue3239ActiveAutopilotDeepInterviewState(cwd, sessionId, threadId); { // The read-only allowlist requires the same trusted-package-CLI // execution-context proof the direct-cancel path uses (#3313/#3314 // hardening), so these assertions need `omx` to resolve to this // worktree's own canonical CLI rather than relying on whatever // ambient PATH the test runner happens to inherit. const readOnlyTrustedBinDir = await mkdtemp(join(tmpdir(), "omx-issue-3239-readonly-trusted-bin-")); const workspacePackageCliForReadOnly = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); await symlink(workspacePackageCliForReadOnly, join(readOnlyTrustedBinDir, "omx")); const inheritedReadOnlyPath = process.env.PATH; const unsafeRuntimeEnvNames = [ "NODE_OPTIONS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ] as const; const inheritedReadOnlyRuntimeEnvironment = Object.fromEntries( unsafeRuntimeEnvNames.map((name) => [name, process.env[name]]), ); process.env.PATH = `${readOnlyTrustedBinDir}:${inheritedReadOnlyPath ?? ""}`; for (const name of unsafeRuntimeEnvNames) delete process.env[name]; try { for (const [label, command] of [ ["omx-help", "omx --help"], ["omx-state-read", "omx state read --json"], ["omx-cleanup-dry-run", "omx cleanup --dry-run"], ["gh-issue-list", "gh issue list --repo Yeachan-Heo/oh-my-codex"], ["rtk-version", "rtk --version"], ["omx-help-benign-locale-env", "LANG=C omx --help"], ] as const) { await assertAllowed(label, await bash(command, label)); } } finally { if (inheritedReadOnlyPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedReadOnlyPath; for (const name of unsafeRuntimeEnvNames) { if (inheritedReadOnlyRuntimeEnvironment[name] === undefined) delete process.env[name]; else process.env[name] = inheritedReadOnlyRuntimeEnvironment[name]; } await rm(readOnlyTrustedBinDir, { recursive: true, force: true }); } } for (const [label, command, pattern] of [ ["chained mutation", "omx --help && printf x > src/generated.ts", /src\/generated\.ts|Deep-interview is active/], ["command substitution mutation", "omx --help $(printf x > src/generated.ts)", /src\/generated\.ts|Deep-interview is active/], ["process substitution mutation", "cat <(printf x > src/generated.ts)", /src\/generated\.ts|Deep-interview is active/], ["omx help with node preload", "NODE_OPTIONS='--require ./preload.cjs' omx --help", /NODE_OPTIONS|Deep-interview is active|preload|write intent/], ["omx state read with node import", "NODE_OPTIONS='--import ./preload.mjs' omx state read --json", /NODE_OPTIONS|Deep-interview is active|preload|write intent/], ["rtk version with node preload", "NODE_OPTIONS='--require ./preload.cjs' rtk --version", /NODE_OPTIONS|Deep-interview is active|preload|write intent/], ["omx help with PATH resolution override", "PATH=/attacker/bin omx --help", /PATH|Deep-interview is active|write intent/], ["rtk version with PATH resolution override", "PATH=/attacker/bin rtk --version", /PATH|Deep-interview is active|write intent/], ["omx state read with PATHEXT resolution override", "PATHEXT=.EVIL omx state read --json", /PATHEXT|Deep-interview is active|write intent/], ["omx help with dynamic env assignment", "OMX_QUESTION_RETURN_PANE=$(printf %42) omx --help", /Deep-interview is active|write intent|OMX_QUESTION_RETURN_PANE/], ["gh web launcher", "gh pr view 3240 --repo Yeachan-Heo/oh-my-codex --web", /gh|Deep-interview is active|write intent/], ["gh run watch polling", "gh run watch 123 --repo Yeachan-Heo/oh-my-codex", /gh|Deep-interview is active|write intent/], ["gh alias execution surface", "gh alias list", /gh|Deep-interview is active|write intent/], ["gh mutation variant", "gh pr checkout 3240 --repo Yeachan-Heo/oh-my-codex", /gh|Deep-interview is active|write intent/], ["gh dynamic repo value", "gh pr view 3240 --repo $(printf Yeachan-Heo/oh-my-codex)", /gh|Deep-interview is active|write intent/], ["gh output redirection", "gh pr view 3240 --repo Yeachan-Heo/oh-my-codex > .omx/context/pr.json", /gh|Deep-interview is active|write intent|.omx\/context/], ["wrapper smuggling", `env node --require ./preload.js dist/cli/omx.js state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: "sess-issue-3239", workingDirectory: cwd })}' --json`, /runtime wrapper|Deep-interview is active|write intent/], ["generated tmp script", "cat > .omx/tmp/run.sh <<'EOF'\necho unsafe\nEOF\nbash .omx/tmp/run.sh", /.omx\/tmp|generated-script|Deep-interview is active/], ] as const) { await assertDenied(label, await bash(command, label), pattern); } await assertDenied("native-child mutation", await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_id: childThreadId, tool_name: "Bash", tool_use_id: "tool-issue-3239-native-child", tool_input: { command: "printf x > .omx/context/native-child.md" }, }, { cwd }), /OWNER_CONFIRMATION_REQUIRED|native child/); await assertDenied("protected state raw write", await dispatch("Write", { file_path: ".omx/state/session.json", content: "{}\n" }, "tool-issue-3239-protected-state"), /Protected workflow state|Deep-interview is active/); await assertDenied("implementation write", await dispatch("Edit", { file_path: "src/runtime.ts", old_string: "true", new_string: "false" }, "tool-issue-3239-implementation-write"), /Deep-interview is active .*implementation\/write tools are blocked/i); await assertDenied("unknown tool", await dispatch("mcp__unknown__mutate", { path: ".omx/context/unknown.md" }, "tool-issue-3239-unknown-tool"), /not a recognized read-only or explicitly authorized deep-interview mutation transport/); } finally { if (originalQuestionReturnPane === undefined) delete process.env.OMX_QUESTION_RETURN_PANE; else process.env.OMX_QUESTION_RETURN_PANE = originalQuestionReturnPane; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; await rm(cwd, { recursive: true, force: true }); } }); it("allows only the canonical leader's authenticated standalone deep-interview complete terminal state write", async () => withCleanAmbientNodeRuntimeEnvironment(async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-terminal-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-di-terminal-write"; const threadId = "thread-di-terminal-write"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, leader_thread_id: threadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" } }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: threadId, active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, thread_id: threadId, }], }); const activeWrite = await executeStateOperation("state_write", { mode: "deep-interview", active: true, current_phase: "intent-first", session_id: sessionId, thread_id: threadId, workingDirectory: cwd, }); assert.notEqual(activeWrite.isError, true); const persistedActiveState = JSON.parse( await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"), ) as { mode?: string; session_id?: string }; assert.equal(persistedActiveState.mode, undefined); assert.equal(persistedActiveState.session_id, undefined); const preToolUse = (command: string) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: threadId, tool_name: "Bash", tool_input: { command }, }, { cwd }); const validPayload = JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, workingDirectory: cwd, state: { spec_path: ".omx/interviews/final.md" }, }); const validCommand = `omx state write --input '${validPayload}' --json`; assert.equal((await preToolUse(validCommand)).outputJson, null); const terminalInputFile = join(cwd, "terminal-input.json"); await writeFile(terminalInputFile, validPayload); const foreignInputFile = join(cwd, "foreign-input.json"); await writeFile(foreignInputFile, JSON.stringify({ mode: "team", active: false, current_phase: "complete", session_id: sessionId, })); const activePayload = JSON.stringify({ mode: "deep-interview", active: true, current_phase: "intent-first", session_id: sessionId, }); for (const command of [ `env bun --preload ./preload.ts dist/cli/omx.js state write --input '${activePayload}' --json`, `command tsx --tsconfig tsconfig.json dist/cli/omx.js state write --input '${activePayload}' --json`, `time nodejs --require ./preload.js dist/cli/omx.js state write --input '${activePayload}' --json`, ]) { assert.equal((await preToolUse(command)).outputJson?.decision, "block", command); } const rejectedCommands = [ ["wrong mode", `omx state write --input '${JSON.stringify({ mode: "ralplan", active: false, current_phase: "complete", session_id: sessionId })}' --json`], ["wrong session", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: "sess-other" })}' --json`], ["missing session", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete" })}' --json`], ["missing inactive flag", `omx state write --input '${JSON.stringify({ mode: "deep-interview", current_phase: "complete", session_id: sessionId })}' --json`], ["missing complete phase", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, session_id: sessionId })}' --json`], ["cancelled deactivation", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "cancelled", session_id: sessionId })}' --json`], ["cleared deactivation", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "cleared", session_id: sessionId })}' --json`], ["contradictory run outcome", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, run_outcome: "cancelled" })}' --json`], ["contradictory lifecycle outcome", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, lifecycle_outcome: "askuserQuestion" })}' --json`], ["nested mode conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, state: { mode: "ralplan" } })}' --json`], ["nested session conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, state: { session_id: "sess-other" } })}' --json`], ["paired run outcome conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, lifecycle_outcome: "finished", run_outcome: "cancelled" })}' --json`], ["paired terminal outcome conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, lifecycle_outcome: "finished", terminal_outcome: "cancelled" })}' --json`], ["shadowed run outcome conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, run_outcome: "cancelled", state: { run_outcome: "finish" } })}' --json`], ["shadowed lifecycle outcome conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, lifecycle_outcome: "askuserQuestion", state: { lifecycle_outcome: "finished" } })}' --json`], ["shadowed terminal outcome conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, terminal_outcome: "cancelled", state: { terminal_outcome: "finished" } })}' --json`], ["foreign working directory", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, workingDirectory: join(cwd, "other") })}' --json`], ["top-level owner session conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, owner_omx_session_id: "sess-other" })}' --json`], ["top-level codex session conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, codex_session_id: "sess-other" })}' --json`], ["nested owner session conflict", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: false, current_phase: "complete", session_id: sessionId, state: { owner_codex_session_id: "sess-other" } })}' --json`], ["nested active override", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: true, current_phase: "intent-first", session_id: sessionId, state: { active: false, current_phase: "complete" } })}' --json`], ["nested camel phase override", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: true, current_phase: "intent-first", session_id: sessionId, state: { active: false, currentPhase: "complete" } })}' --json`], ["state clear", "omx state clear --mode deep-interview --json"], ["direct input file", `omx state write --input-file ${terminalInputFile} --json`], ["prefix chain", `printf ready && ${validCommand}`], ["suffix chain", `${validCommand} && printf done`], ["pipeline", `${validCommand} | tee terminal.json`], ["command substitution", `printf '%s' \"$(${validCommand})\"`], ["nested shell", `bash -c 'omx state write --input-file ${terminalInputFile} --json'`], ["subshell grouping", `( ${validCommand} )`], ["background execution", `${validCommand} &`], ["wrapper dispatch", `env ${validCommand}`], ["node runtime wrapper", `node --require ./preload.js dist/cli/omx.js state write --input '${validPayload}' --json`], ["bun runtime wrapper", `bun --preload ./preload.ts dist/cli/omx.js state write --input '${validPayload}' --json`], ["tsx runtime wrapper", `tsx --require ./preload.ts dist/cli/omx.js state write --input '${validPayload}' --json`], ["path-qualified omx", `./attacker/omx state write --input '${validPayload}' --json`], ["env bun preload wrapper", `env bun --preload ./preload.ts dist/cli/omx.js state write --input '${validPayload}' --json`], ["command tsx config wrapper", `command tsx --tsconfig tsconfig.json dist/cli/omx.js state write --input '${validPayload}' --json`], ["time node preload wrapper", `time node --require ./preload.js dist/cli/omx.js state write --input '${validPayload}' --json`], ["nodejs runtime wrapper", `nodejs --require ./preload.js dist/cli/omx.js state write --input '${validPayload}' --json`], ["path-qualified nodejs wrapper", `/usr/bin/nodejs --require ./preload.js dist/cli/omx.js state write --input '${validPayload}' --json`], ["nested terminal payload wrapper", `env bun --preload ./preload.ts dist/cli/omx.js state write --input '${JSON.stringify({ mode: "deep-interview", session_id: sessionId, state: { active: false, current_phase: "complete" } })}' --json`], ["foreign completed payload wrapper", `command tsx --tsconfig tsconfig.json dist/cli/omx.js state write --input '${JSON.stringify({ mode: "ralph", active: false, current_phase: "complete", session_id: sessionId })}' --json`], ["nodejs terminal input file wrapper", `nodejs dist/cli/omx.js state write --input-file ${terminalInputFile} --json`], ["path-qualified nodejs foreign input file wrapper", `/usr/bin/nodejs dist/cli/omx.js state write --input-file ${foreignInputFile} --json`], ["split-string nodejs terminal input file wrapper", `env -S "nodejs dist/cli/omx.js state write --input-file ${terminalInputFile} --json"`], ["long split-string nodejs foreign input file wrapper", `env --split-string "nodejs dist/cli/omx.js state write --input-file ${foreignInputFile} --json"`], ["split-string bun terminal wrapper", `env -S "bun --preload ./preload.ts dist/cli/omx.js state write --input '${validPayload}' --json"`], ["split-string tsx foreign wrapper", `env --split-string "tsx --tsconfig tsconfig.json dist/cli/omx.js state write --input '${JSON.stringify({ mode: "team", active: true, current_phase: "running", session_id: sessionId })}' --json"`], ["stdout redirect", `${validCommand} > terminal.json`], ["null redirect", `${validCommand} > /dev/null`], ["stderr redirect", `${validCommand} 2> terminal.err`], ["stdin redirect", `${validCommand} < ${terminalInputFile}`], ["arbitrary state mutation", `omx state write --input '${JSON.stringify({ mode: "team", active: false, current_phase: "complete", session_id: sessionId, state: { arbitrary: true } })}' --json`], ] as const; for (const [name, command] of rejectedCommands) { const result = await preToolUse(command); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", name, ); } for (const conflictingState of [ { active: true, mode: "ralplan", current_phase: "intent-first" }, { active: true, current_phase: "intent-first", session_id: "sess-other" }, ]) { await writeJson(join(sessionDir, "deep-interview-state.json"), conflictingState); assert.equal( ((await preToolUse(validCommand)).outputJson as { decision?: string } | null)?.decision, "block", ); const implementationWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }); assert.equal( (implementationWrite.outputJson as { decision?: string } | null)?.decision, "block", ); } } finally { await rm(cwd, { recursive: true, force: true }); } })); it("issue #3313/#3314 permits standalone deep-interview lifecycle reachability and read-only discovery without relaxing write guards", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-issue-3313-3314-di-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-issue-3313-3314-di"; const threadId = "thread-issue-3313-3314-di"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, leader_thread_id: threadId }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" } }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: threadId, active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, thread_id: threadId, }], }); const activeWrite = await executeStateOperation("state_write", { mode: "deep-interview", active: true, current_phase: "intent-first", session_id: sessionId, thread_id: threadId, workingDirectory: cwd, }); assert.notEqual(activeWrite.isError, true); await writeFile(join(cwd, "README.md"), "foo bar\n"); // Payload-realistic regression for #3314's reported live-runtime // discrepancy: exercise the actual PreToolUse dispatch path against a // process-wide trusted PATH (not an inline `PATH=... omx` assignment, // and not a manually short-circuited fixture) so the omx CLI resolves // exactly the way a live Codex session resolves it. const workspacePackageCli = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const trustedBinDir = await mkdtemp(join(tmpdir(), "omx-issue-3313-3314-trusted-bin-")); await symlink(workspacePackageCli, join(trustedBinDir, "omx")); const inheritedPath = process.env.PATH; process.env.PATH = `${trustedBinDir}:${dirname(process.execPath)}:/usr/bin:/bin`; const unsafeRuntimeEnvironmentNames = [ "NODE_OPTIONS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ] as const; const inheritedUnsafeRuntimeEnvironment = Object.fromEntries( unsafeRuntimeEnvironmentNames.map((name) => [name, process.env[name]]), ); for (const name of unsafeRuntimeEnvironmentNames) delete process.env[name]; const sparkshellImplementationEnvironmentNames = [ "OMX_SPARKSHELL_BIN", "OMX_NATIVE_CACHE_DIR", "OMX_NATIVE_MANIFEST_URL", "OMX_NATIVE_RELEASE_BASE_URL", "OMX_NATIVE_AUTO_FETCH", "XDG_CACHE_HOME", "LOCALAPPDATA", ] as const; const inheritedSparkshellImplementationEnvironment = Object.fromEntries( sparkshellImplementationEnvironmentNames.map((name) => [name, process.env[name]]), ); for (const name of sparkshellImplementationEnvironmentNames) delete process.env[name]; const preToolUse = (command: string) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: threadId, tool_name: "Bash", tool_use_id: `tool-issue-3313-3314-di-${Math.random()}`, tool_input: { command }, }, { cwd }); const assertAllowed = async (label: string, command: string) => { const result = await preToolUse(command); assert.equal(result.outputJson, null, `${label}: ${JSON.stringify(result.outputJson)}`); }; const assertBlocked = async (label: string, command: string, pattern?: RegExp) => { const result = await preToolUse(command); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", label); if (pattern) assert.match(JSON.stringify(result.outputJson), pattern, label); }; try { // #3314: plain, non-omx-wrapped read-only discovery must already stay allowed. // Use coreutils/git only (guaranteed present and root-owned on every CI // runner); ripgrep is not part of the standard test image, so asserting // on it here would make this hermetic classifier test depend on ambient // package installation rather than on the fix under test. await assertAllowed("plain git status", "git status --short --branch"); await assertAllowed("plain find", "find . -maxdepth 3 -type d"); // Direct plain `rg` must also stay read-only when it is genuinely // available on this host's real PATH (a user-managed external // executable, not one this repo controls). ripgrep is not part of // the base OS image or this lane's installed toolset, so this check // is best-effort/skip-when-absent rather than hermetic -- it proves // the classifier's generic non-omx/gjc read-only path for a real, // externally-resolved `rg` wherever one happens to exist, without // making the suite depend on ripgrep being installed everywhere. { const { execFileSync } = await import("node:child_process"); let realRgPath: string | null = null; try { realRgPath = execFileSync("/bin/sh", ["-c", "command -v rg"], { encoding: "utf-8" }).trim() || null; } catch { realRgPath = null; } if (realRgPath) { await assertAllowed("plain rg (real, externally-resolved binary)", `rg -n -i "foo" README.md`); } } // #3313/#3314: omx/gjc help/version/status/read must not be misclassified as writes. await assertAllowed("omx --help", "omx --help"); await assertAllowed("omx ralplan --help", "omx ralplan --help"); await assertAllowed("omx state read (bare)", "omx state read --json"); await assertAllowed("omx state read --mode deep-interview --json", "omx state read --mode deep-interview --json"); await assertAllowed("omx state get-status --mode=deep-interview --json", "omx state get-status --mode=deep-interview --json"); await assertAllowed("omx state read nested help", "omx state read --help"); await assertAllowed("omx auth nested help", "omx auth --help"); await assertBlocked("invalid state status spelling stays blocked", "omx state status --mode=deep-interview --json"); // #3314: sparkshell-wrapped read-only discovery must not be misclassified as a write. await assertAllowed("sparkshell wrapped git status", "omx sparkshell -- git status --short --branch"); await assertAllowed("sparkshell nested help", "omx sparkshell --help"); await assertAllowed("sparkshell wrapped find", "omx sparkshell -- find . -maxdepth 1 -type f"); await assertAllowed("sparkshell json-flagged wrapped git status", "omx sparkshell --json -- git status --short --branch"); // #3313: deep-interview's own structured lifecycle stays reachable. await assertAllowed("omx cancel", "omx cancel"); // The read-only allowlist authorizes the *outer* omx/gjc invocation, not // just the wrapped argv shape, so it must require the same trusted-package-CLI // execution-context proof the direct-cancel path already uses. An impostor // binary (PATH-prefix override or path-qualified) must never reach the // allow-return for --help, state read, or sparkshell, regardless of what its // wrapped argv looks like. { const impostorBinDir = await mkdtemp(join(tmpdir(), "omx-impostor-bin-")); await writeFile(join(impostorBinDir, "omx"), "#!/bin/sh\necho pwned\n"); await chmod(join(impostorBinDir, "omx"), 0o755); try { await assertBlocked("PATH-prefix impostor omx --help stays blocked", `PATH="${impostorBinDir}" omx --help`); await assertBlocked( "PATH-prefix impostor omx state read stays blocked", `PATH="${impostorBinDir}" omx state read --mode deep-interview --json`, ); await assertBlocked( "PATH-prefix impostor omx sparkshell stays blocked", `PATH="${impostorBinDir}" omx sparkshell -- git status --short --branch`, ); await assertBlocked( "path-qualified impostor omx sparkshell stays blocked", `${join(impostorBinDir, "omx")} sparkshell -- git status --short --branch`, ); } finally { await rm(impostorBinDir, { recursive: true, force: true }); } } // A relative-path impostor `./omx` resolves via the current directory, // never via PATH, so it must stay blocked even when a legitimate // trusted `omx` also exists on PATH -- basename-only trust would be // fooled by the unrelated trusted PATH entry. { await writeFile(join(cwd, "omx"), "#!/bin/sh\necho pwned\n"); await chmod(join(cwd, "omx"), 0o755); try { await assertBlocked( "relative-path impostor ./omx sparkshell stays blocked with a legitimate trusted omx also on PATH", "./omx sparkshell -- git status --short --branch", ); } finally { await rm(join(cwd, "omx"), { force: true }); } } // An inherited BASH_FUNC_omx%% shell-function shadow must stay blocked // even against a trusted PATH resolution for the real binary. { const previousBashFuncOmx = process.env["BASH_FUNC_omx%%"]; process.env["BASH_FUNC_omx%%"] = "() { echo shadowed; }"; try { await assertBlocked("inherited BASH_FUNC_omx%% shadow stays blocked", "omx --help"); } finally { if (previousBashFuncOmx === undefined) delete process.env["BASH_FUNC_omx%%"]; else process.env["BASH_FUNC_omx%%"] = previousBashFuncOmx; } } // gjc is a first-class alias of the same canonical package CLI; a trusted // gjc shim must get the same read-only discovery allowance omx does. { const gjcTrustedBinDir = await mkdtemp(join(tmpdir(), "omx-gjc-trusted-bin-")); await symlink(workspacePackageCli, join(gjcTrustedBinDir, "gjc")); const previousGjcPath = process.env.PATH; process.env.PATH = `${gjcTrustedBinDir}:${dirname(process.execPath)}:/usr/bin:/bin`; try { await assertAllowed("trusted gjc --help", "gjc --help"); await assertAllowed("trusted gjc sparkshell wrapped git status", "gjc sparkshell -- git status --short --branch"); } finally { if (previousGjcPath === undefined) delete process.env.PATH; else process.env.PATH = previousGjcPath; await rm(gjcTrustedBinDir, { recursive: true, force: true }); } } // Lexical-boundary mismatches between our tokenizer/analysis and the // actual shell that will execute the command must not let a // differently-named executable borrow this trust: a wrapper // (env/command/time) around an impostor, a non-ASCII whitespace // character embedded mid-command, and a leading BOM stripped by // ECMAScript trim() must all still resolve to denial. { const lexicalAttackerDir = await mkdtemp(join(tmpdir(), "omx-lexical-attacker-")); await writeFile(join(lexicalAttackerDir, "omx"), "#!/bin/sh\necho pwned\n"); await chmod(join(lexicalAttackerDir, "omx"), 0o755); // A file literally named "omx--help" (single word to the real // shell, since Bash does not treat U+00A0 as a blank). await writeFile(join(lexicalAttackerDir, "omx\u00A0--help"), "#!/bin/sh\necho pwned\n"); await chmod(join(lexicalAttackerDir, "omx\u00A0--help"), 0o755); // A file literally named "\uFEFFomx" (leading BOM preserved by Bash). await writeFile(join(lexicalAttackerDir, "\uFEFFomx"), "#!/bin/sh\necho pwned\n"); await chmod(join(lexicalAttackerDir, "\uFEFFomx"), 0o755); // Files literally named "omx--help" / "omx--help" (single // words to the real shell -- Bash does not treat U+000B/U+000C as // blanks either, only ASCII space/tab/newline). await writeFile(join(lexicalAttackerDir, "omx\v--help"), "#!/bin/sh\necho pwned\n"); await chmod(join(lexicalAttackerDir, "omx\v--help"), 0o755); await writeFile(join(lexicalAttackerDir, "omx\f--help"), "#!/bin/sh\necho pwned\n"); await chmod(join(lexicalAttackerDir, "omx\f--help"), 0o755); const previousLexicalPath = process.env.PATH; // Attacker directory first, then the legitimate trusted CLI later on PATH. process.env.PATH = `${lexicalAttackerDir}:${trustedBinDir}:${dirname(process.execPath)}:/usr/bin:/bin`; try { await assertBlocked("env-wrapped omx --help stays blocked", "env omx --help"); await assertBlocked("command-wrapped omx --help stays blocked", "command omx --help"); await assertBlocked("time-wrapped omx --help stays blocked", "time omx --help"); await assertBlocked( "embedded NBSP between omx and --help stays blocked", "omx\u00A0--help", ); await assertBlocked( "leading BOM before omx --help stays blocked", "\uFEFFomx --help", ); await assertBlocked( "embedded vertical tab between omx and --help stays blocked", "omx\v--help", ); await assertBlocked( "embedded form feed between omx and --help stays blocked", "omx\f--help", ); } finally { if (previousLexicalPath === undefined) delete process.env.PATH; else process.env.PATH = previousLexicalPath; await rm(lexicalAttackerDir, { recursive: true, force: true }); } } // Guards that must NOT relax: unsafe sparkshell modes, mutation-shaped // wrapped argv, raw redirects into own session state, active-state // overrides, and PATH-prefix smuggling on `omx cancel`. Nested help is // allowed only in parser positions that short-circuit before side effects; // trailing help on mutators must remain blocked. A `--help` meant for a // sparkshell-wrapped script must not short-circuit scrutiny of that // wrapped script, and every inherited or leading sidecar/cache identity // override must deny the sparkshell allowance entirely. await assertBlocked("sparkshell --shell mode stays scrutinized", "omx sparkshell --shell 'git status --short --branch'"); await assertBlocked("sparkshell tmux-pane mode stays scrutinized", "omx sparkshell --tmux-pane %42"); await assertBlocked("sparkshell trailing help does not short-circuit shell mode", "omx sparkshell --shell 'git status --short --branch' --help"); await assertBlocked("auth trailing help does not short-circuit slot mutation", "omx auth use slot --help"); await assertBlocked( "workflow mutator trailing help does not short-circuit execution", "omx performance-goal create --objective x --evaluator-command true --evaluator-contract x --help", ); await assertBlocked("sparkshell wrapped write stays blocked", `omx sparkshell -- bash -c 'echo x > src/generated.ts'`); await assertBlocked("omx cleanup --version stays blocked (not a bare top-level probe)", "omx cleanup --version"); await assertBlocked("omx cleanup -v stays blocked (not a bare top-level probe)", "omx cleanup -v"); await assertBlocked( "sparkshell-wrapped --help meant for the wrapped script stays scrutinized", "omx sparkshell -- ./mutate.sh --help", ); for (const [name, value] of [ ["OMX_SPARKSHELL_BIN", "/tmp/attacker-sidecar"], ["OMX_NATIVE_CACHE_DIR", "/tmp/attacker-native-cache"], ["OMX_NATIVE_MANIFEST_URL", "https://attacker.invalid/manifest.json"], ["OMX_NATIVE_RELEASE_BASE_URL", "https://attacker.invalid/releases"], ["OMX_NATIVE_AUTO_FETCH", "0"], ["XDG_CACHE_HOME", "/tmp/attacker-xdg-cache"], ["LOCALAPPDATA", "/tmp/attacker-localappdata"], ] as const) { const previous = process.env[name]; process.env[name] = value; try { await assertBlocked(`inherited ${name} override denies the sparkshell allowance`, "omx sparkshell -- git status --short --branch"); } finally { if (previous === undefined) delete process.env[name]; else process.env[name] = previous; } await assertBlocked(`leading ${name} override denies the sparkshell allowance`, `${name}=${JSON.stringify(value)} omx sparkshell -- git status --short --branch`); await assertBlocked(`env-wrapper ${name} override denies the sparkshell allowance`, `env ${name}=${JSON.stringify(value)} omx sparkshell -- git status --short --branch`); } await assertBlocked( "raw redirect into own session state", `echo '{}' > ${join(sessionDir, "deep-interview-state.json")}`, /is not under allowed deep-interview artifact/, ); await assertBlocked( "active-state override via omx state write stays blocked", `omx state write --input '${JSON.stringify({ mode: "deep-interview", active: true, current_phase: "planning", session_id: sessionId, workingDirectory: cwd })}' --json`, ); await assertBlocked("PATH-prefix smuggled omx cancel stays blocked", `PATH="${trustedBinDir}" omx cancel`, /PATH|write intent/); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; for (const name of unsafeRuntimeEnvironmentNames) { if (inheritedUnsafeRuntimeEnvironment[name] === undefined) delete process.env[name]; else process.env[name] = inheritedUnsafeRuntimeEnvironment[name]; } for (const name of sparkshellImplementationEnvironmentNames) { const previous = (inheritedSparkshellImplementationEnvironment as Record)[name]; if (previous === undefined) delete process.env[name]; else process.env[name] = previous; } await rm(trustedBinDir, { recursive: true, force: true }); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3314 permits ralplan planning read-only discovery without relaxing write guards", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-issue-3314-ralplan-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-issue-3314-ralplan"; const threadId = "thread-issue-3314-ralplan"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, leader_thread_id: threadId }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" } }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(sessionDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); await writeFile(join(cwd, "README.md"), "foo bar\n"); const workspacePackageCli = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const trustedBinDir = await mkdtemp(join(tmpdir(), "omx-issue-3314-ralplan-trusted-bin-")); await symlink(workspacePackageCli, join(trustedBinDir, "omx")); const inheritedPath = process.env.PATH; process.env.PATH = `${trustedBinDir}:${dirname(process.execPath)}:/usr/bin:/bin`; const unsafeRuntimeEnvironmentNames = [ "NODE_OPTIONS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ] as const; const inheritedUnsafeRuntimeEnvironment = Object.fromEntries( unsafeRuntimeEnvironmentNames.map((name) => [name, process.env[name]]), ); for (const name of unsafeRuntimeEnvironmentNames) delete process.env[name]; const sparkshellImplementationEnvironmentNames = [ "OMX_SPARKSHELL_BIN", "OMX_NATIVE_CACHE_DIR", "OMX_NATIVE_MANIFEST_URL", "OMX_NATIVE_RELEASE_BASE_URL", "OMX_NATIVE_AUTO_FETCH", "XDG_CACHE_HOME", "LOCALAPPDATA", ] as const; const inheritedSparkshellImplementationEnvironment = Object.fromEntries( sparkshellImplementationEnvironmentNames.map((name) => [name, process.env[name]]), ); for (const name of sparkshellImplementationEnvironmentNames) delete process.env[name]; const preToolUse = (command: string) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: threadId, tool_name: "Bash", tool_use_id: `tool-issue-3314-ralplan-${Math.random()}`, tool_input: { command }, }, { cwd }); const assertAllowed = async (label: string, command: string) => { const result = await preToolUse(command); assert.equal(result.outputJson, null, `${label}: ${JSON.stringify(result.outputJson)}`); }; const assertBlocked = async (label: string, command: string) => { const result = await preToolUse(command); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", label); }; try { await assertAllowed("plain git status", "git status --short --branch"); await assertAllowed("plain find", "find . -maxdepth 3 -type d"); await assertAllowed("omx --help", "omx --help"); await assertAllowed("omx ralplan --help", "omx ralplan --help"); await assertAllowed("omx state read --mode ralplan --json", "omx state read --mode ralplan --json"); await assertAllowed("omx state get-status --mode ralplan --json", "omx state get-status --mode ralplan --json"); await assertBlocked("invalid state status spelling stays blocked", "omx state status --mode ralplan --json"); await assertAllowed("sparkshell wrapped git status", "omx sparkshell -- git status --short --branch"); { const impostorBinDir = await mkdtemp(join(tmpdir(), "omx-impostor-bin-ralplan-")); await writeFile(join(impostorBinDir, "omx"), "#!/bin/sh\necho pwned\n"); await chmod(join(impostorBinDir, "omx"), 0o755); try { await assertBlocked("PATH-prefix impostor omx --help stays blocked", `PATH="${impostorBinDir}" omx --help`); await assertBlocked( "PATH-prefix impostor omx sparkshell stays blocked", `PATH="${impostorBinDir}" omx sparkshell -- git status --short --branch`, ); } finally { await rm(impostorBinDir, { recursive: true, force: true }); } } { const gjcTrustedBinDir = await mkdtemp(join(tmpdir(), "omx-gjc-trusted-bin-ralplan-")); await symlink(workspacePackageCli, join(gjcTrustedBinDir, "gjc")); const previousGjcPath = process.env.PATH; process.env.PATH = `${gjcTrustedBinDir}:${dirname(process.execPath)}:/usr/bin:/bin`; try { await assertAllowed("trusted gjc --help", "gjc --help"); } finally { if (previousGjcPath === undefined) delete process.env.PATH; else process.env.PATH = previousGjcPath; await rm(gjcTrustedBinDir, { recursive: true, force: true }); } } await assertBlocked("sparkshell --shell mode stays scrutinized", "omx sparkshell --shell 'git status --short --branch'"); await assertBlocked("sparkshell tmux-pane mode stays scrutinized", "omx sparkshell --tmux-pane %42"); await assertBlocked("sparkshell trailing help does not short-circuit shell mode", "omx sparkshell --shell 'git status --short --branch' --help"); await assertBlocked("auth trailing help does not short-circuit slot mutation", "omx auth use slot --help"); await assertBlocked( "workflow mutator trailing help does not short-circuit execution", "omx performance-goal create --objective x --evaluator-command true --evaluator-contract x --help", ); await assertBlocked("sparkshell wrapped write stays blocked", `omx sparkshell -- bash -c 'echo x > src/generated.ts'`); await assertBlocked("omx cleanup --version stays blocked (not a bare top-level probe)", "omx cleanup --version"); await assertBlocked( "sparkshell-wrapped --help meant for the wrapped script stays scrutinized", "omx sparkshell -- ./mutate.sh --help", ); for (const [name, value] of [ ["OMX_SPARKSHELL_BIN", "/tmp/attacker-sidecar"], ["OMX_NATIVE_CACHE_DIR", "/tmp/attacker-native-cache"], ["OMX_NATIVE_MANIFEST_URL", "https://attacker.invalid/manifest.json"], ["OMX_NATIVE_RELEASE_BASE_URL", "https://attacker.invalid/releases"], ["OMX_NATIVE_AUTO_FETCH", "0"], ["XDG_CACHE_HOME", "/tmp/attacker-xdg-cache"], ["LOCALAPPDATA", "/tmp/attacker-localappdata"], ] as const) { const previous = process.env[name]; process.env[name] = value; try { await assertBlocked(`inherited ${name} override denies the sparkshell allowance`, "omx sparkshell -- git status --short --branch"); } finally { if (previous === undefined) delete process.env[name]; else process.env[name] = previous; } await assertBlocked(`leading ${name} override denies the sparkshell allowance`, `${name}=${JSON.stringify(value)} omx sparkshell -- git status --short --branch`); } await assertBlocked( "raw redirect into own session state", `echo '{}' > ${join(sessionDir, "ralplan-state.json")}`, ); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; for (const name of unsafeRuntimeEnvironmentNames) { if (inheritedUnsafeRuntimeEnvironment[name] === undefined) delete process.env[name]; else process.env[name] = inheritedUnsafeRuntimeEnvironment[name]; } for (const name of sparkshellImplementationEnvironmentNames) { const previous = (inheritedSparkshellImplementationEnvironment as Record)[name]; if (previous === undefined) delete process.env[name]; else process.env[name] = previous; } await rm(trustedBinDir, { recursive: true, force: true }); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not protect matching state basenames outside the canonical state root", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-product-session-json-")); try { await mkdir(join(cwd, "src"), { recursive: true }); const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-product-session-json", tool_name: "Write", tool_use_id: "tool-product-session-json", tool_input: { file_path: "src/session.json", content: "{}\n" }, }, { cwd }); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks filesystem MCP writes to protected workflow state without an active mode", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-filesystem-protected-state-")); try { for (const path of [".omx/state/session.json", ".omx/state/subagent-tracking.json"] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-filesystem-protected-state", tool_name: "mcp__filesystem__write_file", tool_input: { path, content: "{}\n" }, }, { cwd }); assert.equal(result.outputJson?.decision, "block", path); assert.match(String(result.outputJson?.reason ?? ""), /Protected workflow state/); } for (const [source, destination] of [ [".omx/state/session.json", ".omx/state/session.saved"], ["src/session.json", ".omx/state/session.json"], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-filesystem-protected-state", tool_name: "mcp__filesystem__move_file", tool_input: { source, destination }, }, { cwd }); assert.equal(result.outputJson?.decision, "block", `${source} -> ${destination}`); assert.match(String(result.outputJson?.reason ?? ""), /Protected workflow state/); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows canonical leader deep-interview artifact and state writes while blocking implementation Bash writes", async () => withCleanAmbientNodeRuntimeEnvironment(async () => { const cwd = realpathSync(await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-deep-interview-artifact-"), )); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-artifact"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-artifact", cwd, state_root: stateDir, started_at: "2026-01-01T00:00:00.000Z", pid: process.pid, pid_start_ticks: readLinuxStartTicks(process.pid), pid_cmdline: readLinuxCmdline(process.pid), platform: process.platform, leader_thread_id: "thread-di-artifact", native_session_id: "native-di-artifact", owner_omx_session_id: "owner-omx-di-artifact", owner_codex_session_id: "owner-codex-di-artifact", codex_session_id: "codex-di-artifact", }); const threadId = "thread-di-artifact"; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-di-artifact": { session_id: "sess-di-artifact", leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" } }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-artifact", thread_id: threadId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-artifact", thread_id: threadId, }, ], workingDirectory: cwd, }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-artifact", workingDirectory: cwd, thread_id: threadId, rounds: [ { answer: "Use CLI wrapper normalization for protected planning state commands.", }, ], }); const preToolUse = ( payload: Parameters[0], _options?: { cwd?: string }, ) => dispatchCodexNativeHook( { ...payload, hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", thread_id: threadId, agent_id: threadId, }, { cwd }, ); const omxCommand = await withTrustedWorkspaceOmxCli(cwd, async (command) => command); const allowedWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: "tool-di-spec-write", tool_input: { file_path: ".omx/specs/deep-interview-demo.md", content: "# Spec", }, }, { cwd }, ); assert.equal(allowedWrite.outputJson, null); for (const [toolName, toolInput] of [ ["mcp__omx_wiki__wiki_delete", { path: ".omx/specs/deep-interview-demo.md" }], ["mcp__unknown__mutate", { target: ".omx/specs/deep-interview-demo.md" }], ] as const) { const blockedUnknownTransport = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: toolName, tool_use_id: `tool-di-unknown-${toolName}`, tool_input: toolInput, }); assert.equal(blockedUnknownTransport.outputJson?.decision, "block", toolName); assert.match( String(blockedUnknownTransport.outputJson?.reason ?? ""), /not a recognized read-only or explicitly authorized deep-interview mutation transport/, toolName, ); } for (const command of [ 'printf x > "src/new file.ts"', "printf x > 'src/another file.ts'", 'printf x > .omx/context/"../../src/generated.ts"', "printf x > .omx/context/'../../src/generated-single.ts'", 'printf x > ".omx/context/"../../src/generated-quote-first.ts', "printf x > '.omx/context/'../../src/generated-single-quote-first.ts", 'TARGET="../../src/generated-expanded.ts"; printf x > .omx/context/"$TARGET"', 'printf x > .omx/context/"$(printf ../../src/generated-command.ts)"', ]) { const quotedRedirectWrite = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_input: { command }, }); assert.equal(quotedRedirectWrite.outputJson?.decision, "block", command); } await mkdir(join(cwd, ".git"), { recursive: true }); const policySubdir = join(cwd, "nested-policy-cwd"); await mkdir(policySubdir, { recursive: true }); const previousOmxRootForPolicy = process.env.OMX_ROOT; try { process.env.OMX_ROOT = cwd; const noncanonicalPolicyStateWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: policySubdir, session_id: "sess-di-artifact", thread_id: threadId, agent_id: threadId, tool_name: "mcp__omx_state__state_write", tool_input: { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: policySubdir }, }, { cwd: policySubdir }); assert.equal(noncanonicalPolicyStateWrite.outputJson?.decision, "block"); assert.match(String(noncanonicalPolicyStateWrite.outputJson?.reason ?? ""), /canonical session and workingDirectory scope/); } finally { if (previousOmxRootForPolicy === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRootForPolicy; } const blockedRepeatedNodeEval = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_input: { command: `node -e "0" -e "require('fs').writeFileSync('src/owned.ts','x')"` }, }); assert.equal(blockedRepeatedNodeEval.outputJson?.decision, "block"); const allowedAstGrepSearch = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_code_intel__ast_grep_search", tool_input: { pattern: "function $F($$$A) { $$$B }", path: "src" }, }); assert.equal(allowedAstGrepSearch.outputJson, null); const blockedFilesystemMcpWrite = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__filesystem__write_file", tool_use_id: "tool-di-filesystem-write-outside", tool_input: { path: "src/runtime.ts", content: "export {};\n" }, }); assert.equal(blockedFilesystemMcpWrite.outputJson?.decision, "block"); const blockedNativeChildOrchestration = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", thread_id: "agent-di-orchestration-child", agent_id: "agent-di-orchestration-child", tool_name: "collaboration.spawn_agent", tool_input: { agent_type: "executor", message: "mutate product state" }, }, { cwd }); assert.equal(blockedNativeChildOrchestration.outputJson?.decision, "block"); assert.match(String(blockedNativeChildOrchestration.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const boxedRoot = join(cwd, "external-omx-box"); const boxedStateDir = join(boxedRoot, ".omx", "state"); const boxedSessionDir = join(boxedStateDir, "sessions", "sess-di-boxed-policy"); await mkdir(boxedSessionDir, { recursive: true }); await writeJson(join(boxedStateDir, "session.json"), { session_id: "sess-di-boxed-policy", cwd, }); await writeJson(join(boxedSessionDir, "deep-interview-state.json"), { mode: "deep-interview", active: true, current_phase: "intent-first", session_id: "sess-di-boxed-policy", workingDirectory: cwd, }); await writeJson(join(boxedSessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-boxed-policy", workingDirectory: cwd, active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-boxed-policy" }], }); const priorOmxRootForBox = process.env.OMX_ROOT; try { process.env.OMX_ROOT = boxedRoot; const boxedPolicyChildWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-boxed-policy", thread_id: "agent-di-boxed-child", agent_id: "agent-di-boxed-child", tool_name: "Write", tool_input: { file_path: "src/boxed-policy-bypass.ts", content: "export {};\n" }, }, { cwd }); assert.equal(boxedPolicyChildWrite.outputJson?.decision, "block"); assert.match(String(boxedPolicyChildWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (priorOmxRootForBox === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = priorOmxRootForBox; } const disjointExecutionCwd = join(cwd, "disjoint-external-checkout"); await mkdir(join(disjointExecutionCwd, "src"), { recursive: true }); const priorOmxRootForDisjoint = process.env.OMX_ROOT; try { process.env.OMX_ROOT = boxedRoot; const disjointChildWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: disjointExecutionCwd, session_id: "sess-di-boxed-policy", thread_id: "agent-di-disjoint-child", agent_id: "agent-di-disjoint-child", tool_name: "Write", tool_input: { file_path: "src/disjoint-policy-bypass.ts", content: "export {};\n" }, }, { cwd: disjointExecutionCwd }); assert.equal(disjointChildWrite.outputJson?.decision, "block"); assert.match(String(disjointChildWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (priorOmxRootForDisjoint === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = priorOmxRootForDisjoint; } await writeJson(join(boxedStateDir, "session.json"), { session_id: "sess-di-boxed-policy", cwd: join(cwd, "missing-policy-root"), }); const priorOmxRootForInvalidPointer = process.env.OMX_ROOT; try { process.env.OMX_ROOT = boxedRoot; const invalidPointerChildWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: disjointExecutionCwd, session_id: "sess-di-boxed-policy", thread_id: "agent-di-invalid-pointer-child", agent_id: "agent-di-invalid-pointer-child", tool_name: "Write", tool_input: { file_path: "src/invalid-pointer-bypass.ts", content: "export {};\n" }, }, { cwd: disjointExecutionCwd }); assert.equal(invalidPointerChildWrite.outputJson?.decision, "block"); assert.match(String(invalidPointerChildWrite.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); } finally { if (priorOmxRootForInvalidPointer === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = priorOmxRootForInvalidPointer; } await writeJson(join(boxedStateDir, "session.json"), { session_id: "sess-di-boxed-policy", native_session_id: "native-di-boxed-policy", cwd, pid: 2147483647, }); const priorOmxRootForStalePointer = process.env.OMX_ROOT; try { process.env.OMX_ROOT = boxedRoot; const staleMatchingPointerWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: disjointExecutionCwd, session_id: "sess-di-boxed-policy", thread_id: "agent-di-stale-pointer-child", agent_id: "agent-di-stale-pointer-child", tool_name: "Write", tool_input: { file_path: "src/stale-pointer-bypass.ts", content: "export {};\n" }, }, { cwd: disjointExecutionCwd }); assert.equal(staleMatchingPointerWrite.outputJson?.decision, "block"); assert.match(String(staleMatchingPointerWrite.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); } finally { if (priorOmxRootForStalePointer === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = priorOmxRootForStalePointer; } const foreignSharedRoot = join(cwd, "foreign-shared-root"); const foreignSharedStateDir = join(foreignSharedRoot, ".omx", "state"); const payloadSessionId = "sess-di-shared-payload"; const payloadSessionDir = join(foreignSharedStateDir, "sessions", payloadSessionId); await mkdir(payloadSessionDir, { recursive: true }); await writeJson(join(foreignSharedStateDir, "session.json"), { session_id: "sess-di-shared-foreign-root", cwd }); await writeJson(join(payloadSessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: payloadSessionId, workingDirectory: disjointExecutionCwd, active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: payloadSessionId }], }); await writeJson(join(payloadSessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: payloadSessionId, workingDirectory: disjointExecutionCwd, }); const priorOmxRootForForeignShared = process.env.OMX_ROOT; try { process.env.OMX_ROOT = foreignSharedRoot; const foreignSharedChildWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: disjointExecutionCwd, session_id: payloadSessionId, thread_id: "agent-di-shared-payload-child", agent_id: "agent-di-shared-payload-child", tool_name: "Write", tool_input: { file_path: "src/foreign-shared-bypass.ts", content: "export {};\n" }, }, { cwd: disjointExecutionCwd }); assert.equal(foreignSharedChildWrite.outputJson?.decision, "block"); assert.match(String(foreignSharedChildWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED|PROVENANCE_DENIED/); await writeJson(join(payloadSessionDir, "skill-active-state.json"), { version: 1, active: false, skill: "deep-interview", phase: "complete", session_id: payloadSessionId, workingDirectory: disjointExecutionCwd, active_skills: [], }); await writeJson(join(payloadSessionDir, "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "complete", session_id: payloadSessionId, workingDirectory: disjointExecutionCwd, }); const terminalForeignSharedWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: disjointExecutionCwd, session_id: payloadSessionId, tool_name: "Edit", tool_input: { file_path: "src/terminal-allowed.ts" }, }, { cwd: disjointExecutionCwd }); assert.equal(terminalForeignSharedWrite.outputJson, null); } finally { if (priorOmxRootForForeignShared === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = priorOmxRootForForeignShared; } const ancestorBox = join(cwd, "ancestor-box"); const boxedCheckout = join(ancestorBox, "worktree"); const ancestorStateDir = join(ancestorBox, ".omx", "state"); const ancestorSessionDir = join(ancestorStateDir, "sessions", "sess-di-ancestor-box"); await mkdir(boxedCheckout, { recursive: true }); await mkdir(ancestorSessionDir, { recursive: true }); await writeJson(join(ancestorStateDir, "session.json"), { session_id: "sess-di-ancestor-box", cwd: boxedCheckout }); await writeJson(join(ancestorSessionDir, "deep-interview-state.json"), { mode: "deep-interview", active: true, current_phase: "intent-first", session_id: "sess-di-ancestor-box", workingDirectory: boxedCheckout, }); await writeJson(join(ancestorSessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-ancestor-box", workingDirectory: boxedCheckout, active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-ancestor-box" }], }); const priorOmxRootForAncestorBox = process.env.OMX_ROOT; try { process.env.OMX_ROOT = ancestorBox; const ancestorBoxChildWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: boxedCheckout, session_id: "sess-di-ancestor-box", thread_id: "agent-di-ancestor-box-child", agent_id: "agent-di-ancestor-box-child", tool_name: "Write", tool_input: { file_path: "src/ancestor-box-bypass.ts", content: "export {};\n" }, }, { cwd: boxedCheckout }); assert.equal(ancestorBoxChildWrite.outputJson?.decision, "block"); assert.match(String(ancestorBoxChildWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (priorOmxRootForAncestorBox === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = priorOmxRootForAncestorBox; } for (const [name, toolInput] of [ ["missing session", { mode: "deep-interview", active: true, workingDirectory: cwd }], ["missing cwd", { mode: "deep-interview", active: true, session_id: "sess-di-artifact" }], ["foreign session", { mode: "deep-interview", active: true, session_id: "foreign", workingDirectory: cwd }], ["dynamic session", { mode: "deep-interview", active: true, session_id: "$CODEX_THREAD_ID", workingDirectory: cwd }], ["conflicting owner alias", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", owner_codex_session_id: "foreign", workingDirectory: cwd }], ["persisted verified owner alias", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", owner_codex_session_id: "owner-codex-di-artifact", workingDirectory: cwd }], ["foreign cwd", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: join(cwd, "src") }], ["nested foreign session", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { session_id: "foreign" } }], ["nested persisted verified session alias", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { session_id: "native-di-artifact" } }], ["nested foreign owner alias", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { owner_omx_session_id: "foreign" } }], ["nested foreign cwd", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { workingDirectory: join(cwd, "src") } }], ["nested conflicting mode", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { mode: "ralplan" } }], ["depth-two foreign session", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { state: { session_id: "foreign" } } }], ["depth-two foreign cwd", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { state: { workingDirectory: join(cwd, "src") } } }], ["depth-two conflicting mode", { mode: "deep-interview", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, state: { state: { mode: "ralplan" } } }], ] as const) { const invalidPlanningStateWrite = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-di-state-scope-${name}`, tool_input: toolInput, }); assert.equal(invalidPlanningStateWrite.outputJson?.decision, "block", name); assert.match(String(invalidPlanningStateWrite.outputJson?.reason ?? ""), /canonical session and workingDirectory scope/); } for (const [name, alias] of [ ["native", "native-di-artifact"], ["owner OMX", "owner-omx-di-artifact"], ["owner Codex", "owner-codex-di-artifact"], ["Codex", "codex-di-artifact"], ] as const) { const aliasStateWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: alias, thread_id: threadId, agent_id: threadId, tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-di-${name}-alias-state-scope`, tool_input: { mode: "deep-interview", active: true, session_id: alias, workingDirectory: cwd, }, }, { cwd }); assert.equal(aliasStateWrite.outputJson, null, name); } const canonicalOwnershipStateWrite = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-canonical-ownership-state-scope", tool_input: { mode: "deep-interview", active: true, session_id: "sess-di-artifact", owner_omx_session_id: "sess-di-artifact", owner_codex_session_id: "sess-di-artifact", codex_session_id: "sess-di-artifact", workingDirectory: cwd, state: { session_id: "sess-di-artifact", owner_omx_session_id: "sess-di-artifact", owner_codex_session_id: "sess-di-artifact", codex_session_id: "sess-di-artifact", }, }, }); assert.equal(canonicalOwnershipStateWrite.outputJson, null); const blockedReadWriteRedirect = await preToolUse({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-read-write-redirect", tool_input: { command: ": <> src/runtime.ts" }, }); assert.equal(blockedReadWriteRedirect.outputJson?.decision, "block"); assert.match(String(blockedReadWriteRedirect.outputJson?.reason ?? ""), /src\/runtime\.ts/); const previousTeamEnv = { worker: process.env.OMX_TEAM_WORKER, internalWorker: process.env.OMX_TEAM_INTERNAL_WORKER, stateRoot: process.env.OMX_TEAM_STATE_ROOT, leaderCwd: process.env.OMX_TEAM_LEADER_CWD, pane: process.env.TMUX_PANE, }; try { const teamName = "deep-state-guard"; const workerName = "worker-1"; const workerPane = "%77"; const teamRoot = join(stateDir, "team", teamName); await mkdir(join(teamRoot, "workers", workerName), { recursive: true }); await writeJson(join(teamRoot, "workers", workerName, "identity.json"), { name: workerName, team_state_root: stateDir, pane_id: workerPane, working_dir: cwd, }); const teamAuthority = { name: teamName, leader_pane_id: "%1", leader_cwd: cwd, team_state_root: stateDir, workers: [{ name: workerName, pane_id: workerPane, working_dir: cwd, team_state_root: stateDir, }], }; await writeJson(join(teamRoot, "manifest.v2.json"), teamAuthority); await writeJson(join(teamRoot, "config.json"), teamAuthority); process.env.OMX_TEAM_WORKER = `deep-display/${workerName}`; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/${workerName}`; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.OMX_TEAM_LEADER_CWD = cwd; process.env.TMUX_PANE = workerPane; for (const [toolName, toolInput] of [ ["mcp__omx_state__state_clear", { mode: "deep-interview" }], ["mcp__omx_state__state_write", { mode: "deep-interview", active: false, session_id: "sess-di-artifact", workingDirectory: cwd }], ] as const) { const teamStateMutation = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: toolName, tool_use_id: `tool-di-team-state-${toolName}`, tool_input: toolInput, }, { cwd }); assert.equal(teamStateMutation.outputJson?.decision, "block", `${toolName}: ${JSON.stringify(teamStateMutation)}`); assert.match(String(teamStateMutation.outputJson?.reason ?? ""), /Team-worker authority does not permit/); } const allowedTeamProductWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: "tool-di-team-product-write", tool_input: { file_path: "src/runtime.ts", content: "export {};\n" }, }, { cwd }); assert.equal(allowedTeamProductWrite.outputJson, null); await mkdir(join(cwd, "src"), { recursive: true }); const allowedTeamBashProductWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-team-bash-product-write", tool_input: { command: "cd src; printf x > runtime.ts" }, }, { cwd }); assert.equal(allowedTeamBashProductWrite.outputJson, null); const teamProductPushdWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_input: { command: "pushd src; printf x > pushd-runtime.ts; popd" }, }, { cwd }); assert.equal(teamProductPushdWrite.outputJson, null); const spawnedTeamChildWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_input: { file_path: "src/spawned-team-child.ts", content: "export {};\n" }, source: { subagent: { thread_spawn: { parent_thread_id: threadId } } }, }, { cwd }); assert.equal(spawnedTeamChildWrite.outputJson?.decision, "block"); assert.match(String(spawnedTeamChildWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const inaccessibleDir = join(cwd, "inaccessible-cwd"); await mkdir(inaccessibleDir, { recursive: true, mode: 0o000 }); const protectedStateAlias = join(cwd, "src", "team-state-alias.json"); await symlink(join(stateDir, "sessions", "sess-di-artifact", "deep-interview-state.json"), protectedStateAlias, "file"); const protectedStateDirectoryAlias = join(cwd, "state-directory-alias"); await symlink(stateDir, protectedStateDirectoryAlias, "dir"); for (const [name, toolName, toolInput] of [ ["session path", "Write", { file_path: join(stateDir, "sessions", "sess-di-artifact", "deep-interview-state.json"), content: "{}\n" }], ["root state path", "Write", { file_path: join(stateDir, "deep-interview-state.json"), content: "{}\n" }], ["Bash protected state", "Bash", { command: `: > ${JSON.stringify(join(stateDir, "sessions", "sess-di-artifact", "deep-interview-state.json"))}` }], ["canonical state root", "Write", { file_path: stateDir, content: "{}\n" }], ["Team manifest", "Write", { file_path: join(teamRoot, "manifest.v2.json"), content: "{}\n" }], ["Team config", "Write", { file_path: join(teamRoot, "config.json"), content: "{}\n" }], ["worker identity", "Write", { file_path: join(teamRoot, "workers", workerName, "identity.json"), content: "{}\n" }], ["Bash Team manifest", "Bash", { command: `: > ${JSON.stringify(join(teamRoot, "manifest.v2.json"))}` }], ["Bash state root delete", "Bash", { command: `rm -rf -- ${JSON.stringify(stateDir)}` }], ["Bash state CLI", "Bash", { command: `omx state clear --mode deep-interview --json` }], ["unknown Team authority mutation", "mcp__unknown__mutate", { path: join(teamRoot, "manifest.v2.json") }], ["symlinked protected state", "Write", { file_path: protectedStateAlias, content: "{}\n" }], ["symlinked Team manifest", "Write", { file_path: join(protectedStateDirectoryAlias, "team", teamName, "manifest.v2.json"), content: "{}\n" }], ["Bash symlinked Team manifest", "Bash", { command: `: > ${JSON.stringify(join(protectedStateDirectoryAlias, "team", teamName, "manifest.v2.json"))}` }], ["Bash session effective cwd", "Bash", { command: `cd ${JSON.stringify(join(stateDir, "sessions", "sess-di-artifact"))}; printf x > ultragoal-state.json` }], ["failed cd keeps canonical cwd", "Bash", { command: "cd definitely-missing; printf x > .omx/state/session.json" }], ["cd redirect mutates before transition", "Bash", { command: "cd src > .omx/state/session.json" }], ["pushd protected state", "Bash", { command: `pushd ${JSON.stringify(join(stateDir, "sessions", "sess-di-artifact"))}; printf x > ultragoal-state.json` }], ["builtin pushd protected state", "Bash", { command: `builtin pushd ${JSON.stringify(join(stateDir, "sessions", "sess-di-artifact"))}; printf x > ultragoal-state.json` }], ["command pushd protected state", "Bash", { command: `command pushd ${JSON.stringify(join(stateDir, "sessions", "sess-di-artifact"))}; printf x > ultragoal-state.json` }], ["inaccessible cd keeps canonical cwd", "Bash", { command: `cd ${JSON.stringify(inaccessibleDir)}; printf x > .omx/state/session.json` }], ] as const) { const protectedTeamWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: toolName, tool_use_id: `tool-di-team-protected-${name}`, tool_input: toolInput, }, { cwd }); assert.equal(protectedTeamWrite.outputJson?.decision, "block", name); } const canonicalIdentitylessLeader = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "native-di-artifact", tool_name: "Write", tool_use_id: "tool-di-identityless-leader-spoofed-team", tool_input: { file_path: "src/runtime.ts", content: "export {};\n" }, }, { cwd }); assert.equal(canonicalIdentitylessLeader.outputJson?.decision, "block"); assert.match(String(canonicalIdentitylessLeader.outputJson?.reason ?? ""), /Deep-interview is active/); process.env.TMUX_PANE = "%foreign"; const mismatchedPaneWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: "tool-di-team-pane-mismatch", tool_input: { file_path: "src/runtime.ts", content: "export {};\n" }, }, { cwd }); assert.equal(mismatchedPaneWrite.outputJson?.decision, "block"); assert.match(String(mismatchedPaneWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); process.env.TMUX_PANE = workerPane; for (const [name, path, invalidValue, restoreValue] of [ ["config pane mismatch", join(teamRoot, "config.json"), { ...teamAuthority, workers: [{ ...teamAuthority.workers[0], pane_id: "%foreign" }] }, teamAuthority], ["identity root mismatch", join(teamRoot, "workers", workerName, "identity.json"), { name: workerName, pane_id: workerPane, team_state_root: join(cwd, "foreign-state"), working_dir: cwd }, { name: workerName, pane_id: workerPane, team_state_root: stateDir, working_dir: cwd }], ["config worktree mismatch", join(teamRoot, "config.json"), { ...teamAuthority, workers: [{ ...teamAuthority.workers[0], worktree_path: join(cwd, "foreign-worktree") }] }, teamAuthority], ] as const) { await writeJson(path, invalidValue); const mismatchWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: `tool-di-team-authority-mismatch-${name}`, tool_input: { file_path: "src/runtime.ts", content: "export {};\n" }, }, { cwd }); assert.equal(mismatchWrite.outputJson?.decision, "block", name); await writeJson(path, restoreValue); } const detachedWorkerName = "worker-2"; const detachedWorkerPane = "%78"; const detachedWorkerCwd = join(cwd, "worker-checkout"); await mkdir(join(teamRoot, "workers", detachedWorkerName), { recursive: true }); await mkdir(detachedWorkerCwd, { recursive: true }); await writeJson(join(teamRoot, "workers", detachedWorkerName, "identity.json"), { name: detachedWorkerName, pane_id: detachedWorkerPane, team_state_root: stateDir, working_dir: detachedWorkerCwd, }); const detachedAuthority = { ...teamAuthority, workers: [...teamAuthority.workers, { name: detachedWorkerName, pane_id: detachedWorkerPane, team_state_root: stateDir, worktree_path: detachedWorkerCwd, working_dir: detachedWorkerCwd, }], }; await writeJson(join(teamRoot, "config.json"), detachedAuthority); await writeJson(join(teamRoot, "manifest.v2.json"), detachedAuthority); process.env.OMX_TEAM_WORKER = `deep-display/${detachedWorkerName}`; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/${detachedWorkerName}`; process.env.TMUX_PANE = detachedWorkerPane; const detachedWorkerProductWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, session_id: "sess-di-artifact", tool_name: "Write", tool_input: { file_path: "src/detached-worker.ts", content: "export {};\n" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedWorkerProductWrite.outputJson, null); const detachedWorkerStateClear = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, tool_name: "mcp__omx_state__state_clear", tool_input: { mode: "deep-interview", workingDirectory: cwd }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedWorkerStateClear.outputJson?.decision, "block"); const detachedCamelIdentityWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, session_id: "sess-di-artifact", agentId: "untrusted-camel-child", tool_name: "Write", tool_input: { file_path: "src/camel-child.ts", content: "export {};\n" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedCamelIdentityWrite.outputJson?.decision, "block"); const detachedCamelThreadWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, session_id: "sess-di-artifact", threadId: "thread-di-artifact", tool_name: "Write", tool_input: { file_path: "src/camel-thread.ts", content: "export {};\n" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedCamelThreadWrite.outputJson?.decision, "block"); const detachedPatchState = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, tool_name: "Bash", tool_input: { command: `patch "$OMX_TEAM_STATE_ROOT/sessions/sess-di-artifact/deep-interview-state.json"` }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedPatchState.outputJson?.decision, "block"); const detachedHeaderDrivenPatch = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, tool_name: "Bash", tool_input: { command: "printf protected-diff | patch -p0" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedHeaderDrivenPatch.outputJson?.decision, "block"); const detachedProductPatch = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_input: { command: "patch src/product.ts" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedProductPatch.outputJson, null); const detachedPatchMention = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_input: { command: "printf '%s\\n' patch" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedPatchMention.outputJson, null); await symlink(stateDir, join(detachedWorkerCwd, "state-link")); const detachedWorkerStateAliasWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: detachedWorkerCwd, tool_name: "Write", tool_input: { file_path: `state-link/sessions/sess-di-artifact/deep-interview-state.json`, content: "{}\n" }, }, { cwd: detachedWorkerCwd }); assert.equal(detachedWorkerStateAliasWrite.outputJson?.decision, "block"); } finally { for (const [key, value] of Object.entries({ OMX_TEAM_WORKER: previousTeamEnv.worker, OMX_TEAM_INTERNAL_WORKER: previousTeamEnv.internalWorker, OMX_TEAM_STATE_ROOT: previousTeamEnv.stateRoot, OMX_TEAM_LEADER_CWD: previousTeamEnv.leaderCwd, TMUX_PANE: previousTeamEnv.pane, })) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } } const allowedBash = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-context-bash", tool_input: { command: "cat > .omx/context/demo.md <<'EOF'\n# Context\nEOF", }, }, { cwd }, ); assert.equal(allowedBash.outputJson, null); const allowedTmpBash = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-tmp-bash", tool_input: { command: [ "mkdir -p .omx/tmp/sess-di-artifact", "cat > .omx/tmp/sess-di-artifact/demo.md <<'EOF'", "# Scratch", "EOF", ].join("\n"), }, }, { cwd }, ); assert.equal(allowedTmpBash.outputJson, null); const blockedTmpScriptWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: "tool-di-tmp-script-write", tool_input: { file_path: ".omx/tmp/sess-di-artifact/run.sh", content: "printf pwned > src/pwned.ts\n", }, }, { cwd }, ); assert.equal( (blockedTmpScriptWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( JSON.stringify(blockedTmpScriptWrite.outputJson), /\.omx\/tmp|planning artifact paths/, ); const blockedTmpScriptExecution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-tmp-script-exec", tool_input: { command: "sh .omx/tmp/sess-di-artifact/run.sh" }, }, { cwd }, ); assert.equal( (blockedTmpScriptExecution.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( JSON.stringify(blockedTmpScriptExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); const blockedTmpInterpreterExecution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-tmp-python-exec", tool_input: { command: "python3.12 .omx/tmp/sess-di-artifact/generated.txt", }, }, { cwd }, ); assert.equal( ( blockedTmpInterpreterExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( JSON.stringify(blockedTmpInterpreterExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); const blockedTmpTsxExecution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-tmp-tsx-exec", tool_input: { command: "tsx --tsconfig tsconfig.json watch .omx/tmp/sess-di-artifact/generated.ts", }, }, { cwd }, ); assert.equal( (blockedTmpTsxExecution.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( JSON.stringify(blockedTmpTsxExecution.outputJson), /generated-script transport|\.omx\/tmp/, ); for (const [toolUseId, command] of [ [ "tool-di-tmp-python-option-txt-exec", "python -X dev .omx/tmp/sess-di-artifact/run.txt", ], [ "tool-di-tmp-node-require-load", "node --require .omx/tmp/sess-di-artifact/preload -e ''", ], [ "tool-di-tmp-node-import-load", "node --import .omx/tmp/sess-di-artifact/preload -e ''", ], [ "tool-di-tmp-bun-require-load", "bun --require .omx/tmp/sess-di-artifact/preload -e ''", ], [ "tool-di-tmp-bash-rcfile-load", "bash --rcfile .omx/tmp/sess-di-artifact/rc -i -c true", ], [ "tool-di-tmp-go-run-exec", "go run .omx/tmp/sess-di-artifact/probe.go", ], [ "tool-di-tmp-deno-run-exec", "deno run .omx/tmp/sess-di-artifact/generated.ts", ], [ "tool-di-tmp-python-stdin-exec", "python < .omx/tmp/sess-di-artifact/run.txt", ], [ "tool-di-tmp-node-stdin-exec", "node < .omx/tmp/sess-di-artifact/run.txt", ], [ "tool-di-tmp-ruby-stdin-exec", "ruby < .omx/tmp/sess-di-artifact/run.txt", ], [ "tool-di-tmp-perl-stdin-exec", "perl < .omx/tmp/sess-di-artifact/run.txt", ], ["tool-di-tmp-sh-stdin-exec", "sh < .omx/tmp/sess-di-artifact/run.txt"], [ "tool-di-tmp-bash-stdin-exec", "bash < .omx/tmp/sess-di-artifact/run.txt", ], [ "tool-di-tmp-same-command-stdin-exec", [ "python3 - <<'PY'", "from pathlib import Path", "Path('.omx/tmp/sess-di-artifact/run.txt').write_text('print(1)')", "PY", "python < .omx/tmp/sess-di-artifact/run.txt", ].join("\n"), ], ] as const) { const blockedTmpTransport = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: toolUseId, tool_input: { command }, }, { cwd }, ); assert.equal( (blockedTmpTransport.outputJson as { decision?: string } | null) ?.decision, "block", command, ); assert.match( JSON.stringify(blockedTmpTransport.outputJson), /generated-script transport|\.omx\/tmp/, ); } const allowedAppendBash = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-context-append-bash", tool_input: { command: "echo more context >> .omx/context/demo.md" }, }, { cwd }, ); assert.equal(allowedAppendBash.outputJson, null); const deepInterviewRalplanHandoffState = JSON.stringify({ mode: "autopilot", active: true, current_phase: "ralplan", session_id: "sess-di-artifact", workingDirectory: cwd, state: { deep_interview_gate: { status: "complete", rationale: "Requirements are clarified and ready for ralplan handoff.", }, }, }); await mkdir(join(cwd, ".omx", "context"), { recursive: true }); await mkdir(join(cwd, ".omx", "specs"), { recursive: true }); await writeFile(join(cwd, ".omx", "context", "deep-interview-demo.md"), "# Context\n"); await writeFile(join(cwd, ".omx", "specs", "deep-interview-demo.md"), "# Spec\n"); const reportedHandoffShape = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-reported-context-spec-handoff", tool_input: { command: `node "${join(resolve(nativeHookScriptPath(), "../../.."), "dist", "cli", "omx.js")}" state write --input '${deepInterviewRalplanHandoffState}' --json`, }, }, { cwd }, ); await rm(join(cwd, ".omx", "context", "deep-interview-demo.md"), { force: true }); await rm(join(cwd, ".omx", "specs", "deep-interview-demo.md"), { force: true }); assert.equal(reportedHandoffShape.outputJson, null); const blockedTmpOnlyHandoffShape = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-reported-tmp-only-handoff", tool_input: { command: [ "mkdir -p .omx/tmp/sess-di-artifact", "cat > .omx/tmp/sess-di-artifact/only.md <<'EOF'", "# Tmp-only scratch", "EOF", `${omxCommand} state write --input '${deepInterviewRalplanHandoffState}' --json`, ].join("\n"), }, }, { cwd }, ); assert.equal( (blockedTmpOnlyHandoffShape.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedTmpOnlyHandoffShape.outputJson as { reason?: string } | null) ?.reason ?? "", ), /handoff|Bash write intent|deep-interview/i, ); const blockedHandoffWithSameCommandArtifactExecution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-reported-handoff-with-artifact-exec", tool_input: { command: [ "mkdir -p .omx/context", "cat > .omx/context/run.sh <<'EOF'", "printf '%s\\n' same-command-artifact-executed", "EOF", "sh .omx/context/run.sh", `${omxCommand} state write --input '${deepInterviewRalplanHandoffState}' --json`, ].join("\n"), }, }, { cwd }, ); assert.equal( ( blockedHandoffWithSameCommandArtifactExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedHandoffWithSameCommandArtifactExecution.outputJson as { reason?: string; } | null )?.reason ?? "", ), /same-command|Bash write intent|handoff/i, ); const blockedHandoffWithPythonSameCommandArtifactExecution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-reported-handoff-with-python-artifact-exec", tool_input: { command: [ "python3 - <<'PY'", "from pathlib import Path", "Path('.omx/context/run.sh').write_text('echo ran')", "PY", "sh .omx/context/run.sh", `${omxCommand} state write --input '${deepInterviewRalplanHandoffState}' --json`, ].join("\n"), }, }, { cwd }, ); assert.equal( ( blockedHandoffWithPythonSameCommandArtifactExecution.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedHandoffWithPythonSameCommandArtifactExecution.outputJson as { reason?: string; } | null )?.reason ?? "", ), /same-command|Bash write intent|handoff/i, ); for (const [toolUseId, artifactDir, scriptName, executionLine] of [ [ "tool-di-reported-handoff-with-cd-relative-sh-exec", ".omx/context", "run.sh", "cd .omx/context && sh run.sh", ], [ "tool-di-reported-handoff-with-command-cd-relative-sh-exec", ".omx/context", "run.sh", "command cd .omx/context && sh run.sh", ], [ "tool-di-reported-handoff-with-builtin-cd-relative-sh-exec", ".omx/context", "run.sh", "builtin cd .omx/context && sh run.sh", ], [ "tool-di-reported-handoff-with-cd-double-dash-relative-sh-exec", ".omx/context", "run.sh", "cd -- .omx/context && sh run.sh", ], [ "tool-di-reported-handoff-with-cd-physical-relative-sh-exec", ".omx/context", "run.sh", "cd -P .omx/context && sh run.sh", ], [ "tool-di-reported-handoff-with-cd-logical-relative-sh-exec", ".omx/context", "run.sh", "cd -L .omx/context && sh run.sh", ], [ "tool-di-reported-handoff-with-cd-relative-dot-source", ".omx/specs", "env.sh", "cd .omx/specs && . env.sh", ], [ "tool-di-reported-handoff-with-cd-relative-direct-exec", ".omx/context", "run-relative.sh", "cd .omx/context && ./run-relative.sh", ], [ "tool-di-reported-handoff-with-setsid-direct-exec", ".omx/context", "run.sh", "setsid ./.omx/context/run.sh", ], [ "tool-di-reported-handoff-with-env-chdir-relative-sh-exec", ".omx/context", "run.sh", "env -C .omx/context sh run.sh", ], [ "tool-di-reported-handoff-with-env-long-chdir-relative-dot-source", ".omx/specs", "env.sh", "env --chdir .omx/specs . env.sh", ], ] as const) { const blockedCwdRelativeArtifactExecution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: toolUseId, tool_input: { command: [ `mkdir -p ${artifactDir}`, `cat > ${artifactDir}/${scriptName} <<'EOF'`, "printf '%s\\n' same-command-cwd-relative-artifact-executed", "EOF", executionLine, `${omxCommand} state write --input '${deepInterviewRalplanHandoffState}' --json`, ].join("\n"), }, }, { cwd }, ); assert.equal( ( blockedCwdRelativeArtifactExecution.outputJson as { decision?: string; } | null )?.decision, "block", executionLine, ); assert.match( String( ( blockedCwdRelativeArtifactExecution.outputJson as { reason?: string; } | null )?.reason ?? "", ), /same-command|Bash write intent|handoff/i, executionLine, ); } const blockedHandoffWithImplementationWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-reported-handoff-with-source-write", tool_input: { command: [ "mkdir -p .omx/context .omx/specs src", "cat > .omx/context/deep-interview-demo.md <<'EOF'", "# Context", "EOF", "cat > src/runtime.ts <<'EOF'", "export const changed = true;", "EOF", `${omxCommand} state write --input '${deepInterviewRalplanHandoffState}' --json`, ].join("\n"), }, }, { cwd }, ); assert.equal( ( blockedHandoffWithImplementationWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedHandoffWithImplementationWrite.outputJson as { reason?: string; } | null )?.reason ?? "", ), /src\/runtime\.ts/, ); const blockedHandoffWithUnredirectedSourceMutation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-reported-handoff-with-source-mkdir", tool_input: { command: [ "mkdir -p .omx/context .omx/specs src/generated", "cat > .omx/context/deep-interview-demo.md <<'EOF'", "# Context", "EOF", `${omxCommand} state write --input '${deepInterviewRalplanHandoffState}' --json`, ].join("\n"), }, }, { cwd }, ); assert.equal( ( blockedHandoffWithUnredirectedSourceMutation.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedHandoffWithUnredirectedSourceMutation.outputJson as { reason?: string; } | null )?.reason ?? "", ), /Bash/, ); const allowedReadOnlyEditors = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-read-only-editors", tool_input: { command: "sed -n '1,20p' src/runtime.ts; perl -ne 'print if $. < 3' src/runtime.ts", }, }, { cwd }, ); assert.equal(allowedReadOnlyEditors.outputJson, null); const blockedCombinedSedSourceEdit = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-sed-combined-source-edit", tool_input: { command: "sed -Ei 's/old/new/' src/runtime.ts" }, }, { cwd }, ); assert.equal( ( blockedCombinedSedSourceEdit.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedCombinedSedSourceEdit.outputJson as { reason?: string; } | null )?.reason ?? "", ), /src\/runtime\.ts/, ); const allowedCombinedSedArtifactEdit = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-sed-combined-artifact-edit", tool_input: { command: "sed -Ei 's/old/new/' .omx/specs/deep-interview-demo.md", }, }, { cwd }, ); assert.equal(allowedCombinedSedArtifactEdit.outputJson, null); const allowedPlanningStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: "tool-di-planning-state-write", tool_input: { file_path: ".omx/state/deep-interview-notes.json", content: "{}\n", }, }, { cwd }, ); assert.equal(allowedPlanningStateWrite.outputJson, null); const protectedStateFiles = [ ".omx/state/sessions/sess-di-artifact/autopilot-state.json", ".omx/state/sessions/sess-di-artifact/deep-interview-state.json", ".omx/state/sessions/sess-di-artifact/skill-active-state.json", ".omx/state/sessions/sess-di-artifact/ralph-state.json", ".omx/state/sessions/sess-di-artifact/ultragoal-state.json", ".omx/state/deep-interview-state.json", ]; for (const [index, filePath] of protectedStateFiles.entries()) { const protectedWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Write", tool_use_id: `tool-di-protected-state-${index}`, tool_input: { file_path: filePath, content: "{}\n" }, }, { cwd }, ); assert.equal( (protectedWrite.outputJson as { decision?: string } | null)?.decision, "block", `${filePath} should not be model-writable during deep-interview`, ); } const runtimeRoot = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-deep-interview-runtime-root-"), ); const previousOmxRoot = process.env.OMX_ROOT; const runtimeSessionId = "sess-di-runtime-artifact"; const runtimeStateDir = join(runtimeRoot, ".omx", "state"); const runtimeSessionDir = join( runtimeStateDir, "sessions", runtimeSessionId, ); try { process.env.OMX_ROOT = runtimeRoot; await mkdir(runtimeSessionDir, { recursive: true }); await writeJson(join(runtimeStateDir, "session.json"), { session_id: runtimeSessionId, cwd, }); await writeJson(join(runtimeSessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: runtimeSessionId, thread_id: threadId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: runtimeSessionId, thread_id: threadId, }, ], }); await writeJson(join(runtimeSessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: runtimeSessionId, thread_id: threadId, }); const allowedRuntimeStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: runtimeSessionId, thread_id: threadId, tool_name: "Write", tool_use_id: "tool-di-runtime-state-write", tool_input: { file_path: join(runtimeSessionDir, "deep-interview-state.json"), content: "{}\n", }, }, { cwd }, ); assert.equal(allowedRuntimeStateWrite.outputJson?.decision, "block"); const blockedRuntimeStatePeerWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: runtimeSessionId, thread_id: threadId, tool_name: "Write", tool_use_id: "tool-di-runtime-peer-state-write", tool_input: { file_path: join( runtimeRoot, ".omx", "state", "sessions", "../outside", "deep-interview-state.json", ), content: "{}\n", }, }, { cwd }, ); assert.equal( ( blockedRuntimeStatePeerWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); const allowedRuntimeUnrelatedWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: runtimeSessionId, thread_id: threadId, tool_name: "Write", tool_use_id: "tool-di-runtime-unrelated-write", tool_input: { file_path: join( runtimeRoot, "unrelated", "deep-interview-state.json", ), content: "{}\n", }, }, { cwd }, ); assert.equal(allowedRuntimeUnrelatedWrite.outputJson?.decision, "block"); assert.match(String(allowedRuntimeUnrelatedWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; await rm(runtimeRoot, { recursive: true, force: true }); } // Cross-mode non-terminal `omx state write` payloads are activations, // because state_write normalizes them to active=true after the hook. const blockedStateCliMutation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-write", tool_input: { command: 'omx state write --input \'{"mode":"ralph","current_phase":"executing"}\' --json', }, }, { cwd }, ); assert.equal( (blockedStateCliMutation.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedMcpStateMutation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-mcp-state-write-execute", tool_input: { mode: "ralph", current_phase: "executing" }, }, { cwd }, ); assert.equal( (blockedMcpStateMutation.outputJson as { decision?: string } | null) ?.decision, "block", ); for (const phase of [ "planning", "replan", "autopilot:ralplan", ] as const) { const allowedAutopilotPlanningAlias = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-di-mcp-state-write-autopilot-alias-${phase}`, tool_input: { mode: "autopilot", current_phase: phase, session_id: "sess-di-artifact", workingDirectory: cwd }, }, { cwd }, ); assert.equal( allowedAutopilotPlanningAlias.outputJson, null, `${phase} autopilot planning handoff should not be classified as deactivation`, ); } const blockedForeignModeMutationWithQuotedMention = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-mode-mention-in-json", tool_input: { command: 'omx state write --input \'{"mode":"ralph","note":"--mode deep-interview","active":false}\' --json', }, }, { cwd }, ); assert.equal( (blockedForeignModeMutationWithQuotedMention.outputJson as { decision?: string } | null)?.decision, "block", ); const allowedStateInputFile = join(cwd, "allowed-state-input.json"); await writeJson(allowedStateInputFile, { mode: "deep-interview", current_phase: "intent-first", active: true, }); const allowedStateInputFileMutation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-input-file-allowed", tool_input: { command: `omx state write --input-file ${allowedStateInputFile} --json`, }, }, { cwd }, ); assert.equal(allowedStateInputFileMutation.outputJson?.decision, "block"); const standaloneAutopilotRalplanHandoffPayload = { mode: "autopilot", active: true, current_phase: "ralplan", session_id: "sess-di-artifact", workingDirectory: cwd, state: { deep_interview_gate: { status: "complete", rationale: "The bounded PMO catalog task is clear and ready for ralplan handoff.", handoff_summary: "Create the root-level pmo catalog from the captured spec.", }, }, }; const standaloneAutopilotRalplanHandoffInputFile = join( cwd, ".omx", "tmp", "pmo-autopilot-state.json", ); await mkdir(dirname(standaloneAutopilotRalplanHandoffInputFile), { recursive: true, }); await writeJson( standaloneAutopilotRalplanHandoffInputFile, standaloneAutopilotRalplanHandoffPayload, ); const blockedStandaloneAutopilotRalplanHandoffWithoutEvidence = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-standalone-autopilot-ralplan-handoff-without-evidence", tool_input: { command: `omx state write --input-file ${standaloneAutopilotRalplanHandoffInputFile} --json`, }, }, { cwd }, ); assert.equal( ( blockedStandaloneAutopilotRalplanHandoffWithoutEvidence.outputJson as { decision?: string; } | null )?.decision, "block", ); const priorDeepInterviewSpec = join( cwd, ".omx", "specs", "pmo-catalog-spec.md", ); await mkdir(dirname(priorDeepInterviewSpec), { recursive: true }); await writeFile(priorDeepInterviewSpec, "# PMO catalog spec\n"); const allowedStandaloneAutopilotRalplanHandoffWithEvidence = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-standalone-autopilot-ralplan-handoff-with-evidence", tool_input: { command: `omx state write --input-file ${standaloneAutopilotRalplanHandoffInputFile} --json`, }, }, { cwd }, ); assert.equal( allowedStandaloneAutopilotRalplanHandoffWithEvidence.outputJson, null, "a valid autopilot deep-interview -> ralplan state write may use prior durable .omx/specs evidence", ); // A deactivating `omx state write` (or `omx state clear`) ends the planning // phase, which the backend does not gate for standalone modes; the hook // rejects these deactivation vectors at the transport boundary. const blockedStateDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-deactivate", tool_input: { command: 'omx state write --input \'{"mode":"deep-interview","active":false}\' --json', }, }, { cwd }, ); assert.equal( (blockedStateDeactivation.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedStateInputFile = join(cwd, "blocked-state-input.json"); await writeJson(blockedStateInputFile, { mode: "deep-interview", active: false, }); const blockedStateDeactivationFile = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-input-file-blocked", tool_input: { command: `omx state write --input-file ${blockedStateInputFile} --json`, }, }, { cwd }, ); assert.equal( ( blockedStateDeactivationFile.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedModeFlagDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-mode-flag-deactivate", tool_input: { command: "omx state write --mode deep-interview --input '{\"active\":false}' --json", }, }, { cwd }, ); assert.equal( (blockedModeFlagDeactivation.outputJson as { decision?: string } | null) ?.decision, "block", ); const conflictingModeFlagPayload = join( cwd, "conflicting-mode-flag-state.json", ); await writeJson(conflictingModeFlagPayload, { mode: "ralph", active: false, }); const blockedModeFlagFileDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-mode-flag-file-deactivate", tool_input: { command: `omx state write --mode deep-interview --input-file ${conflictingModeFlagPayload} --json`, }, }, { cwd }, ); assert.equal( ( blockedModeFlagFileDeactivation.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedRepeatedModeFlagDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-repeated-mode-flag-deactivate", tool_input: { command: "omx state write --mode ralph --mode deep-interview --input '{\"active\":false}' --json", }, }, { cwd }, ); assert.equal( ( blockedRepeatedModeFlagDeactivation.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedRepeatedInputDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-repeated-input-deactivate", tool_input: { command: 'omx state write --input \'{"mode":"deep-interview","active":true}\' ' + '--input \'{"mode":"deep-interview","active":false}\' --json', }, }, { cwd }, ); assert.equal( ( blockedRepeatedInputDeactivation.outputJson as { decision?: string; } | null )?.decision, "block", ); const repeatedInputFileSafe = join(cwd, "repeated-input-file-safe.json"); const repeatedInputFileBlocked = join( cwd, "repeated-input-file-blocked.json", ); await writeJson(repeatedInputFileSafe, { mode: "deep-interview", current_phase: "intent-first", active: true, }); await writeJson(repeatedInputFileBlocked, { mode: "deep-interview", active: false, }); const blockedRepeatedInputFileDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-repeated-input-file-deactivate", tool_input: { command: `omx state write --input-file ${repeatedInputFileSafe} --input-file ${repeatedInputFileBlocked} --json`, }, }, { cwd }, ); assert.equal( ( blockedRepeatedInputFileDeactivation.outputJson as { decision?: string; } | null )?.decision, "block", ); const terminalCurrentPhaseAliases = [ "finish", "finished", "complete", "completed", "done", "blocked", "blocked-on-user", "blocked_on_user", "failed", "fail", "error", "cancelled", "canceled", "cancel", "aborted", "abort", "userinterlude", "user-interlude", "interrupted", "interrupt", "askuserquestion", "ask-user-question", "askuser", "question", ] as const; for (const alias of terminalCurrentPhaseAliases) { const blockedStateWriteCurrentPhaseAlias = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-current-phase-alias-${alias}`, tool_input: { command: `${omxCommand} state write --input '${JSON.stringify({ mode: "deep-interview", current_phase: alias })}' --json`, }, }, { cwd }, ); assert.equal( ( blockedStateWriteCurrentPhaseAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${alias} current_phase should deactivate protected deep-interview planning`, ); } const blockedCurrentPhaseAliasInputFile = join( cwd, "blocked-current-phase-alias-input-file.json", ); await writeJson(blockedCurrentPhaseAliasInputFile, { mode: "deep-interview", current_phase: "done", }); const blockedStateWriteCurrentPhaseAliasFile = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-current-phase-alias-input-file", tool_input: { command: `omx state write --input-file ${blockedCurrentPhaseAliasInputFile} --json`, }, }, { cwd }, ); assert.equal( ( blockedStateWriteCurrentPhaseAliasFile.outputJson as { decision?: string; } | null )?.decision, "block", ); const terminalOutcomeAliasPayloads = [ { runOutcome: "done" }, { lifecycleOutcome: "blocked" }, { terminalOutcome: "aborted" }, ] as const; for (const [ index, aliasPayload, ] of terminalOutcomeAliasPayloads.entries()) { const blockedCamelCaseOutcomeAlias = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-camel-terminal-outcome-${index}`, tool_input: { command: `${omxCommand} state write --input '${JSON.stringify({ mode: "deep-interview", ...aliasPayload })}' --json`, }, }, { cwd }, ); assert.equal( ( blockedCamelCaseOutcomeAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${Object.keys(aliasPayload)[0]} should deactivate protected deep-interview planning`, ); } const blockedCamelCaseOutcomeInputFile = join( cwd, "blocked-camel-case-outcome-input-file.json", ); await writeJson(blockedCamelCaseOutcomeInputFile, { mode: "deep-interview", lifecycleOutcome: "finished", }); const blockedStateWriteCamelCaseOutcomeFile = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-camel-terminal-outcome-input-file", tool_input: { command: `omx state write --input-file ${blockedCamelCaseOutcomeInputFile} --json`, }, }, { cwd }, ); assert.equal( ( blockedStateWriteCamelCaseOutcomeFile.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedCrossModeActivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-cross-mode-activation", tool_input: { command: 'omx state write --input \'{"mode":"ralph","active":true}\' --json', }, }, { cwd }, ); assert.equal( (blockedCrossModeActivation.outputJson as { decision?: string } | null) ?.decision, "block", ); const cwdRelativeInputFileRootSafe = join( cwd, "cwd-relative-input-file-safe.json", ); const cwdRelativeInputFileSubdir = join( cwd, "cwd-relative-input-file-subdir", ); await mkdir(cwdRelativeInputFileSubdir, { recursive: true }); await writeJson(cwdRelativeInputFileRootSafe, { mode: "deep-interview", active: true, }); await writeJson(join(cwdRelativeInputFileSubdir, "payload.json"), { mode: "deep-interview", active: false, }); const blockedCwdRelativeInputFileAfterCd = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-cwd-relative-input-file-after-cd", tool_input: { command: "cd cwd-relative-input-file-subdir && omx state write --input-file payload.json --json", }, }, { cwd }, ); assert.equal( ( blockedCwdRelativeInputFileAfterCd.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedRewrittenInputFile = join(cwd, "rewritten-input-file.json"); await writeJson(blockedRewrittenInputFile, { mode: "deep-interview", active: true, }); const blockedRewrittenInputFileMutation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-rewritten-input-file", tool_input: { command: 'printf \'{"mode":"deep-interview","active":false}\' > rewritten-input-file.json && ' + "omx state write --input-file rewritten-input-file.json --json", }, }, { cwd }, ); assert.equal( ( blockedRewrittenInputFileMutation.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedNestedStateDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-nested-state-deactivate", tool_input: { command: 'omx state write --mode deep-interview --input \'{"state":{"active":false}}\' --json', }, }, { cwd }, ); assert.equal( ( blockedNestedStateDeactivation.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedMultipleStateWrites = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-multiple-writes", tool_input: { command: 'omx state write --input \'{"mode":"deep-interview","active":true}\' --json && ' + 'omx state write --input \'{"mode":"deep-interview","active":false}\' --json', }, }, { cwd }, ); assert.equal( (blockedMultipleStateWrites.outputJson as { decision?: string } | null) ?.decision, "block", ); const allowedDecoyBeforeStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-decoy-before-real-write", tool_input: { command: 'printf \'%s\\n\' "--input \'{\\"mode\\":\\"deep-interview\\",\\"active\\":false}\'" && ' + 'omx state write --input \'{"mode":"deep-interview","active":true}\' --json', }, }, { cwd }, ); assert.equal(allowedDecoyBeforeStateWrite.outputJson?.decision, "block"); const blockedFileWriteWithLaterSafeDecoy = join( cwd, "blocked-file-before-later-decoy.json", ); await writeJson(blockedFileWriteWithLaterSafeDecoy, { mode: "deep-interview", active: false, }); const blockedSegmentedStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-bounded-segment", tool_input: { command: `omx state write --input-file ${blockedFileWriteWithLaterSafeDecoy} --json && ` + 'printf \'%s\\n\' "--input \'{\\"mode\\":\\"deep-interview\\",\\"active\\":true}\'"', }, }, { cwd }, ); assert.equal( (blockedSegmentedStateWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); const allowedMcpStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-mcp-state-write-allowed", tool_input: { mode: "deep-interview", current_phase: "intent-first", active: true, session_id: "sess-di-artifact", workingDirectory: cwd, }, }, { cwd }, ); assert.equal(allowedMcpStateWrite.outputJson, null); const blockedMcpStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-mcp-state-write-deactivate", tool_input: { mode: "deep-interview", active: false }, }, { cwd }, ); assert.equal( (blockedMcpStateWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedNestedMcpStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-mcp-state-write-nested-deactivate", tool_input: { mode: "deep-interview", state: { active: false } }, }, { cwd }, ); assert.equal( (blockedNestedMcpStateWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); for (const alias of terminalCurrentPhaseAliases) { const blockedMcpCurrentPhaseAlias = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-di-mcp-state-write-current-phase-alias-${alias}`, tool_input: { mode: "deep-interview", state: { currentPhase: alias }, }, }, { cwd }, ); assert.equal( ( blockedMcpCurrentPhaseAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${alias} currentPhase should deactivate protected deep-interview planning`, ); } for (const [ index, aliasPayload, ] of terminalOutcomeAliasPayloads.entries()) { const blockedMcpCamelCaseOutcomeAlias = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-di-mcp-state-write-camel-terminal-outcome-${index}`, tool_input: { mode: "deep-interview", state: aliasPayload }, }, { cwd }, ); assert.equal( ( blockedMcpCamelCaseOutcomeAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${Object.keys(aliasPayload)[0]} MCP write should deactivate protected deep-interview planning`, ); } const blockedMcpStateClear = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "mcp__omx_state__state_clear", tool_use_id: "tool-di-mcp-state-clear", tool_input: { mode: "deep-interview" }, }, { cwd }, ); assert.equal( (blockedMcpStateClear.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedStateClear = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-clear", tool_input: { command: `${omxCommand} state clear --json` }, }, { cwd }, ); assert.equal( (blockedStateClear.outputJson as { decision?: string } | null) ?.decision, "block", ); const stateDeactivationInput = '\'{"mode":"deep-interview","active":false}\''; const absolutePathQualifiedNpm = resolve(cwd, "bin", "npm"); const cliWrapperPlanningDeactivationCommands = [ ["node-wrapper-clear", "node dist/cli/omx.js state clear --json"], [ "node-wrapper-write", `node dist/cli/omx.js state write --input ${stateDeactivationInput} --json`, ], [ "node-wrapper-option-clear", "node --enable-source-maps dist/cli/omx.js state clear --json", ], [ "node-wrapper-require-clear", "node --require tsx/cjs dist/cli/omx.js state clear --json", ], [ "bun-wrapper-path-variant", "bun ./dist/cli/omx.js state clear --json", ], [ "tsx-wrapper-write", `tsx dist/cli/omx.js state write --input ${stateDeactivationInput} --json`, ], ["direct-wrapper-clear", "./dist/cli/omx.js state clear --json"], [ "path-qualified-omx-clear", "./node_modules/.bin/omx state clear --json", ], [ "quoted-node-wrapper-clear", 'node "dist/cli/omx.js" state clear --json', ], [ "quoted-direct-wrapper-clear", '"./dist/cli/omx.js" state clear --json', ], [ "quoted-node-wrapper-write", `node "dist/cli/omx.js" state write --input ${stateDeactivationInput} --json`, ], [ "env-wrapper-clear", "env FOO=bar node dist/cli/omx.js state clear --json", ], [ "env-argv0-short-wrapper-clear", "env -a fake node dist/cli/omx.js state clear --json", ], [ "env-argv0-long-wrapper-clear", "env --argv0 fake node dist/cli/omx.js state clear --json", ], [ "node-title-wrapper-clear", "node --title foo dist/cli/omx.js state clear --json", ], ["time-wrapper-clear", "time omx state clear --json"], [ "time-format-wrapper-clear", "/usr/bin/time -f x omx state clear --json", ], ["time-output-wrapper-clear", "time -o out omx state clear --json"], [ "time-cluster-output-wrapper-clear", "/usr/bin/time -ao out omx state clear --json", ], [ "time-cluster-format-wrapper-clear", "/usr/bin/time -af fmt omx state clear --json", ], ["nice-wrapper-clear", "nice -n 5 omx state clear --json"], [ "nice-wrapper-write", `nice -n 5 omx state write --input ${stateDeactivationInput} --json`, ], ["stdbuf-wrapper-clear", "stdbuf -o0 omx state clear --json"], [ "stdbuf-wrapper-write", `stdbuf -o0 omx state write --input ${stateDeactivationInput} --json`, ], ["timeout-wrapper-clear", "timeout 5 omx state clear --json"], [ "timeout-wrapper-write", `timeout 5 omx state write --input ${stateDeactivationInput} --json`, ], ["setsid-wrapper-clear", "setsid omx state clear --json"], [ "setsid-wrapper-write", `setsid omx state write --input ${stateDeactivationInput} --json`, ], ["setsid-wait-wrapper-clear", "setsid -w omx state clear --json"], [ "setsid-wait-wrapper-write", `setsid -w omx state write --input ${stateDeactivationInput} --json`, ], ["time-brace-group-clear", "time { omx state clear --json; }"], [ "time-if-condition-clear", "time if omx state clear --json; then :; fi", ], ["time-subshell-clear", "time ( omx state clear --json )"], [ "time-command-env-node-wrapper-clear", "time command env node dist/cli/omx.js state clear --json", ], [ "command-time-subshell-clear", "command time ( omx state clear --json )", ], ["coproc-wrapper-clear", "coproc omx state clear --json"], [ "coproc-wrapper-write", `coproc omx state write --input ${stateDeactivationInput} --json`, ], [ "coproc-name-brace-wrapper-clear", "coproc worker { omx state clear --json; }", ], [ "coproc-name-brace-wrapper-write", `coproc worker { omx state write --input ${stateDeactivationInput} --json; }`, ], ["xargs-wrapper-clear", "xargs omx state clear --json /dev/null omx state clear --json"], [ "leading-redirection-write", `>/dev/null omx state write --input ${stateDeactivationInput} --json`, ], ["env-split-trailing-clear", "env -S FOO=bar omx state clear --json"], [ "env-split-string-trailing-write", `env --split-string 'FOO=bar' omx state write --input ${stateDeactivationInput} --json`, ], ["brace-group-clear", "{ omx state clear --json; }"], [ "brace-group-write", `{ omx state write --input ${stateDeactivationInput} --json; }`, ], ["pipeline-to-subshell-clear", "printf 'x' | (omx state clear --json)"], [ "pipeline-stderr-to-subshell-write", `printf 'x' |& (node dist/cli/omx.js state write --input ${stateDeactivationInput} --json)`, ], ["if-condition-clear", "if omx state clear --json; then :; fi"], [ "if-condition-write", `if node dist/cli/omx.js state write --input ${stateDeactivationInput} --json; then :; fi`, ], ["background-clear", "sleep 0 & omx state clear --json"], [ "background-node-wrapper-write", `sleep 0 & node dist/cli/omx.js state write --input ${stateDeactivationInput} --json`, ], [ "nested-command-env-wrapper-clear", "command env node dist/cli/omx.js state clear --json", ], [ "nested-exec-env-wrapper-clear", "exec env node dist/cli/omx.js state clear --json", ], [ "nested-env-option-wrapper-clear", "env -i env node dist/cli/omx.js state clear --json", ], ] as const; for (const [name, command] of cliWrapperPlanningDeactivationCommands) { const blockedCliWrapperPlanningDeactivation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-${name}`, tool_input: { command }, }, { cwd }, ); assert.equal( ( blockedCliWrapperPlanningDeactivation.outputJson as { decision?: string; } | null )?.decision, "block", `${command} should be normalized to a protected omx state operation`, ); } const safeStateWriteInput = `'${JSON.stringify({ mode: "deep-interview", active: true, current_phase: "intent-first", session_id: "sess-di-artifact", workingDirectory: cwd, })}'`; const safeCliWrapperStateWriteCommands = [ [ "env-wrapper-safe-write", `env FOO=bar node dist/cli/omx.js state write --input ${safeStateWriteInput} --json`, ], [ "command-wrapper-safe-write", `command node dist/cli/omx.js state write --input ${safeStateWriteInput} --json`, ], [ "exec-wrapper-safe-write", `exec node dist/cli/omx.js state write --input ${safeStateWriteInput} --json`, ], [ "nested-command-env-wrapper-safe-write", `command env node dist/cli/omx.js state write --input ${safeStateWriteInput} --json`, ], [ "nested-env-command-wrapper-safe-write", `env -i command node dist/cli/omx.js state write --input ${safeStateWriteInput} --json`, ], ] as const; for (const [name, command] of safeCliWrapperStateWriteCommands) { const allowedCliWrapperStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-${name}`, tool_input: { command }, }, { cwd }, ); assert.equal( allowedCliWrapperStateWrite.outputJson, null, `${command} should defer to backend validation`, ); } const previousPath = process.env.PATH; try { process.env.PATH = "/usr/bin:/bin"; const cleanPathCanonicalStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-clean-path-canonical-state-write", tool_input: { command: `env FOO=bar ${JSON.stringify(process.execPath)} dist/cli/omx.js state write --input ${safeStateWriteInput} --json`, }, }, { cwd }, ); assert.equal(cleanPathCanonicalStateWrite.outputJson, null, "canonical clean-PATH state write should reach backend validation"); const cleanPathReadOnlyInspection = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-clean-path-read-only-inspection", tool_input: { command: "sed -n '1,20p' src/runtime.ts; perl -ne 'print if $. < 3' src/runtime.ts", }, }, { cwd }, ); assert.equal(cleanPathReadOnlyInspection.outputJson, null, "sed/perl inspection should remain read-only under clean PATH"); for (const [toolUseId, command] of [ ["tool-di-clean-path-sed-mutation", "sed -Ei 's/old/new/' src/runtime.ts"], ["tool-di-clean-path-perl-mutation", "perl -0pi -e 's/old/new/' src/runtime.ts"], ] as const) { const cleanPathMutation = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: toolUseId, tool_input: { command }, }, { cwd }, ); assert.equal(cleanPathMutation.outputJson?.decision, "block", command); } } finally { if (previousPath === undefined) delete process.env.PATH; else process.env.PATH = previousPath; } const safeArtifactInputFile = join( cwd, ".omx", "context", "safe-state-input-file.json", ); await mkdir(dirname(safeArtifactInputFile), { recursive: true }); await writeJson(safeArtifactInputFile, { mode: "deep-interview", active: true, current_phase: "intent-first", session_id: "sess-di-artifact", workingDirectory: cwd, }); const safeCliWrapperInputFileStateWriteCommands = [ [ "env-unset-wrapper-safe-input-file", `env -u FOO node dist/cli/omx.js state write --input-file ${safeArtifactInputFile} --json`, ], [ "env-chdir-wrapper-safe-input-file", `env -C ${cwd} node dist/cli/omx.js state write --input-file ${safeArtifactInputFile} --json`, ], [ "command-wrapper-safe-input-file", `command node dist/cli/omx.js state write --input-file ${safeArtifactInputFile} --json`, ], [ "exec-wrapper-safe-input-file", `exec node dist/cli/omx.js state write --input-file ${safeArtifactInputFile} --json`, ], ] as const; for (const [name, command] of safeCliWrapperInputFileStateWriteCommands) { const allowedCliWrapperInputFileStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-${name}`, tool_input: { command }, }, { cwd }, ); assert.equal( allowedCliWrapperInputFileStateWrite.outputJson, null, `${command} should defer to backend validation`, ); } const envChdirRelativeInputFileRoot = join(cwd, "payload.json"); const envChdirRelativeInputFileSubdir = join( cwd, "env-chdir-input-file-subdir", ); await mkdir(envChdirRelativeInputFileSubdir, { recursive: true }); await writeJson(envChdirRelativeInputFileRoot, { mode: "deep-interview", active: true, current_phase: "intent-first", }); await writeJson(join(envChdirRelativeInputFileSubdir, "payload.json"), { mode: "deep-interview", active: false, current_phase: "intent-first", }); const blockedEnvChdirRelativeInputFileWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-env-chdir-relative-input-file-write", tool_input: { command: `env -C ${envChdirRelativeInputFileSubdir} node ${resolve(cwd, "dist/cli/omx.js")} state write --input-file payload.json --json`, }, }, { cwd }, ); assert.equal( ( blockedEnvChdirRelativeInputFileWrite.outputJson as { decision?: string; } | null )?.decision, "block", "env -C should resolve --input-file relative to the wrapper cwd, not the hook cwd", ); const blockedEnvChdirLongFlagRelativeInputFileWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-env-chdir-long-relative-input-file-write", tool_input: { command: `env --chdir ${envChdirRelativeInputFileSubdir} node ${resolve(cwd, "dist/cli/omx.js")} state write --input-file payload.json --json`, }, }, { cwd }, ); assert.equal( ( blockedEnvChdirLongFlagRelativeInputFileWrite.outputJson as { decision?: string; } | null )?.decision, "block", "env --chdir should resolve --input-file relative to the wrapper cwd, not the hook cwd", ); const pnpmChdirRelativeInputFileSubdir = join( cwd, "pnpm-chdir-input-file-subdir", ); await mkdir(pnpmChdirRelativeInputFileSubdir, { recursive: true }); await writeJson(join(cwd, "payload.json"), { mode: "deep-interview", active: true, current_phase: "intent-first", }); await writeJson(join(pnpmChdirRelativeInputFileSubdir, "payload.json"), { mode: "deep-interview", active: false, current_phase: "intent-first", }); const blockedPnpmChdirRelativeInputFileWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-pnpm-chdir-relative-input-file-write", tool_input: { command: `pnpm -C ${pnpmChdirRelativeInputFileSubdir} exec ${omxCommand} state write --input-file payload.json --json`, }, }, { cwd }, ); assert.equal( ( blockedPnpmChdirRelativeInputFileWrite.outputJson as { decision?: string; } | null )?.decision, "block", "pnpm -C should resolve --input-file relative to the wrapper cwd, not the hook cwd", ); const rewrittenArtifactInputFile = join( cwd, ".omx", "context", "rewritten-state-input-file.json", ); await mkdir(dirname(rewrittenArtifactInputFile), { recursive: true }); await writeJson(rewrittenArtifactInputFile, { mode: "deep-interview", active: true, }); const blockedNestedArtifactInputFileRewriteBeforeStateWriteCommands = [ [ "bash-c", `printf '{"mode":"deep-interview","active":false}' > ${rewrittenArtifactInputFile} && bash -c 'omx state write --input-file ${rewrittenArtifactInputFile} --json'`, ], [ "env-split", `printf '{"mode":"deep-interview","active":false}' > ${rewrittenArtifactInputFile} && env -S 'omx state write --input-file ${rewrittenArtifactInputFile} --json'`, ], [ "command-substitution", `printf '{"mode":"deep-interview","active":false}' > ${rewrittenArtifactInputFile} && echo $(omx state write --input-file ${rewrittenArtifactInputFile} --json)`, ], [ "backtick-substitution", 'printf \'{"mode":"deep-interview","active":false}\' > ' + rewrittenArtifactInputFile + " && echo `omx state write --input-file " + rewrittenArtifactInputFile + " --json`", ], ] as const; for (const [ name, command, ] of blockedNestedArtifactInputFileRewriteBeforeStateWriteCommands) { const blockedNestedArtifactInputFileRewriteBeforeStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-nested-artifact-input-file-rewrite-${name}`, tool_input: { command }, }, { cwd }, ); assert.equal( ( blockedNestedArtifactInputFileRewriteBeforeStateWrite.outputJson as { decision?: string; } | null )?.decision, "block", `${command} should fail closed because nested --input-file writes cannot be read safely before execution`, ); } const blockedArtifactInputFileRewriteBeforeStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-artifact-input-file-rewrite-before-write", tool_input: { command: `printf '{"mode":"deep-interview","active":false}' > ${rewrittenArtifactInputFile} && ` + `omx state write --input-file ${rewrittenArtifactInputFile} --json`, }, }, { cwd }, ); assert.equal( ( blockedArtifactInputFileRewriteBeforeStateWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); const repeatedArtifactInputFile = join( cwd, ".omx", "context", "repeated-state-input-file.json", ); await writeJson(repeatedArtifactInputFile, { mode: "deep-interview", active: true, }); const blockedRepeatedArtifactInputFileRewriteBeforeSecondStateWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-repeated-artifact-input-file-rewrite", tool_input: { command: `omx state write --input-file ${repeatedArtifactInputFile} --json && ` + `printf '{"mode":"deep-interview","active":false}' > ${repeatedArtifactInputFile} && ` + `omx state write --input-file ${repeatedArtifactInputFile} --json`, }, }, { cwd }, ); assert.equal( ( blockedRepeatedArtifactInputFileRewriteBeforeSecondStateWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); // An implementation write smuggled alongside an allowed `omx state` command // must not be short-circuited through the allowance. const blockedChainedWrite = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-chained", tool_input: { command: "printf 'x' > src/evil.ts && omx state read --json", }, }, { cwd }, ); assert.equal( (blockedChainedWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedSourceGeneratedScriptCommands = [ [ "source-redirect", "printf 'omx state clear --json\n' > .omx/context/x.sh && source .omx/context/x.sh", ], [ "bash-redirect", "printf 'omx state clear --json\n' > .omx/context/x.sh && bash .omx/context/x.sh", ], [ "direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && ./.omx/context/x.sh", ], [ "time-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && time ./.omx/context/x.sh", ], [ "command-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && command ./.omx/context/x.sh", ], [ "env-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && env FOO=bar ./.omx/context/x.sh", ], [ "timeout-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && timeout 5 ./.omx/context/x.sh", ], [ "nohup-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && nohup ./.omx/context/x.sh", ], [ "xargs-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && xargs ./.omx/context/x.sh", ], [ "coproc-direct-exec", "printf '#!/bin/sh\nomx state clear --json\n' > .omx/context/x.sh && chmod +x .omx/context/x.sh && coproc ./.omx/context/x.sh", ], [ "sh-c-generated-script", "printf 'omx state clear --json\n' > .omx/context/x.sh && sh -c '. .omx/context/x.sh'", ], [ "sh-c-at-generated-script", "printf 'omx state clear --json\n' > .omx/context/x.sh && sh -c '. \"$@\"' ignored .omx/context/x.sh", ], [ "sh-c-positional-generated-script", "printf 'omx state clear --json\n' > .omx/context/x.sh && sh -c '. \"$0\"' .omx/context/x.sh", ], [ "source-tee", "printf 'omx state clear --json\n' | tee .omx/context/x.sh >/dev/null && source .omx/context/x.sh", ], [ "source-variable", 'tmp=.omx/context/x.sh; printf \'omx state clear --json\n\' > "$tmp"; source "$tmp"', ], [ "source-tee-second-target", "printf 'omx state clear --json\n' | tee .omx/context/x.sh .omx/context/y.sh >/dev/null && source .omx/context/y.sh", ], [ "bash-tee-append-second-target", "printf 'omx state clear --json\n' | tee -a .omx/context/x.sh .omx/context/y.sh >/dev/null && bash .omx/context/y.sh", ], ] as const; for (const [name, command] of blockedSourceGeneratedScriptCommands) { const blockedSourceGeneratedScript = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: `tool-di-state-cli-generated-script-${name}`, tool_input: { command }, }, { cwd }, ); assert.equal( ( blockedSourceGeneratedScript.outputJson as { decision?: string; } | null )?.decision, "block", `${command} should fail closed because it executes a same-command generated script`, ); } const allowedStateRead = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-file-read", tool_input: { command: "cat .omx/state/skill-active-state.json" }, }, { cwd }, ); assert.equal(allowedStateRead.outputJson, null); const allowedQuotedStateWriteMention = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-quoted-mention", tool_input: { command: 'printf \'%s\\n\' "omx state write --input \'{\\"mode\\":\\"deep-interview\\",\\"active\\":false}\'"', }, }, { cwd }, ); assert.equal(allowedQuotedStateWriteMention.outputJson, null); const allowedHeredocStateWriteMention = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-heredoc-mention", tool_input: { command: 'cat > .omx/context/state-example.md <<\'EOF\'\nomx state write --input \'{"mode":"deep-interview","active":false}\'\nEOF', }, }, { cwd }, ); assert.equal(allowedHeredocStateWriteMention.outputJson, null); const blockedUnquotedHeredocSubstitution = await preToolUse( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-artifact", tool_name: "Bash", tool_use_id: "tool-di-state-cli-unquoted-heredoc-substitution", tool_input: { command: "cat > .omx/context/state-example.md < .omx/context/state-example.md < src/scripts/__tests__/codex-native-hook.test.ts <<'EOF'\nexport const x = 1;\nEOF", }, }, { cwd }, ); assert.equal( (blockedBash.outputJson as { decision?: string } | null)?.decision, "block", ); } finally { await rm(cwd, { recursive: true, force: true }); } })); it("allows canonical leader ralplan complete terminal state writes while blocking partial deactivation writes", async () => withCleanAmbientNodeRuntimeEnvironment(async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-ralplan-state-input-file-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-ralplan-input-file"); const leaderThreadId = "thread-ralplan-input-file"; await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-ralplan-input-file", cwd, leader_thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-ralplan-input-file": { session_id: "sess-ralplan-input-file", leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: "sess-ralplan-input-file", active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: "sess-ralplan-input-file", }, ], }); await writeJson(join(sessionDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: "sess-ralplan-input-file", }); const allowedPayload = join(cwd, "ralplan-allowed-state.json"); await writeJson(allowedPayload, { mode: "ralplan", current_phase: "critic-review", active: true, }); const allowed = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-input-file-allowed", tool_input: { command: `omx state write --input-file ${allowedPayload} --json`, }, }, { cwd }, ); assert.equal(allowed.outputJson?.decision, "block"); const blockedPayload = join(cwd, "ralplan-blocked-state.json"); await writeJson(blockedPayload, { mode: "ralplan", current_phase: "complete", }); const blocked = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-input-file-blocked", tool_input: { command: `omx state write --input-file ${blockedPayload} --json`, }, }, { cwd }, ); assert.equal( (blocked.outputJson as { decision?: string } | null)?.decision, "block", ); const terminalPayload = join(cwd, "ralplan-terminal-state.json"); await writeJson(terminalPayload, { mode: "ralplan", active: false, current_phase: "complete", session_id: "sess-ralplan-input-file", terminal_reason: "consensus approved bounded no-op", }); const allowedTerminalFile = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-input-file-terminal-allowed", tool_input: { command: `omx state write --input-file ${terminalPayload} --json`, }, }, { cwd }, ); assert.equal(allowedTerminalFile.outputJson?.decision, "block"); const mismatchedTerminalPayload = join( cwd, "ralplan-terminal-state-mismatched.json", ); await writeJson(mismatchedTerminalPayload, { mode: "ralplan", active: false, current_phase: "complete", session_id: "other-session", }); const blockedMismatchedTerminalFile = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-input-file-terminal-mismatched", tool_input: { command: `omx state write --input-file ${mismatchedTerminalPayload} --json`, }, }, { cwd }, ); assert.equal( ( blockedMismatchedTerminalFile.outputJson as { decision?: string; } | null )?.decision, "block", ); const terminalCurrentPhaseAliases = [ "finish", "finished", "complete", "completed", "done", "blocked", "blocked-on-user", "blocked_on_user", "failed", "fail", "error", "cancelled", "canceled", "cancel", "aborted", "abort", "userinterlude", "user-interlude", "interrupted", "interrupt", "askuserquestion", "ask-user-question", "askuser", "question", ] as const; for (const alias of terminalCurrentPhaseAliases) { const blockedAliasPayload = join( cwd, `ralplan-blocked-state-${alias}.json`, ); await writeJson(blockedAliasPayload, { mode: "ralplan", current_phase: alias, }); const blockedAliasFile = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: `tool-ralplan-state-input-file-blocked-alias-${alias}`, tool_input: { command: `omx state write --input-file ${blockedAliasPayload} --json`, }, }, { cwd }, ); assert.equal( (blockedAliasFile.outputJson as { decision?: string } | null) ?.decision, "block", `${alias} current_phase input-file write should deactivate protected ralplan planning`, ); } const blockedModeFlagTerminal = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-mode-flag-terminal", tool_input: { command: 'omx state write --mode ralplan --input \'{"current_phase":"complete"}\' --json', }, }, { cwd }, ); assert.equal( (blockedModeFlagTerminal.outputJson as { decision?: string } | null) ?.decision, "block", ); const allowedModeFlagTerminal = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: "tool-ralplan-state-mode-flag-terminal-allowed", tool_input: { command: `omx state write --mode ralplan --input '${JSON.stringify({ active: false, current_phase: "complete", session_id: "sess-ralplan-input-file", workingDirectory: cwd })}' --json`, }, }, { cwd }, ); assert.equal(allowedModeFlagTerminal.outputJson, null); const ralplanTerminalPayload = JSON.stringify({ active: false, current_phase: "complete", session_id: "sess-ralplan-input-file", workingDirectory: cwd }); for (const [name, command] of [ ["node require", `node --require .omx/context/preload.cjs dist/cli/omx.js state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["node attached require", `node -r.omx/context/preload.cjs dist/cli/omx.js state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["node import", `node --import=.omx/context/preload.mjs dist/cli/omx.js state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["node loader", `node --loader .omx/context/loader.mjs dist/cli/omx.js state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["NODE_OPTIONS", `NODE_OPTIONS='--require .omx/context/preload.cjs' node dist/cli/omx.js state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["bun preload", `bun --preload .omx/context/preload.ts dist/cli/omx.js state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["direct OMX NODE_OPTIONS", `NODE_OPTIONS='--require .omx/context/preload.cjs' omx state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ["direct OMX coverage output", `NODE_V8_COVERAGE=src omx state write --mode ralplan --input '${ralplanTerminalPayload}' --json`], ] as const) { const blockedRuntimePreload = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: `tool-ralplan-runtime-preload-${name}`, tool_input: { command }, }, { cwd }); assert.equal(blockedRuntimePreload.outputJson?.decision, "block", name); } const previousNodeOptions = process.env.NODE_OPTIONS; try { process.env.NODE_OPTIONS = "--require .omx/context/preload.cjs"; const blockedInheritedRuntime = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-inherited-node-options", tool_input: { command: `omx state write --mode ralplan --input '${ralplanTerminalPayload}' --json` }, }, { cwd }); assert.equal(blockedInheritedRuntime.outputJson?.decision, "block"); } finally { if (previousNodeOptions === undefined) delete process.env.NODE_OPTIONS; else process.env.NODE_OPTIONS = previousNodeOptions; } const blockedTerminalThenImplementationWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-terminal-then-implementation-write", tool_input: { command: 'omx state write --mode ralplan --input \'{"active":false,"current_phase":"complete"}\' --json && printf bad > src/leak.ts', }, }, { cwd }, ); assert.equal( ( blockedTerminalThenImplementationWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); for (const [toolUseId, suffix] of [ ["tool-ralplan-state-terminal-then-touch", "&& touch src/leak.ts"], ["tool-ralplan-state-terminal-then-rm", "; rm src/leak.ts"], ["tool-ralplan-state-terminal-then-readonly", "&& pwd"], [ "tool-ralplan-state-terminal-command-substitution", "$(touch src/leak.ts)", ], [ "tool-ralplan-state-terminal-backtick-substitution", "`touch src/leak.ts`", ], [ "tool-ralplan-state-terminal-process-substitution", "<(touch src/leak.ts)", ], ] as const) { const blockedTerminalThenSuffix = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: toolUseId, tool_input: { command: `omx state write --mode ralplan --input '{"active":false,"current_phase":"complete"}' --json ${suffix}`, }, }, { cwd }, ); assert.equal( (blockedTerminalThenSuffix.outputJson as { decision?: string } | null) ?.decision, "block", `${suffix} suffix should keep ralplan terminal state writes standalone`, ); } for (const alias of terminalCurrentPhaseAliases) { const blockedModeFlagAlias = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: `tool-ralplan-state-mode-flag-terminal-alias-${alias}`, tool_input: { command: `omx state write --mode ralplan --input '${JSON.stringify({ current_phase: alias })}' --json`, }, }, { cwd }, ); assert.equal( (blockedModeFlagAlias.outputJson as { decision?: string } | null) ?.decision, "block", `${alias} current_phase --input write should deactivate protected ralplan planning`, ); } const terminalOutcomeAliasPayloads = [ { runOutcome: "done" }, { lifecycleOutcome: "blocked" }, { terminalOutcome: "aborted" }, ] as const; for (const [ index, aliasPayload, ] of terminalOutcomeAliasPayloads.entries()) { const blockedModeFlagOutcomeAlias = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: `tool-ralplan-state-mode-flag-terminal-outcome-${index}`, tool_input: { command: `omx state write --mode ralplan --input '${JSON.stringify(aliasPayload)}' --json`, }, }, { cwd }, ); assert.equal( ( blockedModeFlagOutcomeAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${Object.keys(aliasPayload)[0]} --input write should deactivate protected ralplan planning`, ); } const blockedOutcomeAliasPayload = join( cwd, "ralplan-blocked-state-camel-terminal-outcome.json", ); await writeJson(blockedOutcomeAliasPayload, { mode: "ralplan", terminalOutcome: "finished", }); const blockedOutcomeAliasFile = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-input-file-blocked-camel-terminal-outcome", tool_input: { command: `omx state write --input-file ${blockedOutcomeAliasPayload} --json`, }, }, { cwd }, ); assert.equal( (blockedOutcomeAliasFile.outputJson as { decision?: string } | null) ?.decision, "block", ); const allowedModeFlagSafe = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "Bash", tool_use_id: "tool-ralplan-state-mode-flag-safe", tool_input: { command: 'omx state write --mode ralplan --input \'{"current_phase":"critic-review","active":true}\' --json', }, }, { cwd }, ); assert.equal(allowedModeFlagSafe.outputJson?.decision, "block"); const blockedRalplanMcpStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-ralplan-mcp-state-write-terminal", tool_input: { mode: "ralplan", lifecycle_outcome: "finished" }, }, { cwd }, ); assert.equal( (blockedRalplanMcpStateWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedNestedRalplanMcpStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-ralplan-mcp-state-write-nested-terminal", tool_input: { mode: "ralplan", state: { current_phase: "complete" } }, }, { cwd }, ); assert.equal( ( blockedNestedRalplanMcpStateWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); for (const alias of terminalCurrentPhaseAliases) { const blockedNestedRalplanMcpAlias = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-ralplan-mcp-state-write-nested-terminal-alias-${alias}`, tool_input: { mode: "ralplan", state: { currentPhase: alias } }, }, { cwd }, ); assert.equal( ( blockedNestedRalplanMcpAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${alias} currentPhase MCP write should deactivate protected ralplan planning`, ); } for (const [ index, aliasPayload, ] of terminalOutcomeAliasPayloads.entries()) { const blockedNestedRalplanMcpOutcomeAlias = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "mcp__omx_state__state_write", tool_use_id: `tool-ralplan-mcp-state-write-nested-terminal-outcome-${index}`, tool_input: { mode: "ralplan", state: aliasPayload }, }, { cwd }, ); assert.equal( ( blockedNestedRalplanMcpOutcomeAlias.outputJson as { decision?: string; } | null )?.decision, "block", `${Object.keys(aliasPayload)[0]} MCP write should deactivate protected ralplan planning`, ); } const allowedNestedRalplanMcpStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-ralplan-mcp-state-write-nested-safe", tool_input: { mode: "ralplan", state: { current_phase: "critic-review", active: true }, session_id: "sess-ralplan-input-file", workingDirectory: cwd, }, }, { cwd }, ); assert.equal(allowedNestedRalplanMcpStateWrite.outputJson?.decision, "block"); assert.match(String(allowedNestedRalplanMcpStateWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const blockedRalplanMcpStateClear = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-input-file", tool_name: "mcp__omx_state__state_clear", tool_use_id: "tool-ralplan-mcp-state-clear", tool_input: { mode: "ralplan" }, }, { cwd }, ); assert.equal( (blockedRalplanMcpStateClear.outputJson as { decision?: string } | null) ?.decision, "block", ); } finally { await rm(cwd, { recursive: true, force: true }); } })); it("emits hook-specific deny ralplan PreToolUse JSON for wrapped implementation writes on the live CLI path", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-ralplan-wrapper-live-"), ); const sessionId = "sess-cli-ralplan-wrapper-live"; const stateDir = join(cwd, ".omx", "state"); const targetPath = join( cwd, "src", "scripts", "__tests__", "codex-native-hook.test.ts", ); try { await mkdir(dirname(targetPath), { recursive: true }); await writeFile(targetPath, "seed\n", "utf-8"); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, }); await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }, ], }, ); await writeJson( join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: sessionId, }, ); await writeCanonicalLeaderFixture( stateDir, sessionId, "thread-cli-ralplan-wrapper-live", cwd, ); const result = runNativeHookCliResult( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-cli-ralplan-wrapper-live", agent_id: "thread-cli-ralplan-wrapper-live", tool_name: "Bash", tool_input: { command: "bash -lc \"cat > src/scripts/__tests__/codex-native-hook.test.ts <<'EOF'\nexport const wrappedRalplanMutation = true;\nEOF\"", }, }, { cwd }, ); assert.equal(result.status, 0, result.stderr || result.stdout); const output = parseSingleJsonStdout(result.stdout); const hookSpecificOutput = output.hookSpecificOutput as Record< string, unknown >; assert.deepEqual(Object.keys(output).sort(), ["hookSpecificOutput"]); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /Ralplan is active \(phase: critic-review\)/, ); assert.match( String(hookSpecificOutput.permissionDecisionReason ?? ""), /implementation\/write tools are blocked/, ); assert.match( String(hookSpecificOutput.additionalContext ?? ""), /Write only planning artifacts/, ); assert.equal(output.decision, undefined); assert.equal(output.reason, undefined); assert.equal(output.systemMessage, undefined); assert.equal(await readFile(targetPath, "utf-8"), "seed\n"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks MCP state_clear for standalone protected planning modes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-standalone-mcp-clear-"), ); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", active_skills: [ { skill: "deep-interview", phase: "planning", active: true }, ], }); await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const blockedDeepInterviewClear = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "mcp__omx_state__state_clear", tool_use_id: "tool-standalone-di-mcp-clear", tool_input: { mode: "deep-interview" }, }, { cwd }, ); assert.equal( (blockedDeepInterviewClear.outputJson as { decision?: string } | null) ?.decision, "block", ); await writeJson(join(stateDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", active_skills: [{ skill: "ralplan", phase: "planning", active: true }], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", }); const blockedRalplanClear = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "mcp__omx_state__state_clear", tool_use_id: "tool-standalone-ralplan-mcp-clear", tool_input: { mode: "ralplan" }, }, { cwd }, ); assert.equal( (blockedRalplanClear.outputJson as { decision?: string } | null) ?.decision, "block", ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows read-only diagnostics mentioning apply_patch while deep-interview blocks real apply_patch invocations", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-pretool-deep-interview-grep-apply-patch-", ), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-grep"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-grep", cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-grep", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-grep", }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-grep", }); const allowedGrep = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: "tool-di-grep", tool_input: { command: 'grep -n "apply_patch" dist/scripts/codex-native-hook.js', }, }, { cwd }, ); assert.equal(allowedGrep.outputJson, null); const allowedSubshellGrep = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: "tool-di-grep-subshell", tool_input: { command: '(grep -n "apply_patch" dist/scripts/codex-native-hook.js)', }, }, { cwd }, ); assert.equal(allowedSubshellGrep.outputJson, null); // Double-quoted spans that merely mention the literal token, expand a // parameter, or run a substitution that is not `apply_patch` stay allowed // — the quoted-substitution fix must not over-block read-only diagnostics. const allowedQuotedMention = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: "tool-di-grep-quoted-mention", tool_input: { command: 'echo "${apply_patch} $(echo apply_patch)"' }, }, { cwd }, ); assert.equal(allowedQuotedMention.outputJson, null); const blockedApplyPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: "tool-di-apply-patch-invoke", tool_input: { command: "apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF", }, }, { cwd }, ); assert.equal( (blockedApplyPatch.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedEnvAssignmentApplyPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: "tool-di-apply-patch-env-assignment", tool_input: { command: "FOO=bar apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF", }, }, { cwd }, ); assert.equal( ( blockedEnvAssignmentApplyPatch.outputJson as { decision?: string; } | null )?.decision, "block", ); const blockedEnvWrapperApplyPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: "tool-di-apply-patch-env-wrapper", tool_input: { command: "env FOO=bar apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF", }, }, { cwd }, ); assert.equal( (blockedEnvWrapperApplyPatch.outputJson as { decision?: string } | null) ?.decision, "block", ); const heredocBody = "\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF"; const blockedRealApplyPatchForms: Array<{ id: string; command: string }> = [ { id: "tool-di-apply-patch-env-i", command: `env -i apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-env-unset", command: `env -u FOO apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-env-i-assignment", command: `env -i FOO=bar apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-assignment-env", command: `FOO=bar env apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-exec-env-assignment", command: `exec env FOO=bar apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-absolute-path", command: `/usr/bin/apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-relative-path", command: `./apply_patch <<'EOF'${heredocBody}`, }, { id: "tool-di-apply-patch-subshell", command: `(apply_patch <<'EOF'${heredocBody}\n)`, }, { id: "tool-di-apply-patch-subshell-spaced", command: `( apply_patch <<'EOF'${heredocBody}\n)`, }, { id: "tool-di-apply-patch-double-subshell", command: `((apply_patch <<'EOF'${heredocBody}\n))`, }, { id: "tool-di-apply-patch-pipe-subshell", command: `true | (apply_patch <<'EOF'${heredocBody}\n)`, }, { id: "tool-di-apply-patch-subshell-env", command: `(env apply_patch <<'EOF'${heredocBody}\n)`, }, { id: "tool-di-apply-patch-command-substitution", command: `x=$(apply_patch <<'EOF'${heredocBody}\n)`, }, { id: "tool-di-apply-patch-brace-group", command: `{ apply_patch <<'EOF'${heredocBody}\n}`, }, // Command substitution runs even inside double quotes, so quoting the // already-blocked `$(…)` / `` `…` `` form must not bypass the guard. { id: "tool-di-apply-patch-quoted-command-substitution", command: `echo "$(apply_patch <<'EOF'${heredocBody}\n)"`, }, { id: "tool-di-apply-patch-quoted-backtick", command: `echo "\`apply_patch <<'EOF'${heredocBody}\`"`, }, { id: "tool-di-apply-patch-quoted-command-substitution-prefixed", command: `echo "patched: $(apply_patch <<'EOF'${heredocBody}\n) done"`, }, ]; for (const form of blockedRealApplyPatchForms) { const blockedForm = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-grep", tool_name: "Bash", tool_use_id: form.id, tool_input: { command: form.command }, }, { cwd }, ); assert.equal( (blockedForm.outputJson as { decision?: string } | null)?.decision, "block", `expected deep-interview to block real apply_patch form: ${form.command}`, ); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks operator-adjacent transport bypass forms while still allowing a safe direct-wrapper state write", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-pretool-deep-interview-transport-bypass-", ), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-di-transport-bypass"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: "thread-di-transport-bypass", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, thread_id: "thread-di-transport-bypass", }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, thread_id: "thread-di-transport-bypass", rounds: [ { answer: "Use CLI wrapper normalization for protected planning state commands.", }, ], }); await writeCanonicalLeaderFixture(stateDir, sessionId, "thread-di-transport-bypass", cwd); const preToolUse = (tool_use_id: string, command: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-di-transport-bypass", agent_id: "thread-di-transport-bypass", tool_name: "Bash", tool_use_id, tool_input: { command }, }, { cwd }, ); const absoluteCliEntry = resolve(cwd, "dist/cli/omx.js"); const blockedCommands = [ `printf 'omx state clear --json'|bash`, `printf 'omx state clear --json'|&bash`, `bash<<<'omx state clear --json'`, `bash<<'EOF'\nomx state clear --json\nEOF`, `bash< <(printf 'omx state clear --json')`, `env -S "bash -c 'omx state clear --json'"`, `env --split-string "bash -c 'omx state clear --json'"`, `env -S "node dist/cli/omx.js state clear --json"`, `env --split-string "node dist/cli/omx.js state clear --json"`, `env -S "dist/cli/omx.js state clear --json"`, `env --split-string "dist/cli/omx.js state clear --json"`, `node dist/cli/omx.js state clear --json`, `node ./dist/cli/omx.js state clear --json`, `node ${absoluteCliEntry} state clear --json`, `bun dist/cli/omx.js state clear --json`, `tsx src/cli/omx.ts state clear --json`, `dist/cli/omx.js state clear --json`, `./dist/cli/omx.js state clear --json`, `exec node dist/cli/omx.js state clear --json`, `command node dist/cli/omx.js state clear --json`, `command env VAR=x node dist/cli/omx.js state clear --json`, `env VAR=x node dist/cli/omx.js state clear --json`, `exec env -S "node dist/cli/omx.js state clear --json"`, `node dist/cli/omx.js state write --input '{"mode":"deep-interview","active":true}' --json`, ]; for (const [index, command] of blockedCommands.entries()) { const blocked = await preToolUse( `tool-di-transport-bypass-block-${index}`, command, ); assert.equal( (blocked.outputJson as { decision?: string } | null)?.decision, "block", `expected transport guard to block: ${command}`, ); } const allowedCommands = [ `echo '|& <<< <<'`, `node dist/cli/omx.js state write --input '${JSON.stringify({ mode: "deep-interview", active: true, session_id: sessionId, workingDirectory: cwd })}' --json`, ]; for (const [index, command] of allowedCommands.entries()) { const allowed = await preToolUse( `tool-di-transport-bypass-allow-${index}`, command, ); assert.equal( allowed.outputJson, null, `expected safe transport form to remain allowed: ${command}`, ); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows direct literal planning redirects while blocking all variable redirect targets", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-deep-interview-var-redirect-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-var-redirect"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-var-redirect", cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-var-redirect", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-var-redirect", }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-var-redirect", }); await writeCanonicalLeaderFixture( stateDir, "sess-di-var-redirect", "thread-di-var-redirect", cwd, ); const allowedLiteralRedirect = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: "tool-di-literal-redirect-allow", tool_input: { command: "cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF" }, }, { cwd }); assert.equal(allowedLiteralRedirect.outputJson, null); for (const [name, command] of [ ["printf builtin", "printf safe > .omx/context/printf.md"], ["echo builtin", "echo safe > .omx/context/echo.md"], ["colon builtin", ": > .omx/context/empty.md"], ] as const) { const allowedBuiltinProducer = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: `tool-di-redirect-producer-positive-${name}`, tool_input: { command }, }, { cwd }); assert.equal(allowedBuiltinProducer.outputJson, null, name); } for (const [name, command] of [ ["unknown producer", "./mutator > .omx/context/literal.md"], ["shadowed cat producer", "cat(){ touch src/owned.ts; command cat \"$@\"; }; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["PATH assignment shadowed cat", "PATH=\"$PWD/.omx/tmp/bin:/usr/bin:/bin\"; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["exported PATH shadowed cat", "export PATH=\"$PWD:$PATH\"; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["quoted exported PATH shadowed cat", "export 'PATH=$PWD:$PATH'; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["env PATH shadowed cat", "env PATH=\"$PWD:$PATH\" cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["command-prefix PATH shadowed printf", "PATH=\"$PWD:$PATH\" printf safe > .omx/context/literal.md"], ["readonly PATH shadowed echo", "readonly PATH=\"$PWD:$PATH\"; echo safe > .omx/context/literal.md"], ["printf-v PATH shadowed cat", "printf -v PATH '%s' \"$PWD:$PATH\"; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["shadowed printf producer", "printf(){ touch src/owned.ts; }; printf safe > .omx/context/literal.md"], ["shadowed echo producer", "alias echo='touch src/owned.ts'; echo safe > .omx/context/literal.md"], ["disabled printf builtin", "enable -n printf; printf safe > .omx/context/literal.md"], ["disabled echo builtin", "builtin enable -n echo; echo safe > .omx/context/literal.md"], ["command-disabled printf builtin", "command enable -n printf; printf safe > .omx/context/literal.md"], ["wrapped command-disabled echo builtin", "command builtin enable -n echo; echo safe > .omx/context/literal.md"], ["repeated command-disabled printf builtin", "command command enable -n printf; printf safe > .omx/context/literal.md"], ["option-wrapped command-disabled printf builtin", "command -- enable -n printf; printf safe > .omx/context/literal.md"], ["hashed printf producer", "hash -p .omx/context/evil printf; printf safe > .omx/context/literal.md"], ["declared command cache producer", "declare 'BASH_CMDS[cat]=.omx/context/evil'; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["escaped declared command cache producer", "declare BASH\\_CMDS[cat]=.omx/context/evil; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["nameref command cache producer", "declare -n cache=BASH_CMDS; declare 'cache[cat]=.omx/context/evil'; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["wrapped declared command cache producer", "command builtin declare 'BASH_CMDS[cat]=.omx/context/evil'; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["fragmented wrapped command cache producer", "builtin d'e'clare \"BASH_C\"\"MDS[cat]=.omx/context/evil\"; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["fragmented wrapped PATH producer", "command t'y'peset 'PA'\"TH[0]=.omx/context\"; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["assigned command cache producer", "BASH_CMDS[printf]=.omx/context/evil; printf safe > .omx/context/literal.md"], ["subscripted PATH producer", "declare 'PATH[0]=.omx/context'; cat > .omx/context/literal.md <<'EOF'\ncontent\nEOF"], ["unset command cache producer", "unset 'BASH_CMDS[printf]'; printf safe > .omx/context/literal.md"], ["shadowed sed mutation", "sed(){ touch src/owned.ts; }; sed -i 's/a/b/' .omx/context/literal.md"], ["compound unknown mutation", "./mutator; printf safe > .omx/context/literal.md"], ] as const) { const blockedProducer = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: `tool-di-redirect-producer-${name}`, tool_input: { command }, }, { cwd }); assert.equal(blockedProducer.outputJson?.decision, "block", name); } const allowedVarRedirect = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: "tool-di-var-redirect-allow", tool_input: { command: 'SNAP=".omx/context/example.md"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF', }, }, { cwd }, ); assert.equal(allowedVarRedirect.outputJson?.decision, "block"); const blockedVarRedirect = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: "tool-di-var-redirect-block", tool_input: { command: 'SNAP="src/leak.ts"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF', }, }, { cwd }, ); assert.equal( (blockedVarRedirect.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedReassignedRedirect = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: "tool-di-var-redirect-reassigned", tool_input: { command: 'SNAP="src/leak.ts"; cat > "$SNAP" <<\'EOF\'\ncontent\nEOF\nSNAP=".omx/context/example.md"', }, }, { cwd }); assert.equal(blockedReassignedRedirect.outputJson?.decision, "block"); const blockedLaterRebindRedirect = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: "tool-di-var-redirect-later-rebind", tool_input: { command: 'SNAP=".omx/context/example.md"; :; SNAP="src/leak.ts"; printf x > "$SNAP"', }, }, { cwd }); assert.equal(blockedLaterRebindRedirect.outputJson?.decision, "block"); for (const [name, command] of [ ["printf-v rebind", 'SNAP=".omx/context/example.md"; printf -v SNAP %s src/leak.ts; printf x > "$SNAP"'], ["quoted printf-v rebind", 'SNAP=".omx/context/example.md"; printf -v \'SNAP\' %s src/leak.ts; printf x > "$SNAP"'], ["append rebind", 'SNAP=".omx/context"; SNAP+=/../../src/leak.ts; printf x > "$SNAP"'], ["mapfile rebind", 'SNAP=".omx/context/example.md"; mapfile -t SNAP <<< src/leak.ts; printf x > "$SNAP"'], ["readarray rebind", 'SNAP=".omx/context/example.md"; readarray -t SNAP <<< src/leak.ts; printf x > "$SNAP"'], ["getopts rebind", 'SNAP=".omx/context/example.md"; getopts x SNAP -x src/leak.ts; printf x > "$SNAP"'], ["for rebind", 'SNAP=".omx/context/example.md"; for SNAP in src/leak.ts; do printf x > "$SNAP"; done'], ["select rebind", 'SNAP=".omx/context/example.md"; select SNAP in src/leak.ts; do printf x > "$SNAP"; break; done'], ["parameter assignment rebind", 'SNAP=".omx/context/example.md"; : "${SNAP:=src/leak.ts}"; printf x > "$SNAP"'], ["function hash producer", "poison(){ hash -p /tmp/evil cat; }; poison; cat > .omx/context/example.md <<'EOF'\nx\nEOF"], ["function enable producer", "poison(){ enable -f /tmp/evil.so cat; }; poison; cat > .omx/context/example.md <<'EOF'\nx\nEOF"], ["function cache binding producer", "poison(){ declare BASH_CMDS[cat]=/tmp/evil; }; poison; cat > .omx/context/example.md <<'EOF'\nx\nEOF"], ["context write then execute", "printf 'touch src/owned.ts\\n' > .omx/context/payload.txt; bash .omx/context/payload.txt"], ["late tmp binding execute", "printf 'touch src/owned.ts\\n' > .omx/tmp/payload.txt; S=.omx/tmp/payload.txt; bash \"$S\""], ] as const) { const blockedDynamic = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: `tool-di-var-redirect-${name}`, tool_input: { command }, }, { cwd }); assert.equal(blockedDynamic.outputJson?.decision, "block", name); } const blockedUnresolvedVarRedirect = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-var-redirect", agent_id: "thread-di-var-redirect", thread_id: "thread-di-var-redirect", tool_name: "Bash", tool_use_id: "tool-di-var-redirect-unresolved", tool_input: { command: "cat > \"$SNAP\" <<'EOF'\ncontent\nEOF" }, }, { cwd }, ); assert.equal( ( blockedUnresolvedVarRedirect.outputJson as { decision?: string; } | null )?.decision, "block", ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows deep-interview apply_patch artifact writes from freeform patch text while blocking outside paths", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-deep-interview-apply-patch-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-apply-patch"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-apply-patch", cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-apply-patch", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-apply-patch", }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-apply-patch", }); await writeCanonicalLeaderFixture( stateDir, "sess-di-apply-patch", "thread-di-apply-patch", cwd, ); const allowedAddFile = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "apply_patch", tool_use_id: "tool-di-apply-patch-add", tool_input: { input: "*** Begin Patch\n*** Add File: .omx/context/findings.md\n+# Findings\n*** End Patch\n", }, }, { cwd }, ); assert.equal(allowedAddFile.outputJson, null); const allowedUpdateFile = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "ApplyPatch", tool_use_id: "tool-di-apply-patch-update", tool_input: { input: "*** Begin Patch\n*** Update File: .omx/specs/deep-interview-demo.md\n@@\n-old\n+new\n*** End Patch\n", }, }, { cwd }, ); assert.equal(allowedUpdateFile.outputJson, null); const allowedStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "apply_patch", tool_use_id: "tool-di-apply-patch-state", tool_input: { input: "*** Begin Patch\n*** Add File: .omx/state/deep-interview-notes.json\n+{}\n*** End Patch\n", }, }, { cwd }, ); assert.equal(allowedStateWrite.outputJson, null); const blockedProtectedStatePatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "apply_patch", tool_use_id: "tool-di-apply-patch-protected-state", tool_input: { input: '*** Begin Patch\n*** Update File: .omx/state/sessions/sess-di-apply-patch/deep-interview-state.json\n@@\n-{}\n+{"active":false}\n*** End Patch\n', }, }, { cwd }, ); assert.equal( (blockedProtectedStatePatch.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedOutsidePath = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "apply_patch", tool_use_id: "tool-di-apply-patch-outside", tool_input: { input: "*** Begin Patch\n*** Add File: src/implementation.ts\n+export const x = 1;\n*** End Patch\n", }, }, { cwd }, ); assert.equal( (blockedOutsidePath.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedOutsidePath.outputJson as { reason?: string } | null) ?.reason ?? "", ), /Deep-interview is active/, ); const blockedMixedPaths = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "apply_patch", tool_use_id: "tool-di-apply-patch-mixed", tool_input: { input: "*** Begin Patch\n*** Add File: .omx/context/ok.md\n+ok\n*** Add File: src/leak.ts\n+leak\n*** End Patch\n", }, }, { cwd }, ); assert.equal( (blockedMixedPaths.outputJson as { decision?: string } | null) ?.decision, "block", ); const blockedUnparseablePatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-apply-patch", agent_id: "thread-di-apply-patch", thread_id: "thread-di-apply-patch", tool_name: "apply_patch", tool_use_id: "tool-di-apply-patch-empty", tool_input: { input: "not a recognizable patch" }, }, { cwd }, ); assert.equal( (blockedUnparseablePatch.outputJson as { decision?: string } | null) ?.decision, "block", ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Ralplan read-only inspection and planning artifact writes while blocking implementation targets", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-ralplan-guard-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-guard"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); await writeCanonicalLeaderFixture( stateDir, sessionId, "thread-ralplan-guard", cwd, ); const preToolUse = async ( tool_name: string, tool_use_id: string, tool_input: Record, ) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-guard", agent_id: "thread-ralplan-guard", tool_name, tool_use_id, tool_input, }, { cwd }, ); const omxCommand = await withTrustedWorkspaceOmxCli(cwd, async (command) => command); const readOnlyCommands = [ "git status --short", "ls -1 .omx/plans", "find .omx/plans -type f -name '*.md'", 'grep -RIn "Scholastic" doc tests .omx/plans .omx/context', "sed -n '1,80p' .omx/plans/demo.md", "cat .omx/plans/demo.md", 'git status --short; ls -1 .omx/plans; grep -RIn "Scholastic" doc tests .omx/plans .omx/context', ]; for (const [index, command] of readOnlyCommands.entries()) { const result = await preToolUse("Bash", `tool-ralplan-read-${index}`, { command, }); assert.equal( result.outputJson, null, `read-only command should be allowed: ${command}`, ); } for (const path of [ ".omx/context/findings.md", ".omx/plans/issue-2863.md", ".omx/drafts/issue-3105.md", ".omx/specs/issue-2863.md", ".omx/state/required-planning-state.json", ]) { const writeResult = await preToolUse( "Write", `tool-ralplan-write-${path}`, { file_path: path, content: "ok" }, ); assert.equal( writeResult.outputJson, null, `Write should be allowed for ${path}`, ); } const allowedDraftEdit = await preToolUse( "Edit", "tool-ralplan-draft-edit", { file_path: ".omx/drafts/issue-3105.md", old_string: "old", new_string: "new", }, ); assert.equal(allowedDraftEdit.outputJson, null); for (const path of [ join(cwd, ".omx", "drafts", "issue-3105.md"), ".omx/drafts/subdir/../issue-3105.md", ]) { const normalizedDraftWrite = await preToolUse( "Write", `tool-ralplan-normalized-draft-${path}`, { file_path: path, content: "# Draft", }, ); assert.equal( normalizedDraftWrite.outputJson, null, `normalized draft path should be allowed: ${path}`, ); } for (const protectedPath of [ ".omx/state/sessions/sess-ralplan-guard/ralplan-state.json", ".omx/state/sessions/sess-ralplan-guard/autopilot-state.json", ".omx/state/sessions/sess-ralplan-guard/skill-active-state.json", ]) { const protectedResult = await preToolUse( "Write", `tool-ralplan-protected-${protectedPath}`, { file_path: protectedPath, content: "{}", }, ); assert.equal( (protectedResult.outputJson as { decision?: string } | null) ?.decision, "block", `${protectedPath} should not be model-writable during ralplan`, ); } // A non-deactivating `omx state write` defers to the gate-enforcing // state_write backend (same enforcement for CLI and MCP). const allowedStateCliMutation = await preToolUse( "Bash", "tool-ralplan-state-cli-write", { command: 'omx state write --input \'{"mode":"autopilot","current_phase":"ultragoal"}\' --json', }, ); assert.equal(allowedStateCliMutation.outputJson?.decision, "block"); // Broad deactivation vectors such as `omx state clear` are still rejected // at the transport boundary; complete consensus terminal writes are // covered by the state-write closeout tests. const blockedStateClear = await preToolUse( "Bash", "tool-ralplan-state-cli-clear", { command: "omx state clear --json", }, ); assert.equal( (blockedStateClear.outputJson as { decision?: string } | null) ?.decision, "block", ); const allowedPatchAdd = await preToolUse( "apply_patch", "tool-ralplan-patch-add", { input: "*** Begin Patch\n*** Add File: .omx/plans/issue-2863.md\n+# Plan\n*** End Patch\n", }, ); assert.equal(allowedPatchAdd.outputJson, null); const allowedPatchUpdate = await preToolUse( "ApplyPatch", "tool-ralplan-patch-update", { input: "*** Begin Patch\n*** Update File: .omx/plans/issue-2863.md\n@@\n-old\n+new\n*** End Patch\n", }, ); assert.equal(allowedPatchUpdate.outputJson, null); const allowedDraftPatchAdd = await preToolUse( "apply_patch", "tool-ralplan-draft-patch-add", { input: "*** Begin Patch\n*** Add File: .omx/drafts/issue-3105.md\n+# Draft\n*** End Patch\n", }, ); assert.equal(allowedDraftPatchAdd.outputJson, null); const allowedDraftPatchUpdate = await preToolUse( "ApplyPatch", "tool-ralplan-draft-patch-update", { input: "*** Begin Patch\n*** Update File: .omx/drafts/issue-3105.md\n@@\n-old\n+new\n*** End Patch\n", }, ); assert.equal(allowedDraftPatchUpdate.outputJson, null); const allowedRedirect = await preToolUse( "Bash", "tool-ralplan-redirect-allow", { command: "printf '\\nmore\\n' >> .omx/plans/issue-2863.md", }, ); assert.equal(allowedRedirect.outputJson, null); const allowedDraftRedirect = await preToolUse( "Bash", "tool-ralplan-draft-redirect-allow", { command: "printf '# Draft\\n' > .omx/drafts/issue-3105.md", }, ); assert.equal(allowedDraftRedirect.outputJson, null); const allowedTee = await preToolUse("Bash", "tool-ralplan-tee-allow", { command: "printf '\\nmore\\n' | tee -a .omx/specs/issue-2863.md", }); assert.equal(allowedTee.outputJson, null); const allowedCombinedSedArtifact = await preToolUse( "Bash", "tool-ralplan-sed-combined-artifact-allow", { command: "sed -Ei 's/old/new/' .omx/plans/issue-2863.md", }, ); assert.equal(allowedCombinedSedArtifact.outputJson, null); for (const [toolUseId, command] of [ [ "tool-ralplan-tmp-python-stdin-exec", "python < .omx/tmp/sess-ralplan-guard/run.txt", ], [ "tool-ralplan-tmp-node-stdin-exec", "node < .omx/tmp/sess-ralplan-guard/run.txt", ], [ "tool-ralplan-tmp-ruby-stdin-exec", "ruby < .omx/tmp/sess-ralplan-guard/run.txt", ], [ "tool-ralplan-tmp-perl-stdin-exec", "perl < .omx/tmp/sess-ralplan-guard/run.txt", ], [ "tool-ralplan-tmp-sh-stdin-exec", "sh < .omx/tmp/sess-ralplan-guard/run.txt", ], [ "tool-ralplan-tmp-bash-stdin-exec", "bash < .omx/tmp/sess-ralplan-guard/run.txt", ], [ "tool-ralplan-tmp-same-command-stdin-exec", [ "python3 - <<'PY'", "from pathlib import Path", "Path('.omx/tmp/sess-ralplan-guard/run.txt').write_text('print(1)')", "PY", "python < .omx/tmp/sess-ralplan-guard/run.txt", ].join("\n"), ], ] as const) { const blockedTmpStdin = await preToolUse("Bash", toolUseId, { command, }); assert.equal( (blockedTmpStdin.outputJson as { decision?: string } | null) ?.decision, "block", command, ); assert.match( JSON.stringify(blockedTmpStdin.outputJson), /generated-script transport|\.omx\/tmp/, ); } const allowedReadOnlyEditors = await preToolUse( "Bash", "tool-ralplan-read-only-editors-allow", { command: "sed -n '1,20p' src/runtime.ts; perl -ne 'print if $. < 3' src/runtime.ts", }, ); assert.equal(allowedReadOnlyEditors.outputJson, null); const blockedCombinedSedSource = await preToolUse( "Bash", "tool-ralplan-sed-combined-source-block", { command: "sed -Ei 's/old/new/' src/runtime.ts", }, ); assert.equal( (blockedCombinedSedSource.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedCombinedSedSource.outputJson as { reason?: string } | null) ?.reason ?? "", ), /src\/runtime\.ts/, ); const blockedRedirect = await preToolUse( "Bash", "tool-ralplan-redirect-block", { command: "printf 'bad' > src/implementation.ts", }, ); assert.equal( (blockedRedirect.outputJson as { decision?: string } | null)?.decision, "block", ); const redirectReason = String( (blockedRedirect.outputJson as { reason?: string } | null)?.reason ?? "", ); assert.match(redirectReason, /Bash redirect write/); assert.match(redirectReason, /src\/implementation\.ts/); const blockedEdit = await preToolUse("Edit", "tool-ralplan-edit-block", { file_path: "src/implementation.ts", old_string: "a", new_string: "b", }); assert.equal( (blockedEdit.outputJson as { decision?: string } | null)?.decision, "block", ); const editReason = String( (blockedEdit.outputJson as { reason?: string } | null)?.reason ?? "", ); assert.match(editReason, /Edit path/); assert.match(editReason, /src\/implementation\.ts/); const blockedEditContext = String( ( blockedEdit.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } | null )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match( blockedEditContext, /Markdown drafts under `\.omx\/drafts\/\*\.md`/, ); for (const allowedCategory of [ ".omx/context/", ".omx/plans/", ".omx/specs/", ".omx/tmp/", ".omx/state/", ".beads/", ]) { assert.match( blockedEditContext, new RegExp(allowedCategory.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")), ); } for (const blockedDraftPath of [ ".omx/drafts/issue-3105.MD", ".omx/Drafts/issue-3105.md", ".omx/drafts/issue-3105", ".omx/drafts/run.sh", ".omx/drafts/subdir/issue-3105.md", ".omx/drafts/../../src/leak.ts", "/tmp/issue-3105.md", ]) { const blockedDraft = await preToolUse( "Write", `tool-ralplan-draft-block-${blockedDraftPath}`, { file_path: blockedDraftPath, content: "bad", }, ); assert.equal( (blockedDraft.outputJson as { decision?: string } | null)?.decision, "block", `draft path should be blocked: ${blockedDraftPath}`, ); } const blockedMixedPatch = await preToolUse( "apply_patch", "tool-ralplan-patch-mixed", { input: "*** Begin Patch\n*** Add File: .omx/plans/ok.md\n+ok\n*** Add File: src/leak.ts\n+leak\n*** End Patch\n", }, ); assert.equal( (blockedMixedPatch.outputJson as { decision?: string } | null) ?.decision, "block", ); const mixedReason = String( (blockedMixedPatch.outputJson as { reason?: string } | null)?.reason ?? "", ); assert.match(mixedReason, /apply_patch target/); assert.match(mixedReason, /src\/leak\.ts/); const blockedMixedDraftPatch = await preToolUse( "apply_patch", "tool-ralplan-draft-patch-mixed", { input: "*** Begin Patch\n*** Add File: .omx/drafts/issue-3105.md\n+ok\n*** Add File: src/leak.ts\n+leak\n*** End Patch\n", }, ); assert.equal( (blockedMixedDraftPatch.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedMixedDraftPatch.outputJson as { reason?: string } | null) ?.reason ?? "", ), /src\/leak\.ts/, ); const blockedMixedDraftBash = await preToolUse( "Bash", "tool-ralplan-draft-bash-mixed", { command: "printf '# Draft\\n' > .omx/drafts/issue-3105.md; printf 'bad\\n' > src/leak.ts", }, ); assert.equal( (blockedMixedDraftBash.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedMixedDraftBash.outputJson as { reason?: string } | null) ?.reason ?? "", ), /src\/leak\.ts/, ); const blockedUnparseablePatch = await preToolUse( "apply_patch", "tool-ralplan-patch-unparseable", { input: "not a recognizable patch", }, ); assert.equal( (blockedUnparseablePatch.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedUnparseablePatch.outputJson as { reason?: string } | null) ?.reason ?? "", ), /apply_patch target extraction failed/, ); const blockedUnresolvedRedirect = await preToolUse( "Bash", "tool-ralplan-redirect-unresolved", { command: "cat > \"$PLAN_PATH\" <<'EOF'\ncontent\nEOF", }, ); assert.equal( (blockedUnresolvedRedirect.outputJson as { decision?: string } | null) ?.decision, "block", ); const unresolvedReason = String( (blockedUnresolvedRedirect.outputJson as { reason?: string } | null) ?.reason ?? "", ); assert.match(unresolvedReason, /unresolved Bash write target/); assert.match(unresolvedReason, /\$PLAN_PATH/); const blockedUnresolvedDraftRedirect = await preToolUse( "Bash", "tool-ralplan-draft-redirect-unresolved", { command: "cat > \"$DRAFT_PATH\" <<'EOF'\ncontent\nEOF", }, ); assert.equal( ( blockedUnresolvedDraftRedirect.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedUnresolvedDraftRedirect.outputJson as { reason?: string; } | null )?.reason ?? "", ), /\$DRAFT_PATH/, ); const blockedTraversal = await preToolUse( "Write", "tool-ralplan-traversal-block", { file_path: ".omx/plans/../../src/leak.ts", content: "bad", }, ); assert.equal( (blockedTraversal.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( String( (blockedTraversal.outputJson as { reason?: string } | null)?.reason ?? "", ), /\.omx\/plans\/\.\.\/\.\.\/src\/leak\.ts/, ); const blockedAbsolute = await preToolUse( "Write", "tool-ralplan-absolute-block", { file_path: "/tmp/leak.ts", content: "bad", }, ); assert.equal( (blockedAbsolute.outputJson as { decision?: string } | null)?.decision, "block", ); assert.match( String( (blockedAbsolute.outputJson as { reason?: string } | null)?.reason ?? "", ), /\/tmp\/leak\.ts/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Autopilot ralplan planning artifacts while blocking implementation writes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-autopilot-ralplan-artifact-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join( stateDir, "sessions", "sess-autopilot-ralplan-artifact", ); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-autopilot-ralplan-artifact", cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", active_skills: [ { skill: "autopilot", phase: "ralplan", active: true, session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", }); await writeCanonicalLeaderFixture( stateDir, "sess-autopilot-ralplan-artifact", "thread-autopilot-ralplan-artifact", cwd, ); const preToolUse = async ( tool_name: string, tool_use_id: string, tool_input: Record, ) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", agent_id: "thread-autopilot-ralplan-artifact", tool_name, tool_use_id, tool_input, }, { cwd }, ); const issueReadOnlyEvidenceCommand = [ "printf 'collecting planning evidence\\n'", "ROOT=$(git rev-parse --show-toplevel)", "BASE=$(git merge-base HEAD origin/dev)", "git log --oneline ${BASE}..HEAD", "git diff --name-status ${BASE}..HEAD", "sed -n '1,120p' src/scripts/codex-native-hook.ts", "grep -RIn 'commandHasDeepInterviewWriteIntent' src/scripts/codex-native-hook.ts", "printf '%s\\n' \"$ROOT\"", ].join("; "); const allowedIssueReadOnlyBash = await preToolUse( "Bash", "tool-autopilot-ralplan-read-only-bash", { command: issueReadOnlyEvidenceCommand, }, ); assert.equal(allowedIssueReadOnlyBash.outputJson, null); for (const command of [ "git merge origin/dev", "git reset --hard HEAD", "git checkout-index -f -a", "git merge-file ours base theirs", ]) { const blockedDestructiveGit = await preToolUse( "Bash", `tool-autopilot-ralplan-${command.replace(/[^a-z0-9]+/gi, "-")}`, { command, }, ); assert.equal( (blockedDestructiveGit.outputJson as { decision?: string } | null) ?.decision, "block", `${command} should stay blocked during Autopilot ralplan`, ); } const allowedPlanWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", agent_id: "thread-autopilot-ralplan-artifact", tool_name: "Write", tool_use_id: "tool-autopilot-ralplan-plan-write", tool_input: { file_path: ".omx/plans/prd-omx-y7a.md", content: "# Plan", }, }, { cwd }, ); assert.equal(allowedPlanWrite.outputJson, null); const allowedSpecEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", agent_id: "thread-autopilot-ralplan-artifact", tool_name: "Edit", tool_use_id: "tool-autopilot-ralplan-spec-edit", tool_input: { file_path: ".omx/specs/omx-y7a.md", old_string: "old", new_string: "new", }, }, { cwd }, ); assert.equal(allowedSpecEdit.outputJson, null); const allowedPythonPlanWrite = await preToolUse( "Bash", "tool-autopilot-ralplan-python-plan-write", { command: `python3 - <<'PY' from pathlib import Path Path('.omx/plans').mkdir(parents=True, exist_ok=True) Path('.omx/plans/rebase-pr3010-ultragoal-fix-plan.md').write_text('planning text') PY`, }, ); assert.equal(allowedPythonPlanWrite.outputJson, null); const blockedPythonAllowedMkdirDynamicSourceWrite = await preToolUse( "Bash", "tool-autopilot-ralplan-python-allowed-mkdir-dynamic-source-write", { command: `python3 - <<'PY' from pathlib import Path Path('.omx/plans').mkdir(parents=True, exist_ok=True) (Path('src') / 'generated.ts').write_text('implementation') PY`, }, ); assert.equal( ( blockedPythonAllowedMkdirDynamicSourceWrite.outputJson as { decision?: string; } | null )?.decision, "block", ); assert.match( String( ( blockedPythonAllowedMkdirDynamicSourceWrite.outputJson as { reason?: string; } | null )?.reason ?? "", ), /write intent did not identify an allowed planning artifact path/, ); const blockedPythonSourceWrite = await preToolUse( "Bash", "tool-autopilot-ralplan-python-source-write", { command: `python3 - <<'PY' from pathlib import Path Path('src/generated.ts').write_text('implementation') PY`, }, ); assert.equal( (blockedPythonSourceWrite.outputJson as { decision?: string } | null) ?.decision, "block", ); assert.match( String( (blockedPythonSourceWrite.outputJson as { reason?: string } | null) ?.reason ?? "", ), /src\/generated\.ts/, ); const blockedPythonMixedWrite = await preToolUse( "Bash", "tool-autopilot-ralplan-python-mixed-write", { command: `python3 - <<'PY' from pathlib import Path Path('.omx/plans/rebase-pr3010-ultragoal-fix-plan.md').write_text('planning text') Path('src/generated').mkdir(parents=True, exist_ok=True) PY`, }); assert.equal((blockedPythonMixedWrite.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(String((blockedPythonMixedWrite.outputJson as { reason?: string } | null)?.reason ?? ""), /src\/generated/); const blockedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-ralplan-artifact", thread_id: "thread-autopilot-ralplan-artifact", agent_id: "thread-autopilot-ralplan-artifact", tool_name: "Edit", tool_use_id: "tool-autopilot-ralplan-src-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal((blockedImplementationEdit.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(String((blockedImplementationEdit.outputJson as { reason?: string } | null)?.reason ?? ""), /src\/implementation\.ts/); assert.match(String((blockedImplementationEdit.outputJson as { reason?: string } | null)?.reason ?? ""), /not under allowed planning artifact paths/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Ralplan Beads tracker metadata writes during planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-ralplan-beads-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-ralplan-beads"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-ralplan-beads", cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", }, ], }); await writeJson(join(sessionDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", }); await writeCanonicalLeaderFixture( stateDir, "sess-ralplan-beads", "thread-ralplan-beads", cwd, ); const allowedWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "Write", tool_use_id: "tool-ralplan-beads-write", tool_input: { file_path: ".beads/issues.db", content: "metadata" }, }, { cwd }, ); assert.equal(allowedWrite.outputJson, null); const allowedEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "Edit", tool_use_id: "tool-ralplan-beads-edit", tool_input: { file_path: ".beads/tasks/issue.json", old_string: "old", new_string: "new" }, }, { cwd }, ); assert.equal(allowedEdit.outputJson, null); const allowedPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "apply_patch", tool_use_id: "tool-ralplan-beads-apply-patch", tool_input: { input: "*** Begin Patch\n*** Add File: .beads/tasks/issue.json\n+{}\n*** End Patch\n", }, }, { cwd }, ); assert.equal(allowedPatch.outputJson, null); const allowedCommands = [ "bd --db .beads/issues.db create 'Issue title'", "bd --db .beads/issues.db update OMX-1 --title 'Issue title'", "bd --db .beads/issues.db edit OMX-1 --title 'Issue title'", "bd --db .beads/issues.db comments add OMX-1 'evidence'", "bd --db .beads/issues.db close OMX-1", "bd --db .beads/issues.db reopen OMX-1", "bd --db .beads/issues.db status OMX-1", "bd --db .beads/issues.db dep add OMX-1 OMX-2", ]; for (const [index, command] of allowedCommands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "Bash", tool_use_id: `tool-ralplan-beads-bd-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal(result.outputJson, null, `expected Beads metadata command to be allowed: ${command}`); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks unsafe Beads tracker targets and mixed implementation writes during Autopilot ralplan planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-autopilot-beads-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-autopilot-beads-block"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-autopilot-beads-block", cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: "sess-autopilot-beads-block", thread_id: "thread-autopilot-beads-block", active_skills: [ { skill: "autopilot", phase: "ralplan", active: true, session_id: "sess-autopilot-beads-block", thread_id: "thread-autopilot-beads-block", }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: "sess-autopilot-beads-block", thread_id: "thread-autopilot-beads-block", }); await writeCanonicalLeaderFixture( stateDir, "sess-autopilot-beads-block", "thread-autopilot-beads-block", cwd, ); const blockedCommands = [ "bd --db /tmp/outside.db create 'Issue title'", "bd --db ../outside.db create 'Issue title'", "bd --db .beads create 'Issue title'", "bd --db $DB create 'Issue title'", "DB=.beads/issues.db bd --db $DB create 'Issue title'", "env bd --db /tmp/outside.db create 'Issue title'", "command bd --db /tmp/outside.db create 'Issue title'", "bd --db .beads/issues.db export", "bd --db .beads/issues.db create 'Issue title' > src/leak.ts", "bd --db .beads/issues.db create 'Issue title'; cat > src/leak.ts", ]; for (const [index, command] of blockedCommands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-beads-block", thread_id: "thread-autopilot-beads-block", agent_id: "thread-autopilot-beads-block", tool_name: "Bash", tool_use_id: `tool-autopilot-beads-block-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal( (result.outputJson as { decision?: string } | null)?.decision, "block", `expected unsafe Beads command to be blocked: ${command}`, ); } const mentionWithImplementationWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-beads-block", thread_id: "thread-autopilot-beads-block", agent_id: "thread-autopilot-beads-block", tool_name: "Bash", tool_use_id: "tool-autopilot-beads-mention-write", tool_input: { command: "echo bd --db .beads/issues.db create > src/leak.ts" }, }, { cwd }, ); assert.equal((mentionWithImplementationWrite.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(String((mentionWithImplementationWrite.outputJson as { reason?: string } | null)?.reason ?? ""), /src\/leak\.ts/); const blockedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-beads-block", thread_id: "thread-autopilot-beads-block", agent_id: "thread-autopilot-beads-block", tool_name: "Edit", tool_use_id: "tool-autopilot-beads-src-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal((blockedImplementationEdit.outputJson as { decision?: string } | null)?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Autopilot rework implementation writes while ralplan remains guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-autopilot-rework-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-autopilot-rework"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-autopilot-rework", cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "rework", session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", active_skills: [ { skill: "autopilot", phase: "rework", active: true, session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", review_cycle: 2, review_verdict: { clean: false, recommendation: "REQUEST_CHANGES", findings: ["src/implementation.ts"] }, session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", }); const allowedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", tool_name: "Edit", tool_use_id: "tool-autopilot-rework-src-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(allowedImplementationEdit.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks typed agent-role-only implementation writes under active ralplan", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-typed-agent-role-ralplan-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-typed-executor-ralplan"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, thread_id: "thread-typed-executor-ralplan", active_skills: [ { skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId, thread_id: "thread-typed-executor-ralplan", }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, thread_id: "thread-typed-executor-ralplan", }); const allowedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-typed-executor-ralplan", agent_role: "executor", tool_name: "Edit", tool_use_id: "tool-typed-executor-ralplan-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(allowedImplementationEdit.outputJson?.decision, "block"); assert.match(String(allowedImplementationEdit.outputJson?.reason ?? ""), /Autopilot planning is active \(phase: ralplan\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks trusted typed agent-role implementation writes under active ralplan while preserving planning artifacts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-trusted-typed-agent-role-ralplan-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-trusted-typed-executor-ralplan"; const leaderThreadId = "thread-trusted-typed-executor-ralplan-leader"; const childThreadId = "thread-trusted-typed-executor-ralplan"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, active_skills: [ { skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId, thread_id: leaderThreadId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: "2026-04-19T00:10:00.000Z", threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: "2026-04-19T00:00:00.000Z", last_seen_at: "2026-04-19T00:10:00.000Z", turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "executor", first_seen_at: "2026-04-19T00:01:00.000Z", last_seen_at: "2026-04-19T00:10:00.000Z", turn_count: 1, leader_thread_id: leaderThreadId }, }, }, }, }); const allowedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId, }, }, }, tool_name: "Edit", tool_use_id: "tool-trusted-typed-executor-ralplan-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(allowedImplementationEdit.outputJson?.decision, "block"); assert.match(String(allowedImplementationEdit.outputJson?.reason ?? ""), /Autopilot planning is active \(phase: ralplan\)/); const allowedPlanningArtifactWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } } }, tool_name: "Write", tool_use_id: "tool-trusted-typed-executor-ralplan-plan", tool_input: { file_path: ".omx/plans/issue-3127.md", content: "# Planning artifact\n" }, }, { cwd }, ); assert.equal(allowedPlanningArtifactWrite.outputJson?.decision, "block"); assert.match(String(allowedPlanningArtifactWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("blocks typed thread-spawn provenance when the parent belongs to another leader session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-stale-typed-thread-spawn-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stale-typed-thread-spawn"; const leaderThreadId = "thread-stale-typed-leader"; const childThreadId = "thread-stale-typed-child"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, active_skills: [ { skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId, thread_id: leaderThreadId }, ], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: "2026-04-19T00:10:00.000Z", threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: "2026-04-19T00:00:00.000Z", last_seen_at: "2026-04-19T00:10:00.000Z", turn_count: 1 }, }, }, }, }); const blockedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: "thread-other-leader", }, }, }, tool_name: "Edit", tool_use_id: "tool-stale-typed-child-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(blockedImplementationEdit.outputJson?.decision, "block"); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("keeps typed and untyped collaboration child implementation writes blocked under active ralplan planning (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-ralplan-untyped-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-ralplan-untyped"; const leaderThreadId = "thread-3116-ralplan-untyped-leader"; const childThreadId = "thread-3116-ralplan-untyped-child"; const sessionDir = join(stateDir, "sessions", sessionId); const nowIso = new Date().toISOString(); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, active_skills: [ { skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId, thread_id: leaderThreadId }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, leader_thread_id: leaderThreadId }, }, }, }, }); // Untyped tracked/spawn-provenance child must NOT bypass the ralplan planning guard. const untypedChild = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "collaboration-child", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } }, }, tool_name: "Edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(untypedChild.outputJson?.decision, "block"); // Typed role labels do not bypass the ralplan planning guard. const typedChild = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } }, }, tool_name: "Edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(typedChild.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps typed and untyped collaboration child implementation writes blocked under active deep-interview planning (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-di-untyped-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-di-untyped"; const leaderThreadId = "thread-3116-di-untyped-leader"; const childThreadId = "thread-3116-di-untyped-child"; const sessionDir = join(stateDir, "sessions", sessionId); const nowIso = new Date().toISOString(); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: leaderThreadId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, thread_id: leaderThreadId }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, leader_thread_id: leaderThreadId }, }, }, }, }); // Untyped tracked/spawn-provenance child must NOT bypass the deep-interview planning guard. const untypedChild = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "collaboration-child", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } }, }, tool_name: "Edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(untypedChild.outputJson?.decision, "block"); // Typed role labels do not bypass the deep-interview planning guard. const typedChild = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } }, }, tool_name: "Edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(typedChild.outputJson?.decision, "block"); const allowedInterviewArtifactWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } } }, tool_name: "Write", tool_input: { file_path: ".omx/interviews/issue-3127.md", content: "# Interview artifact\n" }, }, { cwd }, ); assert.equal(allowedInterviewArtifactWrite.outputJson?.decision, "block"); assert.match(String(allowedInterviewArtifactWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows null-device fd redirects while deep-interview blocks real Bash writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-null-redirect-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-null-redirect"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-null-redirect", cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-null-redirect", active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-null-redirect" }], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-null-redirect", }); const allowedCommands = [ "find application -type d -name 'bug-tracking*' 2>/dev/null | head -20", "find application -type d -name 'bug-tracking*' 2> /dev/null | head -20", "find application -type d -name 'bug-tracking*' 2>NUL | head -20", "find application -type d -name 'bug-tracking*' 1>/dev/null", "find application -type d -name 'bug-tracking*' &>/dev/null", ]; for (const [index, command] of allowedCommands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-null-redirect", tool_name: "Bash", tool_use_id: `tool-di-null-redirect-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal(result.outputJson, null, command); } const blockedCommands = [ "find application -type d -name 'bug-tracking*' 2>errors.log | head -20", "find application -type d -name 'bug-tracking*' > /tmp/bug-tracking.txt", "find application -type d -name 'bug-tracking*' | tee /dev/null", ]; for (const [index, command] of blockedCommands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-null-redirect", tool_name: "Bash", tool_use_id: `tool-di-real-redirect-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows implementation tools after an explicit deep-interview handoff deactivates the mode", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-handoff-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-handoff"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-handoff", active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-handoff" }], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-handoff", }); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-di-handoff", prompt: "$ralph implement the clarified spec in src/implementation.ts", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-handoff", tool_name: "Edit", tool_use_id: "tool-di-post-handoff-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(result.outputJson, null); const completed = JSON.parse(await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8")) as { active?: boolean }; assert.equal(completed.active, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns a destructive-command caution on PreToolUse for rm -rf dist", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-danger-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-danger", tool_input: { command: "rm -rf dist" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: "Destructive Bash command detected (`rm -rf dist`). Confirm the target and expected side effects before running it.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for neutral pwd", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-neutral-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-neutral", tool_input: { command: "pwd" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records native subagent capacity exhaustion from spawn_agent PostToolUse output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-record-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-capacity-record", thread_id: "thread-subagent-capacity-record", turn_id: "turn-subagent-capacity-record", tool_name: "multi_agent_v1.spawn_agent", tool_response: { error: "collab spawn failed: agent thread limit reached", }, }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); const blocker = JSON.parse( await readFile(join(cwd, ".omx", "state", "native-subagent-capacity-blocker.json"), "utf-8"), ) as Record; assert.equal(blocker.reason, "agent_thread_limit_reached"); assert.equal(blocker.session_id, "sess-subagent-capacity-record"); assert.equal(blocker.thread_id, "thread-subagent-capacity-record"); assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent"); assert.match(String(blocker.error_summary), /agent thread limit reached/); assert.ok(Date.parse(String(blocker.expires_at)) > Date.parse(String(blocker.observed_at))); assert.equal(existsSync(join(cwd, ".omx", "state", "native-subagent-support.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records unsupported native subagent support blocker from spawn_agent PostToolUse output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-support-record-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-support-record", thread_id: "thread-subagent-support-record", turn_id: "turn-subagent-support-record", tool_name: "multi_agent_v1.spawn_agent", tool_response: { error: "unknown tool: multi_agent_v1.spawn_agent is unavailable" }, }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); const blocker = JSON.parse( await readFile(join(cwd, ".omx", "state", "native-subagent-support.json"), "utf-8"), ) as Record; assert.equal(blocker.status, "unsupported"); assert.equal(blocker.reason, "multi_agent_v1_unavailable"); assert.equal(blocker.session_id, "sess-subagent-support-record"); assert.equal(blocker.thread_id, "thread-subagent-support-record"); assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent"); assert.match(String(blocker.evidence), /unknown tool/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not persist support or capacity poison from successful collaboration child output", async () => { for (const toolName of [ "collaboration.spawn_agent", "collaboration.list_agents", "collaboration.followup_task", "collaboration.wait_agent", ]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-collaboration-success-")); try { await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-collaboration-success", thread_id: "thread-collaboration-success", tool_name: toolName, tool_response: { success: true, status: "completed", output: "Child completed. Optional packed plugin was unavailable, unsupported, and not found.", }, }, { cwd }, ); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, toolName); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false, toolName); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "sess-collaboration-success", thread_id: "thread-collaboration-success", }, { cwd }); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, `${toolName} restart`); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not persist legacy prose poison from non-spawn collaboration results", async () => { for (const toolName of [ "collaboration.list_agents", "collaboration.followup_task", "collaboration.wait_agent", ]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-collaboration-legacy-success-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, tool_name: toolName, tool_response: "Successful result: optional dependency unavailable, unsupported, or not found.", }, { cwd }); assert.equal(existsSync(join(cwd, ".omx", "state", "native-subagent-support.json")), false, toolName); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("#3316: denies collaboration.send_message during active deep-interview without host-authenticated caller-parent-target proof", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3316-di-send-message-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3316-di-send-message"; const leaderThreadId = "thread-3316-di-leader"; const childThreadId = "thread-3316-di-child"; const foreignSessionId = "sess-3316-di-foreign"; const foreignLeaderThreadId = "thread-3316-di-foreign-leader"; const foreignChildThreadId = "thread-3316-di-foreign-child"; const unrelatedThreadId = "thread-3316-di-unrelated"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, leader_thread_id: leaderThreadId, native_session_id: "native-3316-di", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", parent_thread_id: leaderThreadId }, }, }, [foreignSessionId]: { session_id: foreignSessionId, leader_thread_id: foreignLeaderThreadId, threads: { [foreignLeaderThreadId]: { thread_id: foreignLeaderThreadId, kind: "leader" }, [foreignChildThreadId]: { thread_id: foreignChildThreadId, kind: "subagent", parent_thread_id: foreignLeaderThreadId }, }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: leaderThreadId, active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, thread_id: leaderThreadId }], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, thread_id: leaderThreadId, }); const sendMessage = ( threadId: string, agentId: string, targetAgentId: unknown, overrides: Record = {}, ) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: agentId, tool_name: "collaboration.send_message", tool_input: { agent_id: targetAgentId, message: "status update" }, ...overrides, }, { cwd }, ); const leaderToChild = await sendMessage(leaderThreadId, leaderThreadId, childThreadId); assert.equal(leaderToChild.outputJson?.decision, "block", "leader-to-child"); assert.match(String(leaderToChild.outputJson?.reason ?? ""), /not a recognized read-only or explicitly authorized deep-interview mutation transport|documented host-authenticated Main-root authority/, "leader-to-child"); const childToLeader = await sendMessage(childThreadId, childThreadId, leaderThreadId); assert.equal(childToLeader.outputJson?.decision, "block", "child-to-leader"); assert.match(String(childToLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "child-to-leader"); // Flattened tool names remain equally denied without a host-authenticated caller-parent-target relation. const flattenedChildToLeader = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_id: childThreadId, tool_name: "collaborationsend_message", tool_input: { agent_id: leaderThreadId, message: "status update" }, }, { cwd }, ); assert.equal(flattenedChildToLeader.outputJson?.decision, "block", "flattened-child-to-leader"); assert.match(String(flattenedChildToLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "flattened-child-to-leader"); // spawn/close/interrupt/followup/wait remain gated for the same registered child: no namespace-wide loosening. for (const toolName of [ "collaboration.spawn_agent", "collaboration.close_agent", "collaboration.interrupt_agent", "collaboration.followup_task", "collaboration.wait_agent", ]) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_id: childThreadId, tool_name: toolName, tool_input: { agent_id: leaderThreadId, message: "status update" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block", toolName); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, toolName); } // Cold-start leader (a genuine leader thread that has not yet been recorded as this // session's leader_thread_id in subagent-tracking.json) must not be silently treated as // an arbitrary authorized child; it resolves to native-child and stays fail-closed and denied. const coldStartUnregisteredThreadId = "thread-3316-di-cold-start-leader"; const coldStartLeader = await sendMessage(coldStartUnregisteredThreadId, coldStartUnregisteredThreadId, leaderThreadId); assert.equal(coldStartLeader.outputJson?.decision, "block", "cold-start-leader"); assert.match(String(coldStartLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "cold-start-leader"); // Foreign session: a child registered under a different session_id's tracking record stays denied. const foreignSessionChild = await sendMessage(foreignChildThreadId, foreignChildThreadId, leaderThreadId); assert.equal(foreignSessionChild.outputJson?.decision, "block", "foreign-session-child"); assert.match(String(foreignSessionChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "foreign-session-child"); // Unrelated cross-child: a same-session child messaging an arbitrary thread that is not the // registered leader (only leader<->registered-child pairs are admitted). const unrelatedCrossChild = await sendMessage(childThreadId, childThreadId, unrelatedThreadId); assert.equal(unrelatedCrossChild.outputJson?.decision, "block", "unrelated-cross-child"); assert.match(String(unrelatedCrossChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "unrelated-cross-child"); // Wrong-parent/session: a child claims a target agent_id pointing at a leader thread from a // DIFFERENT session; session-scoped target relation must not resolve across sessions. const wrongParentSession = await sendMessage(childThreadId, childThreadId, foreignLeaderThreadId); assert.equal(wrongParentSession.outputJson?.decision, "block", "wrong-parent-session"); assert.match(String(wrongParentSession.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "wrong-parent-session"); // Malformed / empty / non-string target agent_id stays denied (fail-closed). for (const [label, malformedTarget] of [ ["missing", undefined], ["empty", ""], ["whitespace", " "], ["number", 42], ["object", { thread_id: leaderThreadId }], ["array", [leaderThreadId]], ["null", null], ] as const) { const malformed = await sendMessage(childThreadId, childThreadId, malformedTarget as unknown); assert.equal(malformed.outputJson?.decision, "block", `malformed-target-${label}`); assert.match(String(malformed.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, `malformed-target-${label}`); } // Message content is inert, but transport authority is still absent and must fail closed. const messageWithShellPayload = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_id: childThreadId, tool_name: "collaboration.send_message", tool_input: { agent_id: leaderThreadId, message: "; rm -rf / #`touch /tmp/pwned`$(id)" }, }, { cwd }, ); assert.equal(messageWithShellPayload.outputJson?.decision, "block", "shell-payload-message-content"); assert.match(String(messageWithShellPayload.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "shell-payload-message-content"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("#3316: denies collaboration.send_message during active ralplan without host-authenticated caller-parent-target proof", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3316-ralplan-send-message-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3316-ralplan-send-message"; const leaderThreadId = "thread-3316-ralplan-leader"; const childThreadId = "thread-3316-ralplan-child"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, leader_thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", parent_thread_id: leaderThreadId }, }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(sessionDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: sessionId, }); const sendMessage = (threadId: string, targetAgentId: unknown) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: threadId, tool_name: "collaboration.send_message", tool_input: { agent_id: targetAgentId, message: "critic feedback ready" }, }, { cwd }, ); const leaderToChild = await sendMessage(leaderThreadId, childThreadId); assert.equal(leaderToChild.outputJson?.decision, "block", "ralplan-leader-to-child"); assert.match(String(leaderToChild.outputJson?.reason ?? ""), /not a recognized read-only or explicitly authorized planning mutation transport|documented host-authenticated Main-root authority/, "ralplan-leader-to-child"); const childToLeader = await sendMessage(childThreadId, leaderThreadId); assert.equal(childToLeader.outputJson?.decision, "block", "ralplan-child-to-leader"); assert.match(String(childToLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "ralplan-child-to-leader"); const spawnAgentStillGated = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_id: childThreadId, tool_name: "collaboration.spawn_agent", tool_input: { agent_type: "executor", message: "spawn attempt" }, }, { cwd }, ); assert.equal(spawnAgentStillGated.outputJson?.decision, "block", "ralplan-spawn-agent-still-gated"); assert.match(String(spawnAgentStillGated.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "ralplan-spawn-agent-still-gated"); const malformedTarget = await sendMessage(childThreadId, undefined); assert.equal(malformedTarget.outputJson?.decision, "block", "ralplan-malformed-target"); assert.match(String(malformedTarget.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "ralplan-malformed-target"); } finally { await rm(cwd, { recursive: true, force: true }); } }); // Direct-cancel positives must prove behavior in a clean execution context, // but the test runner itself may inherit Node startup/output instrumentation // (e.g. NODE_V8_COVERAGE from the coverage lane). Clear all listed baseline // values, including the tolerated NODE_EXTRA_CA_CERTS, solely to establish a // deterministic baseline; denial cases inject unsafe values explicitly. // The list is a test-environment reset, not the hook's unsafe-env denylist. const RUNNER_BASELINE_NODE_ENV_NAMES = [ "NODE_OPTIONS", "NODE_EXTRA_CA_CERTS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ]; const withCleanRunnerNodeEnvironment = async (run: () => Promise): Promise => { const previous = new Map(); for (const name of RUNNER_BASELINE_NODE_ENV_NAMES) { previous.set(name, process.env[name]); delete process.env[name]; } try { return await run(); } finally { for (const [name, value] of previous) { if (value === undefined) delete process.env[name]; else process.env[name] = value; } } }; const writeFlattenedCollaborationRalplanFixture = async (cwd: string) => { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-flattened-collab-ralplan"; const leaderThreadId = "thread-flattened-collab-ralplan-leader"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId, thread_id: leaderThreadId, }], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", started_at: "2026-07-22T00:00:00.000Z", updated_at: "2026-07-22T00:10:00.000Z", session_id: sessionId, thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: "2026-07-22T00:10:00.000Z", threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: "2026-07-22T00:00:00.000Z", last_seen_at: "2026-07-22T00:10:00.000Z", turn_count: 1, }, }, }, }, }); return { sessionId, leaderThreadId, stateDir }; }; it("denies flattened collaboration tools under active ralplan without documented host authority", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-ralplan-")); try { const { sessionId, leaderThreadId } = await writeFlattenedCollaborationRalplanFixture(cwd); for (const [tool_name, tool_input] of [ ["collaborationlist_agents", {}], ["collaborationfollowup_task", {}], ["collaborationwait_agent", {}], ["collaborationsend_message", {}], ["collaborationinterrupt_agent", {}], ["collaborationclose_agent", {}], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name, tool_input, }, { cwd }); assert.equal(result.outputJson?.decision, "block", tool_name); assert.match( String(result.outputJson?.reason ?? ""), /not a recognized read-only or explicitly authorized planning mutation transport|OWNER_CONFIRMATION_REQUIRED/, tool_name, ); } const blocked = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name: "collaborationbogus_thing", tool_input: {}, }, { cwd }); assert.equal(blocked.outputJson?.decision, "block"); const reason = String(blocked.outputJson?.reason ?? ""); assert.match(reason, /not a recognized read-only or explicitly authorized planning mutation transport|OWNER_CONFIRMATION_REQUIRED/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows installed-role spawn_agent consensus delegation during active ralplan planning (#3451-B)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3451-ralplan-consensus-spawn-")); try { const { sessionId, leaderThreadId } = await writeFlattenedCollaborationRalplanFixture(cwd); // Spawn with an installed role (planner) should pass through the ralplan boundary for (const [tool_name, tool_input] of [ ["collaborationspawn_agent", { agent_type: "planner", message: "draft the plan" }], ["collaboration.spawn_agent", { agent_type: "architect", message: "review the plan" }], ["spawn_agent", { agent_type: "critic", message: "critique the plan" }], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name, tool_input, }, { cwd }); assert.equal(result.outputJson, null, `${tool_name} with installed role should not be blocked during ralplan`); } const customRole = "custom-consensus-bypass"; const agentsDir = join(cwd, ".codex", "agents"); await mkdir(agentsDir, { recursive: true }); await writeFile(join(agentsDir, `${customRole}.toml`), "# installed custom role\n"); for (const [agent_type, expectedReason] of [ ["executor", /not a recognized read-only or explicitly authorized planning mutation transport/], ["debugger", /not a recognized read-only or explicitly authorized planning mutation transport/], [customRole, /not a recognized read-only or explicitly authorized planning mutation transport/], ["", /not a recognized read-only or explicitly authorized planning mutation transport/], ["planner!", /unknown or not installed/], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name: "collaborationspawn_agent", tool_input: agent_type ? { agent_type, message: "must not bypass consensus role boundary" } : {}, }, { cwd }); assert.equal(result.outputJson?.decision, "block", `${agent_type || "empty"} role must be denied`); assert.match(String(result.outputJson?.reason ?? ""), expectedReason, `${agent_type || "empty"} role deny reason`); } // Dotted and flattened non-spawn collaboration transports remain blocked. for (const tool_name of ["collaborationlist_agents", "collaboration.list_agents"]) { const blockedList = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name, tool_input: {}, }, { cwd }); assert.equal(blockedList.outputJson?.decision, "block", `${tool_name} must remain blocked`); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies unknown typed roles dispatched through flattened spawn names", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-unknown-role-")); try { const { sessionId, leaderThreadId } = await writeFlattenedCollaborationRalplanFixture(cwd); const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name: "collaborationspawn_agent", tool_input: { agent_type: "definitely-not-an-installed-role", message: "x" }, }, { cwd }); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /unknown or not installed/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records native blockers from flattened collaboration spawn failures with the original tool name", async () => { const capacityCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-capacity-")); try { const result = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: capacityCwd, session_id: "sess-flattened-capacity", thread_id: "thread-flattened-capacity", turn_id: "turn-flattened-capacity", tool_name: "collaborationspawn_agent", tool_response: "collab spawn failed: agent thread limit reached", }, { cwd: capacityCwd }); assert.equal(result.outputJson, null); const blocker = JSON.parse( await readFile(join(capacityCwd, ".omx", "state", "native-subagent-capacity-blocker.json"), "utf-8"), ) as Record; assert.equal(blocker.tool_name, "collaborationspawn_agent"); assert.match(String(blocker.error_summary), /agent thread limit reached/); } finally { await rm(capacityCwd, { recursive: true, force: true }); } const supportCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-support-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: supportCwd, session_id: "sess-flattened-support", thread_id: "thread-flattened-support", turn_id: "turn-flattened-support", tool_name: "collaborationspawn_agent", tool_response: { error: "unknown tool: collaborationspawn_agent is unavailable" }, }, { cwd: supportCwd }); const blocker = JSON.parse( await readFile(join(supportCwd, ".omx", "state", "native-subagent-support.json"), "utf-8"), ) as Record; assert.equal(blocker.tool_name, "collaborationspawn_agent"); } finally { await rm(supportCwd, { recursive: true, force: true }); } }); it("does not persist support or capacity poison from successful flattened collaboration child output", async () => { for (const toolName of [ "collaborationspawn_agent", "collaborationlist_agents", "collaborationfollowup_task", "collaborationwait_agent", ]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-success-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, session_id: "sess-flattened-success", thread_id: "thread-flattened-success", tool_name: toolName, tool_response: { success: true, status: "completed", output: "Child completed. Optional packed plugin was unavailable, unsupported, and not found.", }, }, { cwd }); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, toolName); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false, toolName); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not persist blockers from nested-only flattened collaboration lifecycle evidence", async () => { for (const toolName of ["collaboration.list_agents", "collaborationlist_agents"]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-collab-nested-lifecycle-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, session_id: "sess-nested-lifecycle", thread_id: "thread-nested-lifecycle", tool_name: toolName, tool_response: { agents: [ { agent_name: "/root", agent_status: "running" }, { agent_name: "/root/reviewer", agent_status: { completed: "Child report discusses an unavailable and unsupported optional adapter.", }, }, ], }, }, { cwd }); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, toolName); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false, toolName); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("blocks flattened close_agent cleanup after recent native subagent capacity exhaustion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-capacity-close-block-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, session_id: "sess-flattened-capacity-close-block", thread_id: "thread-flattened-capacity-close-block", turn_id: "turn-flattened-capacity-close-block", tool_name: "collaborationspawn_agent", tool_response: "collab spawn failed: agent thread limit reached", }, { cwd }); const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-flattened-capacity-close-block", thread_id: "thread-flattened-capacity-close-block", tool_name: "collaborationclose_agent", tool_input: { target: "019ecc36-stale" }, }, { cwd }); assert.equal(result.outputJson?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /capacity/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows session-scoped omx cancel under active ralplan without weakening state guards", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-ralplan-cancel-")); try { const { sessionId, leaderThreadId } = await writeFlattenedCollaborationRalplanFixture(cwd); const bash = (command: string) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name: "Bash", tool_input: { command }, }, { cwd }); // The exemption also requires the bare `omx` token to resolve to the // hook package's canonical CLI under the inherited PATH, so positives // and impostor denials run under a proven-trusted resolution to stay // discriminating (a denial must not pass merely because the ambient // PATH was untrusted). const workspacePackageCli = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const trustedPackageBin = join(cwd, "node_modules", ".bin", "omx"); await mkdir(dirname(trustedPackageBin), { recursive: true }); await symlink(workspacePackageCli, trustedPackageBin); const trustedPackagePath = `${dirname(trustedPackageBin)}:${dirname(process.execPath)}`; const bashTrusted = async (command: string) => { const inheritedPath = process.env.PATH; process.env.PATH = trustedPackagePath; try { return await bash(command); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }; const cleanPositives = await withCleanRunnerNodeEnvironment(async () => ({ plain: await bashTrusted("omx cancel"), force: await bashTrusted("omx cancel --force"), })); assert.equal(cleanPositives.plain.outputJson, null); assert.equal(cleanPositives.force.outputJson, null); const inheritedCaPositives = await withCleanRunnerNodeEnvironment(async () => { process.env.NODE_EXTRA_CA_CERTS = "/nonexistent/enterprise-ca.pem"; return { plain: await bashTrusted("omx cancel"), force: await bashTrusted("omx cancel --force"), }; }); assert.equal(inheritedCaPositives.plain.outputJson, null); assert.equal(inheritedCaPositives.force.outputJson, null); assert.equal((await bash("omx cancel")).outputJson?.decision, "block", "ambient non-package omx resolution is not trusted"); const chained = await bash("omx cancel --force && rm -rf x"); assert.equal(chained.outputJson?.decision, "block"); const unknownFlag = await bash("omx cancel --json"); assert.equal(unknownFlag.outputJson?.decision, "block"); // The direct-cancel grammar admits no leading environment assignments at // all: runtime startup/configuration variables are an open-ended // namespace (PATH, NODE_OPTIONS, OPENSSL_CONF, OMX_* state selectors), // so every prefixed form is denied rather than denylisted one by one. for (const [label, command] of [ ["benign-looking env prefix", "FOO=bar omx cancel --force"], ["multi env prefix", "A=1 B=2 omx cancel"], ["openssl config injection", "OPENSSL_CONF=/tmp/evil.cnf omx cancel"], ["path override", "PATH=/tmp/attacker omx cancel"], ["node preload override", "NODE_OPTIONS=--require=./payload.cjs omx cancel"], ["path-qualified impostor", "/tmp/omx cancel --force"], ["relative-path impostor", "./omx cancel"], ["quoted pseudo-assignment", "'X=./payload' omx cancel"], ["escaped pseudo-assignment", "X\\=./payload omx cancel"], ["omx root override", "OMX_ROOT=/tmp/other omx cancel"], ["omx state root override", "OMX_STATE_ROOT=/tmp/other omx cancel"], ["omx team state root override", "OMX_TEAM_STATE_ROOT=/tmp/other omx cancel"], ["omx session override", "OMX_SESSION_ID=other-session omx cancel"], ["quoted executable", "\"omx\" cancel"], ["semicolon operator in assignment", "FOO=bar;./payload omx cancel"], ["command substitution in assignment", "FOO=$(./payload) omx cancel"], ["backtick substitution in assignment", "FOO=`./payload` omx cancel"], ["redirection in assignment", "FOO=bar>src/pwned omx cancel"], ["and-operator in assignment", "FOO=bar&&./payload omx cancel"], ["uppercase executable", "OMX cancel"], ["unicode nbsp separator", "omx\u00a0cancel"], ["newline between words", "omx\ncancel"], // Byte-form lookalikes: the raw payload command is not byte-identical // to the trim-normalized command, so the direct-cancel exemption can // never fire and the omx-mutation analysis denies the command. This // asserts observable denial, not merely failure of one helper. ["byte-order-mark lookalike executable", "\ufeffomx cancel"], ["carriage-return suffix lookalike", "omx cancel\r"], ] as const) { const impostor = await bashTrusted(command); assert.equal(impostor.outputJson?.decision, "block", label); } // Inherited execution-context overrides must deny even the byte-exact // command: the exemption proves the text, not the runtime that will // execute it (Bash sources $BASH_ENV first, imported functions shadow // the omx executable, and Node loader env preloads code). for (const [label, envName, envValue] of [ ["inherited bash startup file", "BASH_ENV", "/tmp/prelude.sh"], ["imported omx function shadow", "BASH_FUNC_omx%%", "() { printf owned > src/pwned.ts; }"], ["inherited node loader override", "NODE_OPTIONS", "--require=./payload.cjs"], ["inherited openssl config", "OPENSSL_CONF", "/tmp/evil.cnf"], ["inherited dynamic loader preload", "LD_PRELOAD", "/tmp/payload.so"], ["inherited node coverage output", "NODE_V8_COVERAGE", "/tmp/coverage-out"], ] as const) { const previousValue = process.env[envName]; const previousCa = process.env.NODE_EXTRA_CA_CERTS; process.env[envName] = envValue; process.env.NODE_EXTRA_CA_CERTS = "/nonexistent/enterprise-ca.pem"; try { const poisoned = await bashTrusted("omx cancel"); assert.equal(poisoned.outputJson?.decision, "block", `${label} with inherited CA`); } finally { if (previousValue === undefined) delete process.env[envName]; else process.env[envName] = previousValue; if (previousCa === undefined) delete process.env.NODE_EXTRA_CA_CERTS; else process.env.NODE_EXTRA_CA_CERTS = previousCa; } } // A PATH-shadowed omx (a different executable resolving first) is not // the validated cancellation program and must deny even byte-exact text. const shadowBinDir = join(cwd, "shadow-bin"); await mkdir(shadowBinDir, { recursive: true }); await writeFile(join(shadowBinDir, "omx"), "#!/bin/sh\ntouch src/path-shadow-owned.ts\n", "utf-8"); await chmod(join(shadowBinDir, "omx"), 0o755); { const inheritedPath = process.env.PATH; process.env.PATH = `${shadowBinDir}:${trustedPackagePath}`; try { const shadowed = await bash("omx cancel"); assert.equal(shadowed.outputJson?.decision, "block", "PATH-shadowed omx executable"); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } } const stateClear = await bash("omx state clear --force --mode ralplan --json"); assert.equal(stateClear.outputJson?.decision, "block"); assert.match(String(stateClear.outputJson?.reason ?? ""), /Autopilot planning is active \(phase: ralplan\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not persist native blockers from structured failures on unrelated tools", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unrelated-structured-failure-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_response: { success: false, status: "failed", error: "optional plugin is unsupported and unavailable", }, }, { cwd }); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks close_agent cleanup after recent native subagent capacity exhaustion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-close-block-")); try { await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-capacity-close-block", thread_id: "thread-subagent-capacity-close-block", turn_id: "turn-subagent-capacity-close-block", tool_name: "multi_agent_v1.spawn_agent", tool_response: "collab spawn failed: agent thread limit reached", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-close-block", thread_id: "thread-subagent-capacity-close-block", tool_name: "multi_agent_v1.close_agent", tool_input: { target: "019ecc36-stale" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /agent thread limit reached/); assert.match(JSON.stringify(result.outputJson), /multi_agent_v1\.close_agent/); assert.match(JSON.stringify(result.outputJson), /multi_tool_use\.parallel/); assert.match(JSON.stringify(result.outputJson), /bounded capacity blocker/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits hook-specific deny PreToolUse CLI stdout for close_agent capacity blocks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-subagent-capacity-close-block-")); try { parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "PostToolUse", cwd, session_id: "sess-cli-subagent-capacity-close-block", thread_id: "thread-cli-subagent-capacity-close-block", turn_id: "turn-cli-subagent-capacity-close-block", tool_name: "multi_agent_v1.spawn_agent", tool_response: "collab spawn failed: agent thread limit reached", }, { cwd })); const result = runNativeHookCliResult({ hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-subagent-capacity-close-block", thread_id: "thread-cli-subagent-capacity-close-block", tool_name: "multi_agent_v1.close_agent", tool_input: { target: "019ecc36-stale" }, }, { cwd }); assert.equal(result.status, 0, result.stderr || result.stdout); const output = parseSingleJsonStdout(result.stdout); const hookSpecificOutput = output.hookSpecificOutput as Record; assert.deepEqual(Object.keys(output).sort(), ["hookSpecificOutput"]); assert.equal(hookSpecificOutput.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput.permissionDecision, "deny"); assert.match(String(hookSpecificOutput.permissionDecisionReason ?? ""), /Native subagent capacity was exhausted recently/); assert.match(String(hookSpecificOutput.additionalContext ?? ""), /agent thread limit reached/); assert.match(String(hookSpecificOutput.additionalContext ?? ""), /Do not call multi_agent_v1\.close_agent/); assert.equal(output.decision, undefined); assert.equal(output.reason, undefined); assert.equal(output.systemMessage, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks parallel close_agent cleanup after recent native subagent capacity exhaustion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-parallel-close-block-")); try { await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-capacity-parallel-close-block", thread_id: "thread-subagent-capacity-parallel-close-block", tool_name: "multi_agent_v1.spawn_agent", tool_response: "agent thread limit reached", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-parallel-close-block", thread_id: "thread-subagent-capacity-parallel-close-block", tool_name: "multi_tool_use.parallel", tool_input: { tool_uses: [ { recipient_name: "multi_agent_v1.close_agent", parameters: { target: "stale-1" } }, { recipient_name: "multi_agent_v1.close_agent", parameters: { target: "stale-2" } }, ], }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Do not call multi_agent_v1\.close_agent/); assert.match(JSON.stringify(result.outputJson), /do not batch close_agent through multi_tool_use\.parallel/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks parallel close_agent cleanup with flattened nested recipients after native capacity exhaustion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-parallel-close-flattened-")); try { await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-capacity-parallel-close-flattened", thread_id: "thread-subagent-capacity-parallel-close-flattened", tool_name: "collaborationspawn_agent", tool_response: "agent thread limit reached", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-parallel-close-flattened", thread_id: "thread-subagent-capacity-parallel-close-flattened", tool_name: "multi_tool_use.parallel", tool_input: { tool_uses: [ { recipient_name: "collaborationclose_agent", parameters: { target: "stale-1" } }, { recipient_name: "collaborationclose_agent", parameters: { target: "stale-2" } }, ], }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /do not batch close_agent through multi_tool_use\.parallel/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block close_agent without a recent native capacity blocker", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-close-no-blocker-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-close-no-blocker", thread_id: "thread-subagent-capacity-close-no-blocker", tool_name: "multi_agent_v1.close_agent", tool_input: { target: "completed-agent" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block close_agent when the native capacity blocker is expired", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-close-expired-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "native-subagent-capacity-blocker.json"), { schema_version: 1, reason: "agent_thread_limit_reached", session_id: "sess-subagent-capacity-close-expired", error_summary: "agent thread limit reached", observed_at: "2026-06-20T00:00:00.000Z", expires_at: "2026-06-20T00:30:00.000Z", cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-close-expired", thread_id: "thread-subagent-capacity-close-expired", tool_name: "multi_agent_v1.close_agent", tool_input: { target: "completed-agent" }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("warns on PreToolUse for vague sloppy fallback implementation framing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-warn-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-warn", tool_input: { command: [ "cat > src/runtime.ts <<'EOF'", "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", "EOF", ].join("\n"), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, undefined); assert.equal((result.outputJson as { hookSpecificOutput?: { hookEventName?: string } } | null)?.hookSpecificOutput?.hookEventName, "PreToolUse"); assert.match(JSON.stringify(result.outputJson), /don't make potential slop/); assert.match(JSON.stringify(result.outputJson), /architect/); assert.match(JSON.stringify(result.outputJson), /environment issue/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not warn on PreToolUse for read-only fallback text inspection", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-readonly-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-readonly", tool_input: { command: "rg \"quick hack fallback if it fails\" src docs" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("warns when a read-only command is chained before sloppy fallback writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-chained-write-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-chained-write", tool_input: { command: [ "rg foo src && cat > src/runtime.ts < { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-grounded-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-grounded", tool_input: { command: [ "cat > src/compat.ts <<'EOF'", "export function resolveCompatMode() {", " // temporary fallback because legacy compatibility needs fail-safe startup behavior", " return 'legacy';", "}", "// Tested: npm test", "EOF", ].join("\n"), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for untracked non-Bash-style sloppy fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-untracked-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-untracked" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); assert.match(JSON.stringify(result.outputJson), /src\/runtime\.ts/); assert.match(JSON.stringify(result.outputJson), /grounded design/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking repeated Stop while sloppy fallback diff remains", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-repeat-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-repeat", turn_id: "turn-repeat" }; const first = await dispatchCodexNativeHook(payload, { cwd }); const repeated = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal((first.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((repeated.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((repeated.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails open after repeated identical sloppy fallback findings exceed the repeat cap", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-cap-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-cap", turn_id: "turn-cap" }; const decisions: Array = []; for (let attempt = 0; attempt < 5; attempt += 1) { const result = await dispatchCodexNativeHook( attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }, ); decisions.push((result.outputJson as { decision?: string } | null)?.decision ?? null); } assert.deepEqual(decisions, ["block", "block", "block", null, null]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resets the sloppy fallback repeat cap when findings change", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-cap-reset-"); try { await mkdir(join(cwd, "src"), { recursive: true }); const sloppySource = (exportName: string) => [ `export function ${exportName}() {`, " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"); await writeFile(join(cwd, "src", "runtime.ts"), sloppySource("loadRuntime")); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-cap-reset", turn_id: "turn-cap-reset" }; for (let attempt = 0; attempt < 5; attempt += 1) { await dispatchCodexNativeHook( attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }, ); } const allowed = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal(allowed.outputJson, null); await writeFile(join(cwd, "src", "other.ts"), sloppySource("loadOther")); const blockedAgain = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal((blockedAgain.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((blockedAgain.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the repeat count when identical findings move across staging states", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-staging-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-staging", turn_id: "turn-staging" }; const stop = (attempt: number) => dispatchCodexNativeHook(attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }); const first = await stop(0); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); const staged = await stop(1); execFileSync("git", ["reset", "-q", "--", "src/runtime.ts"], { cwd, stdio: "ignore" }); const unstagedAgain = await stop(2); const afterCap = await stop(3); assert.equal((first.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((staged.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((unstagedAgain.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal(afterCap.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the repeat count when staging a subset reorders identical findings", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-subset-"); try { await mkdir(join(cwd, "src"), { recursive: true }); const sloppySource = (exportName: string) => [ `export function ${exportName}() {`, " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"); await writeFile(join(cwd, "src", "alpha.ts"), sloppySource("loadAlpha")); await writeFile(join(cwd, "src", "beta.ts"), sloppySource("loadBeta")); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-subset", turn_id: "turn-subset" }; const stop = (attempt: number) => dispatchCodexNativeHook(attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }); const first = await stop(0); // Staging only beta reorders the raw finding list (staged findings come // first), but the fingerprint must stay identical for the same set. execFileSync("git", ["add", "src/beta.ts"], { cwd, stdio: "ignore" }); const subsetStaged = await stop(1); execFileSync("git", ["add", "src/alpha.ts"], { cwd, stdio: "ignore" }); const allStaged = await stop(2); const afterCap = await stop(3); assert.equal((first.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((subsetStaged.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((allStaged.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal(afterCap.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resets the repeat cap when a finding moves to a new line in the same file", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-moveline-"); try { await mkdir(join(cwd, "src"), { recursive: true }); const sloppySource = (headerLines: string[]) => [ ...headerLines, "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"); await writeFile(join(cwd, "src", "runtime.ts"), sloppySource([])); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-moveline", turn_id: "turn-moveline" }; const stop = (attempt: number) => dispatchCodexNativeHook(attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }); for (let attempt = 0; attempt < 4; attempt += 1) { await stop(attempt); } const allowed = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal(allowed.outputJson, null); // The identical text relocated to another line is a new finding set and // must block again rather than inherit the exhausted repeat cap. await writeFile(join(cwd, "src", "runtime.ts"), sloppySource(["// header comment"])); const blockedAgain = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal((blockedAgain.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((blockedAgain.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("skips the sloppy fallback diff audit when OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT is off", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-disabled-"); const previousValue = process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT; process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT = "off"; try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-disabled" }, { cwd }, ); assert.equal(result.outputJson, null); } finally { if (previousValue === undefined) delete process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT; else process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT = previousValue; await rm(cwd, { recursive: true, force: true }); } }); it("ignores untracked sloppy fallback files last written before the session transcript", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-preexisting-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); // The file must genuinely predate the session: ctime/birth time cannot // be backdated with utimes, so create the transcript afterwards. await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); // A real session appends to its transcript; the append makes the // transcript's ctime newer than its immutable birth time, which is what // lets the audit trust that birth time as the session start. await new Promise((resolve) => setTimeout(resolve, 25)); await appendFile(transcriptPath, "{}\n"); const { birthtimeMs, ctimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0) || birthtimeMs >= ctimeMs) { t.skip("immutable file birth time is not available on this platform"); return; } const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-preexisting", transcript_path: transcriptPath }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("trusts only immutable transcript birth times as the sloppy fallback session start", () => { assert.equal(isSloppyFallbackTranscriptStartUsable(50, 100), true); assert.equal(isSloppyFallbackTranscriptStartUsable(100, 100), false); assert.equal(isSloppyFallbackTranscriptStartUsable(0, 100), false); assert.equal(isSloppyFallbackTranscriptStartUsable(100, 50), false); assert.equal(isSloppyFallbackTranscriptStartUsable(Number.NaN, 100), false); assert.equal(isSloppyFallbackTranscriptStartUsable(50, Number.NaN), false); }); it("audits untracked files when the transcript birth time is indistinguishable from ctime", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-ctimebacked-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); // A freshly created transcript has birth time == ctime, mimicking // filesystems where Node reports a mutable ctime fallback as birthtime; // the audit must treat that as unavailable rather than trust it. const { birthtimeMs, ctimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0) || birthtimeMs < ctimeMs) { t.skip("this platform reports an immutable birth time distinct from ctime"); return; } const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-ctimebacked", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks untracked sloppy fallback files materialized in-session with preserved mtime", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-cpreserve-"); try { const seedPath = join(cwd, "seed.ts"); await writeFile( seedPath, [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const oldTime = new Date(Date.now() - 10 * 60_000); await utimes(seedPath, oldTime, oldTime); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); const { birthtimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0)) { t.skip("file birth time is not available on this platform"); return; } await mkdir(join(cwd, "src"), { recursive: true }); execFileSync("cp", ["-p", seedPath, join(cwd, "src", "runtime.ts")], { stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-cpreserve", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks untracked sloppy fallback files reachable through an in-session symlink to an older target", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-symlink-"); try { const seedPath = join(cwd, "seed.ts"); await writeFile( seedPath, [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const oldTime = new Date(Date.now() - 10 * 60_000); await utimes(seedPath, oldTime, oldTime); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); const { birthtimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0)) { t.skip("file birth time is not available on this platform"); return; } await mkdir(join(cwd, "src"), { recursive: true }); await symlink(seedPath, join(cwd, "src", "runtime.ts")); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-symlink", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks when a pre-session symlink's target is rewritten during the session", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-targetrewrite-"); try { // A non-auditable target extension keeps the symlink path as the only // route to the flagged content. const targetPath = join(cwd, "seed-data.txt"); await writeFile(targetPath, "export const clean = true;\n"); await mkdir(join(cwd, "src"), { recursive: true }); await symlink(targetPath, join(cwd, "src", "runtime.ts")); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); await new Promise((resolve) => setTimeout(resolve, 25)); await appendFile(transcriptPath, "{}\n"); const { birthtimeMs, ctimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0) || birthtimeMs >= ctimeMs) { t.skip("immutable file birth time is not available on this platform"); return; } await writeFile( targetPath, [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-targetrewrite", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks untracked sloppy fallback files written after the session transcript", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-inturn-"); try { const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); const { birthtimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0)) { t.skip("file birth time is not available on this platform"); return; } await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-inturn", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for unstaged tracked sloppy fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-unstaged-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(cwd, "src", "runtime.ts"), "export const runtime = 'base';\n"); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "initial"], { cwd, stdio: "ignore" }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // just bypass fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-unstaged" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /unstaged/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop from a subdirectory cwd for untracked sloppy source elsewhere", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-subdir-"); try { await mkdir(join(cwd, "src", "nested"), { recursive: true }); await writeFile(join(cwd, "src", "nested", "anchor.ts"), "export const anchor = true;\n"); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const subdir = join(cwd, "src", "nested"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd: subdir, session_id: "sess-stop-slop-subdir" }, { cwd: subdir }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /src\/runtime\.ts/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for staged sloppy fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-staged-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(cwd, "src", "runtime.ts"), "export const runtime = 'base';\n"); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "initial"], { cwd, stdio: "ignore" }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // temporary workaround fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-staged" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /staged/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for grounded compatibility fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-grounded-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "compat.ts"), [ "export function resolveCompatMode() {", " // temporary fallback for legacy startup", " // compatibility fail-safe tested by regression coverage", " return 'legacy';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-grounded" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when existing nearby source context grounds a new fallback line", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-existing-ground-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "compat.ts"), [ "export function resolveCompatMode() {", " // compatibility fail-safe tested by regression coverage", " return 'legacy';", "}", ].join("\n"), ); execFileSync("git", ["add", "src/compat.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "initial"], { cwd, stdio: "ignore" }); await writeFile( join(cwd, "src", "compat.ts"), [ "export function resolveCompatMode() {", " // compatibility fail-safe tested by regression coverage", " // temporary fallback if it fails", " return 'legacy';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-existing-ground" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for source-adjacent test file fallback wording", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-test-file-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.test.ts"), "it('documents no quick hack fallback if it fails', () => {});\n", ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-test-file" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for docs-only fallback wording", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-docs-"); try { await mkdir(join(cwd, "docs"), { recursive: true }); await writeFile( join(cwd, "docs", "notes.md"), "Do not implement a quick hack fallback if it fails.\n", ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-docs" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps git commit Lore enforcement ahead of sloppy fallback advisory", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-git-priority-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-git-priority", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "quick hack fallback if it fails"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); assert.doesNotMatch(JSON.stringify(result.outputJson), /don't make potential slop/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit with supported response shape when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); const hookSpecificOutput = (result.outputJson as { hookSpecificOutput?: Record }) .hookSpecificOutput ?? {}; assert.equal("additionalContext" in hookSpecificOutput, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when process env explicitly enables the Lore commit guard", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-env-enabled-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-env-enabled", tool_input: { command: 'git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("allows non-Lore git commit messages when the Lore commit guard is disabled by default", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-disabled-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { delete process.env.OMX_LORE_COMMIT_GUARD; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-disabled", tool_input: { command: 'git commit -m "fix: use conventional commit"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("blocks non-Lore git commit messages when the Lore commit guard is enabled in CODEX_HOME config.toml", async () => { await withLoreGuardConfig("1", "config-enabled", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-enabled", tool_input: { command: 'git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); }); }); it("allows non-Lore git commit messages when the Lore commit guard is disabled in CODEX_HOME config.toml", async () => { await withLoreGuardConfig("0", "config-disabled", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-disabled", tool_input: { command: 'git commit -m "fix: use conventional commit"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); }); }); it("lets inline Lore commit guard values override a disabled CODEX_HOME config.toml", async () => { await withLoreGuardConfig("0", "config-inline-enabled", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-inline-enabled", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); }); }); it("restores default-off Lore guard when env -u removes a disabled CODEX_HOME config source", async () => { await withLoreGuardConfig("0", "config-codex-home-unset", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-codex-home-unset", tool_input: { command: 'env -u CODEX_HOME git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); }); }); it("allows non-Lore git commit messages when the Lore commit guard is disabled inline", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-inline-disabled-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-inline-disabled", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=0 git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows inline disabled guard to override an enabled process env", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-inline-override-disabled-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-inline-override-disabled", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=0 git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("does not treat newline-separated Lore guard assignment as inline git commit opt-in", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-newline-assignment-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-newline-assignment", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1\ngit commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("restores default-off Lore guard when env -u unsets a config.toml fallback", async () => { await withLoreGuardConfig("1", "config-env-unset", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-env-unset", tool_input: { command: 'env -u OMX_LORE_COMMIT_GUARD git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); }); }); it("restores default-off Lore guard when env -u unsets an enabled process env", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-env-unset-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-env-unset", tool_input: { command: 'env -u OMX_LORE_COMMIT_GUARD git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("restores default-off Lore guard when env -i clears an enabled process env", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-env-ignore-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-env-ignore", tool_input: { command: 'env -i PATH=/usr/bin git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("keeps Lore commit enforcement disabled for unknown inline guard values", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-inline-unknown-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-inline-unknown", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=maybe git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats Lore commit guard disabled values as trim and case tolerant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-off-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = " OFF "; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-off", tool_input: { command: 'git commit -m "chore: conventional commit"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("keeps Lore commit enforcement disabled for unknown guard values", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-unknown-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "maybe"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-unknown", tool_input: { command: 'git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("continues to later PreToolUse checks when Lore commit guard is disabled", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-lore-disabled-destructive-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "false"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-lore-disabled-destructive", tool_input: { command: "rm -rf dist" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.doesNotMatch(JSON.stringify(result.outputJson), /Lore protocol/); assert.match(JSON.stringify(result.outputJson), /Destructive Bash command detected/); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for `git help commit`", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-help-commit-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-help-commit", tool_input: { command: "git help commit" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for `git config alias.ci commit`", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-config-alias-commit-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-config-alias-commit", tool_input: { command: "git config alias.ci commit" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for `git tag commit`", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-tag-commit-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-tag-commit", tool_input: { command: "git tag commit" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env-prefixed git commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-env-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-env-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 HUSKY=0 git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when git options appear before the real commit subcommand", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-option-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-option-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git -c core.editor=true commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env wrapper-prefixed git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-env-wrapper-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-env-wrapper-invalid", tool_input: { command: 'env OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env flag wrapper-prefixed git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-env-flag-wrapper-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-env-flag-wrapper-invalid", tool_input: { command: 'env -i PATH=/usr/bin OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env value-taking wrapper-prefixed git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-env-value-wrapper-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-env-value-wrapper-invalid", tool_input: { command: 'env -u FOO OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse path-qualified Windows git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-windows-path-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-windows-path-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 "C:/Program Files/Git/cmd/git.exe" commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse quoted backslash Windows git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-windows-backslash-path-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-windows-backslash-path-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 "C:\\Program Files\\Git\\cmd\\git.exe" commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse path-qualified git commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-path-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-path-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 /usr/bin/git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when the message comes from an external source", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-file-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-file", tool_input: { command: "OMX_LORE_COMMIT_GUARD=1 git commit -F .git/COMMIT_EDITMSG" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Use inline `git commit -m ...` paragraphs for Lore-format commits in this path; file/editor/reuse/fixup message sources are not inspectable safely from pre-tool-use enforcement.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when Lore trailers exist but the OmX co-author trailer is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-missing-omx-coauthor-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-missing-omx-coauthor", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Prevent invalid history from bypassing Lore enforcement"', '-m "The native pre-tool-use hook now blocks inline git commit messages that skip Lore trailers or the required OmX co-author trailer."', '-m "Constraint: Native PreToolUse can only inspect the Bash command text"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for Lore-compliant git commit with OmX co-author trailer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-valid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-valid", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Prevent invalid history from bypassing Lore enforcement"', '-m "The native pre-tool-use hook now blocks inline git commit messages that skip Lore trailers or the required OmX co-author trailer."', '-m "Constraint: Native PreToolUse can only inspect the Bash command text"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for compact inline Lore commit with only OmX co-author trailer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-coauthor-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-compact-coauthor", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Launch lvisai.xyz intro site"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for body-omitted inline Lore commit with decision trailers", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-trailers-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-compact-trailers", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Launch lvisai.xyz intro site"', '-m "Constraint: Native PreToolUse can only inspect inline Bash command text\nTested: node --test dist/scripts/__tests__/codex-native-hook.test.js\n\nCo-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse compact inline Lore commit when the blank separator is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-no-separator-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-compact-no-separator", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '--message="Launch lvisai.xyz intro site\nCo-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Add a blank line after the subject/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("warns on PreToolUse git commit when mapped source changes lack staged docs refresh", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-warn-")); try { execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await mkdir(join(cwd, "src", "scripts"), { recursive: true }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); await writeFile(join(cwd, "README.md"), "base\n", "utf-8"); execFileSync("git", ["add", "README.md", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-warn", tool_input: { command: [ 'git commit', '-m "Keep native hooks aligned with docs"', '-m "Update the stop hook internals without refreshing the operator docs yet."', '-m "Constraint: native hook warning MVP must remain non-blocking on commit path"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, undefined); assert.equal((result.outputJson as { hookSpecificOutput?: { hookEventName?: string } } | null)?.hookSpecificOutput?.hookEventName, "PreToolUse"); assert.match(JSON.stringify(result.outputJson), /Document-refresh warning/); assert.match(JSON.stringify(result.outputJson), /docs\/codex-native-hooks\.md/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not warn on PreToolUse when relevant docs are staged", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-docs-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); await mkdir(join(cwd, "docs"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); await writeFile(join(cwd, "docs", "codex-native-hooks.md"), "initial\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts", "docs/codex-native-hooks.md"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); await writeFile(join(cwd, "docs", "codex-native-hooks.md"), "updated\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts", "docs/codex-native-hooks.md"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-docs", tool_input: { command: [ 'git commit', '-m "Keep native hooks aligned with docs"', '-m "Update the stop hook internals and refresh the native hook docs together."', '-m "Constraint: native hook warning MVP must remain non-blocking on commit path"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not run commit-path document-refresh against payload cwd when git -C targets another repo", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-chdir-")); const otherRepo = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-other-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["init"], { cwd: otherRepo, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: otherRepo, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd: otherRepo, stdio: "ignore" }); await writeFile(join(otherRepo, "README.md"), "base\n", "utf-8"); execFileSync("git", ["add", "README.md"], { cwd: otherRepo, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd: otherRepo, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-chdir", tool_input: { command: [ `git -C ${JSON.stringify(otherRepo)}`, 'commit', '-m "Keep native hooks aligned with docs"', '-m "Document-refresh check should not inspect the caller cwd when commit targets another repo."', '-m "Constraint: alternate git targets are skipped unless hook-side repo resolution is added explicitly"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); await rm(otherRepo, { recursive: true, force: true }); } }); it("suppresses PreToolUse document-refresh warning when commit message includes an exemption", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-exempt-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-exempt", tool_input: { command: [ 'git commit', '-m "Keep native hooks aligned with docs"', '-m "Update the stop hook internals without docs refresh because behavior is internal-only."', '-m "Constraint: native hook warning MVP must remain non-blocking on commit path"', `-m "${DOCUMENT_REFRESH_EXEMPTION_PREFIX} internal-only behavior verified"`, '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns PostToolUse remediation guidance for command-not-found output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-fail", tool_input: { command: "foo --version" }, tool_response: "{\"exit_code\":127,\"stdout\":\"\",\"stderr\":\"bash: foo: command not found\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The Bash output indicates a command/setup failure that should be fixed before retrying.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "Bash reported `command not found`, `permission denied`, or a missing file/path. Verify the command, dependency installation, PATH, file permissions, and referenced paths before retrying.", }, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when successful search output contains old Bash failure text", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-successful-search-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-search-log", tool_input: { command: "rg 'command not found' .omx/logs" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "old-session.log: bash: foo: command not found", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when Bash stdout only contains failure-like source text", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-source-text-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-source-text", tool_input: { command: "sed -n '1,40p' hook-source.ts" }, tool_response: "const text = 'bash: foo: command not found';\nconst detail = 'permission denied';", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent for rc-zero build logs that mention missing grep paths", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-build-log-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-build-log", tool_input: { command: "npm run build" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "build passed\nnote: grep fixture says no such file or directory", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat Bash output containing MCP transport text as MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-source-text-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-mcp-source-text", tool_input: { command: "sed -n '580,620p' codex-native-pre-post.ts" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "reason: 'MCP transport closed before response over stdio pipe closed'", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when successful output includes prior hook context text", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-recursive-context-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-hook-context", tool_input: { command: "cat transcript.txt" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "Bash reported `command not found`, `permission denied`, or a missing file/path. Verify the command, dependency installation, PATH, file permissions, and referenced paths before retrying.", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when successful Bash output quotes MCP transport warnings", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-mcp-quote-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-bash-mcp-quote", tool_input: { command: "cat diagnostic-log.txt" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "diagnostic log quoted: MCP transport closed; stdio pipe closed before response", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when Bash hard-failure text has no parsed exit code", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-unparsed-failure-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-bash-unparsed-failure", tool_input: { command: "cat captured-output.txt" }, tool_response: "captured transcript says: bash: foo: command not found", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat non-MCP source output containing detector constants as MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-read-mcp-source-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Read", tool_use_id: "tool-read-mcp-source", tool_input: { file_path: "src/scripts/codex-native-pre-post.ts" }, tool_response: "const MCP_TRANSPORT_FAILURE_PATTERNS = [/transport closed/i, /server disconnected/i];\nconst context = /\\bmcp\\b/i;", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat non-MCP docs stdout mentioning closed MCP transport as transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-docs-mcp-log-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "ShellOutput", tool_use_id: "tool-docs-mcp-log", tool_response: JSON.stringify({ stdout: "Troubleshooting note: MCP transport closed after the server disconnected in an old log.", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not MCP-block non-MCP command output with unrelated stderr and MCP transport stdout", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-nonmcp-mixed-output-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "ShellOutput", tool_use_id: "tool-nonmcp-mixed-output", tool_response: JSON.stringify({ stdout: "captured log line: MCP transport closed before response", stderr: "grep: fixture.txt: No such file or directory", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks MCP-like raw transport failures", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-raw-transport-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-raw-transport", tool_response: "transport closed after server disconnected", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason || ""), /lost its transport\/server connection/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns PostToolUse MCP transport fallback guidance for clear MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-transport-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-transport", tool_input: { mode: "team", active: true }, tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); const output = result.outputJson as { decision?: string; reason?: string; hookSpecificOutput?: { additionalContext?: string }; } | null; assert.equal(output?.decision, "block"); assert.equal( output?.reason, "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.", ); const additionalContext = String( output?.hookSpecificOutput?.additionalContext ?? "", ); assert.match( additionalContext, /omx state write --input/, ); assert.match( additionalContext, /plain Node stdio processes/i, ); assert.match( additionalContext, /read-stall-state/, ); assert.match( additionalContext, /OMX_MCP_TRANSPORT_DEBUG=1/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not classify non-transport MCP failures as transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-nontransport-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-nontransport", tool_input: { active: true }, tool_response: "{\"error\":\"validation failed\",\"details\":\"mode is required\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("marks active team state failed on MCP transport death without deleting team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-mcp-transport-")); const previousCwd = process.cwd(); try { process.chdir(cwd); await initTeamState( "transport-team", "task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-transport" }, ); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "transport-team", current_phase: "team-exec", }); await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-transport", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-transport-team", tool_input: { mode: "team", active: true }, tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}", }, { cwd }, ); const phase = await readTeamPhase("transport-team", cwd); const attention = await readTeamLeaderAttention("transport-team", cwd); assert.equal(phase?.current_phase, "failed"); assert.equal(attention?.leader_attention_reason, "mcp_transport_dead"); assert.equal(attention?.leader_attention_pending, true); assert.equal(existsSync(join(cwd, ".omx", "state", "team", "transport-team")), true); } finally { process.chdir(previousCwd); await rm(cwd, { recursive: true, force: true }); } }); it("marks canonical team state failed when native payload session ids differ during MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-native-transport-")); const previousCwd = process.cwd(); const canonicalSessionId = "omx-canonical-session"; const nativeSessionId = "codex-native-session"; try { process.chdir(cwd); await writeSessionStart(cwd, canonicalSessionId); const sessionPath = join(cwd, ".omx", "state", "session.json"); const sessionState = JSON.parse( await readFile(sessionPath, "utf-8"), ) as { session_id?: string; native_session_id?: string }; await writeFile( sessionPath, JSON.stringify( { ...sessionState, native_session_id: nativeSessionId, }, null, 2, ), ); await initTeamState( "transport-team", "task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: canonicalSessionId }, ); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "transport-team", current_phase: "team-exec", }); await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: nativeSessionId, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-transport-team-native", tool_input: { mode: "team", active: true }, tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}", }, { cwd }, ); const phase = await readTeamPhase("transport-team", cwd); const attention = await readTeamLeaderAttention("transport-team", cwd); assert.equal(phase?.current_phase, "failed"); assert.equal(attention?.leader_attention_reason, "mcp_transport_dead"); assert.equal(attention?.leader_attention_pending, true); assert.equal(attention?.leader_session_id, canonicalSessionId); } finally { process.chdir(previousCwd); await rm(cwd, { recursive: true, force: true }); } }); it("does not block ordinary non-zero grep output in PostToolUse", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-grep-nonzero-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-grep-nonzero", tool_input: { command: "grep -R missing-pattern src | head -20" }, tool_response: "{\"exit_code\":1,\"stdout\":\"src/example.ts:TODO\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block ordinary non-zero diagnostic output in PostToolUse", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-diagnostic-nonzero-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-diagnostic-nonzero", tool_input: { command: "find src -name nope -print" }, tool_response: "{\"exit_code\":1,\"stdout\":\"searched 10 files\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats stderr-only informative non-zero output as reviewable instead of a generic failure", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-stderr-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-useful-stderr", tool_input: { command: "gh pr checks" }, tool_response: "{\"exit_code\":8,\"stdout\":\"\",\"stderr\":\"build pending\\nlint pass\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The Bash command returned a non-zero exit code but produced useful output that should be reviewed before retrying.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "The Bash output appears informative despite the non-zero exit code. Review and report the output before retrying instead of assuming the command simply failed.", }, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats non-zero gh pr checks style output as informative instead of a generic failure", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-useful", tool_input: { command: "gh pr checks" }, tool_response: "{\"exit_code\":8,\"stdout\":\"build\\tpending\\t2m\\nlint\\tpass\\t18s\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The Bash command returned a non-zero exit code but produced useful output that should be reviewed before retrying.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "The Bash output appears informative despite the non-zero exit code. Review and report the output before retrying instead of assuming the command simply failed.", }, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats wrapped gh pr checks output as reviewable", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-gh-wrapped-")); try { for (const command of [ "GH_PAGER=cat gh pr checks", "env GH_TOKEN=ghp_testtoken gh pr checks", "/usr/bin/env gh pr checks", "env -- gh pr checks", "env -C repo gh pr checks", "/usr/bin/gh pr checks", "gh --repo owner/repo pr checks", "echo a; gh pr checks", "cd repo && gh pr checks", ]) { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: `tool-useful-${command}`, tool_input: { command }, tool_response: "{\"exit_code\":8,\"stdout\":\"build pending\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat heredoc gh pr checks text as a reviewable command", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-gh-heredoc-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-heredoc-gh-checks", tool_input: { command: "cat <<'EOF'\ngh pr checks\nEOF\nfalse" }, tool_response: "{\"exit_code\":1,\"stdout\":\"gh pr checks\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat echoed gh pr checks text as a reviewable command", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-gh-echo-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-echo-gh-checks", tool_input: { command: "echo gh pr checks" }, tool_response: "{\"exit_code\":1,\"stdout\":\"gh pr checks\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns MCP transport-death guidance and preserves failed team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-dead-")); try { await initTeamState( "mcp-transport-dead-team", "transport failure fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-mcp-dead" }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-mcp-dead", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-dead", tool_response: JSON.stringify({ error: "transport closed", message: "MCP server disconnected", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason || ""), /lost its transport\/server connection/); const hookSpecificOutput = result.outputJson?.hookSpecificOutput as { hookEventName?: string; additionalContext?: string; } | undefined; assert.equal(hookSpecificOutput?.hookEventName, "PostToolUse"); assert.match( String(hookSpecificOutput?.additionalContext || ""), /Retry via CLI parity with `omx state write --input '\{\}' --json`\./, ); assert.match( String(hookSpecificOutput?.additionalContext || ""), /omx team api read-stall-state/, ); const phase = JSON.parse( await readFile(join(cwd, ".omx", "state", "team", "mcp-transport-dead-team", "phase.json"), "utf-8"), ) as { current_phase?: string; transitions?: Array<{ reason?: string }> }; assert.equal(phase.current_phase, "failed"); assert.equal(phase.transitions?.at(-1)?.reason, "mcp_transport_dead"); const attention = JSON.parse( await readFile(join(cwd, ".omx", "state", "team", "mcp-transport-dead-team", "leader-attention.json"), "utf-8"), ) as { leader_attention_reason?: string; attention_reasons?: string[] }; assert.equal(attention.leader_attention_reason, "mcp_transport_dead"); assert.ok(attention.attention_reasons?.includes("mcp_transport_dead")); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on neutral successful PostToolUse output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-neutral-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-ok", tool_input: { command: "pwd" }, tool_response: "{\"exit_code\":0,\"stdout\":\"/repo\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns CLI fallback guidance and preserves failed team state on clear MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-transport-")); try { await initTeamState( "transport-team", "transport failure fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-mcp-transport" }, ); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "transport-team", current_phase: "team-exec", }); const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-stop-mcp-transport", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-fail", tool_input: { mode: "team", active: true }, tool_response: JSON.stringify({ error: "MCP transport closed unexpectedly", exit_code: 1, }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state write --input '{\"mode\":\"team\",\"active\":true}' --json`. OMX MCP servers are plain Node stdio processes, so they still shut down when stdin/transport closes. If this happened during team runtime, inspect first with `omx team status ` or `omx team api read-stall-state --input '{\"team_name\":\"\"}' --json`, and only force cleanup after capturing needed state. For root-cause debugging, rerun with `OMX_MCP_TRANSPORT_DEBUG=1` to log why the stdio transport closed.", }, }); const phase = await readTeamPhase("transport-team", cwd); const attention = await readTeamLeaderAttention("transport-team", cwd); assert.equal(phase?.current_phase, "failed"); assert.equal(attention?.leader_attention_reason, "mcp_transport_dead"); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const rootActiveCase of [ { mode: "autopilot", phase: "execution" }, { mode: "ultrawork", phase: "executing" }, { mode: "ultraqa", phase: "diagnose" }, ] as const) { it(`returns Stop continuation output from root ${rootActiveCase.mode} state when no session is active`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-root-${rootActiveCase.mode}-`)); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, `${rootActiveCase.mode}-state.json`), { active: true, mode: rootActiveCase.mode, current_phase: rootActiveCase.phase, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX ${rootActiveCase.mode} is still active (phase: ${rootActiveCase.phase}); continue the task and gather fresh verification evidence before stopping.`, stopReason: `${rootActiveCase.mode}_${rootActiveCase.phase}`, systemMessage: `OMX ${rootActiveCase.mode} is still active (phase: ${rootActiveCase.phase}).`, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("returns Stop continuation output while Autopilot is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autopilot"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-autopilot", "autopilot-state.json"), { active: true, current_phase: "execution", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autopilot", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: execution); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_execution", systemMessage: "OMX autopilot is still active (phase: execution).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses parent Autopilot Stop continuation in side conversations", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-side-conversation-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-side-conversation"; const transcriptPath = join(cwd, "side-conversation-rollout.jsonl"); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", }); await writeFile( transcriptPath, `${JSON.stringify({ type: "message", role: "user", content: [ "Side conversation boundary.", "Everything before this boundary is inherited history from the parent thread. It is reference context only. It is not your current task.", "Only messages submitted after this boundary are active user instructions for this side conversation.", "You are a side-conversation assistant, separate from the main thread.", ].join("\n\n"), })}\n`, "utf-8", ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-autopilot-side-conversation", transcript_path: transcriptPath, last_assistant_message: "Waiting for a new side-conversation question.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires Autopilot code review after a compact-boundary Stop exemption", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-review-compact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-review-compact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "code-review", state: { phase_cycle: ["ralplan", "ralph", "code-review"], handoff_artifacts: { ralplan: ".omx/plans/prd-issue-2366.md", ralph: { verification: ["npm test"] }, code_review: null, }, review_verdict: null, }, }); const compactBoundary = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, stop_reason: "context compact", }, { cwd }, ); const resumedStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(compactBoundary.omxEventName, "stop"); assert.equal(compactBoundary.outputJson, null); assert.equal(resumedStop.omxEventName, "stop"); assert.deepEqual(resumedStop.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: code-review); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_code-review", systemMessage: "OMX autopilot is still active (phase: code-review). Run the required $code-review step before completing or clearing Autopilot state.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses duplicate Autopilot planning Stop replays so stale planning state cannot loop indefinitely", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-planning-replay-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autopilot-planning-replay"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-autopilot-planning-replay", "autopilot-state.json"), { active: true, current_phase: "planning", }); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-autopilot-planning-replay", thread_id: "thread-stop-autopilot-planning-replay", turn_id: "turn-stop-autopilot-planning-replay", last_assistant_message: "Autopilot planning is still active.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.deepEqual(first.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: planning); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_planning", systemMessage: "OMX autopilot is still active (phase: planning).", }); assert.equal(replay.omxEventName, "stop"); assert.equal(replay.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop when terminal Autopilot run-state shadows stale session ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-terminal-run-state-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-terminal-run-state"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-20T11:00:00.000Z", updated_at: "2026-05-20T11:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-autopilot-terminal-run-state", turn_id: "turn-stop-autopilot-terminal-run-state-1", last_assistant_message: "Done. Verification passed.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks Stop while Autopilot ralplan state is genuinely non-terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-active-ralplan-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-active-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: true, mode: "autopilot", outcome: "continue", current_phase: "ralplan", updated_at: "2026-05-20T11:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-autopilot-active-ralplan", turn_id: "turn-stop-autopilot-active-ralplan-1", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: ralplan); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_ralplan", systemMessage: "OMX autopilot is still active (phase: ralplan).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale root Autopilot planning state when the explicit session has no scoped state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-autopilot-planning-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd }); await writeJson(join(stateDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const staleRootCase of [ { mode: "autopilot", phase: "execution" }, { mode: "ultrawork", phase: "executing" }, { mode: "ultraqa", phase: "diagnose" }, ] as const) { it(`does not block Stop from stale root ${staleRootCase.mode} state when the explicit session directory is missing`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-missing-session-${staleRootCase.mode}-`)); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, `${staleRootCase.mode}-state.json`), { active: true, mode: staleRootCase.mode, current_phase: staleRootCase.phase, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "missing-session", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("does not block Stop when an explicit blocked_on_user run_outcome is present on a mode state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-blocked-outcome-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autopilot-blocked-outcome"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-autopilot-blocked-outcome", "autopilot-state.json"), { active: true, current_phase: "execution", run_outcome: "blocked_on_user", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autopilot-blocked-outcome", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while Ultrawork is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultrawork-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ultrawork"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-ultrawork", "ultrawork-state.json"), { active: true, current_phase: "executing", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork" }, { cwd }, ); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultrawork_executing", systemMessage: "OMX ultrawork is still active (phase: executing).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while UltraQA is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultraqa-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ultraqa"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-ultraqa", "ultraqa-state.json"), { active: true, current_phase: "diagnose", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultraqa" }, { cwd }, ); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX ultraqa is still active (phase: diagnose); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultraqa_diagnose", systemMessage: "OMX ultraqa is still active (phase: diagnose).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("marks leader-owned team attention during native Stop dispatch without a polling watcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-attention-")); try { await initTeamState( "stop-attention-team", "native stop attention", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-attention" }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-attention", }, { cwd }, ); const attention = await readTeamLeaderAttention("stop-attention-team", cwd); assert.equal(result.omxEventName, "stop"); assert.equal(attention?.source, "native_stop"); assert.equal(attention?.leader_session_active, false); assert.equal(attention?.leader_session_id, "sess-stop-team-attention"); assert.match(attention?.leader_session_stopped_at ?? "", /^\d{4}-\d{2}-\d{2}T/); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (stop-attention-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while team phase is non-terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "team-exec", team_name: "review-team", session_id: "sess-stop-team", }); await writeJson(join(stateDir, "team", "review-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (review-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for a team worker with a non-terminal assigned task via native worker context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevLeaderCwd = process.env.OMX_TEAM_LEADER_CWD; try { await initTeamState( "worker-stop-team", "worker stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker" }, ); const workerCwd = join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1"); const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team", "workers", "worker-1"); await mkdir(workerCwd, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: workerCwd, team_state_root: join(cwd, ".omx", "state"), }); await writeJson(join(workerDir, "status.json"), { state: "working", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team", "tasks", "task-1.json"), { id: "1", subject: "hook task", description: "finish hook task", status: "in_progress", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stop-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = join(cwd, ".omx", "state"); process.env.OMX_TEAM_LEADER_CWD = cwd; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd: workerCwd, session_id: "sess-stop-team-worker", }, { cwd: workerCwd }, ); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (in_progress); continue the current assigned task or report a concrete blocker before stopping.", stopReason: "team_worker_worker-1_1_in_progress", systemMessage: "OMX team worker worker-1 is still assigned task 1 (in_progress).", }); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevLeaderCwd === "string") process.env.OMX_TEAM_LEADER_CWD = prevLeaderCwd; else delete process.env.OMX_TEAM_LEADER_CWD; await rm(cwd, { recursive: true, force: true }); } }); it("rejects foreign explicit worker roots for completed Stop/PostToolUse and ignores malformed internal identity", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-foreign-root-")); const workerCwd = join(cwd, "worker"); const canonicalRoot = join(cwd, "canonical-state"); const foreignRoot = join(cwd, "foreign-state"); const teamName = "foreign-root-team"; const workerName = "worker-1"; const paneId = "%10"; const writeWorkerRoot = async (stateRoot: string, boundRoot: string, taskStatus: string) => { const teamRoot = join(stateRoot, "team", teamName); const workerRoot = join(teamRoot, "workers", workerName); await mkdir(workerRoot, { recursive: true }); await writeJson(join(workerRoot, "identity.json"), { name: workerName, assigned_tasks: ["1"], pane_id: paneId, worktree_path: workerCwd, team_state_root: boundRoot, }); await writeJson(join(workerRoot, "status.json"), { state: taskStatus === "completed" ? "done" : "working", current_task_id: "1", }); await writeJson(join(teamRoot, "tasks", "task-1.json"), { id: "1", owner: workerName, status: taskStatus, }); const metadata = { name: teamName, leader_cwd: cwd, team_state_root: boundRoot, leader_pane_id: "%42", workers: [{ name: workerName, pane_id: paneId, worktree_path: workerCwd, team_state_root: boundRoot }], }; await writeJson(join(teamRoot, "manifest.v2.json"), metadata); await writeJson(join(teamRoot, "config.json"), metadata); }; try { await mkdir(workerCwd, { recursive: true }); await writeWorkerRoot(canonicalRoot, canonicalRoot, "in_progress"); await writeWorkerRoot(foreignRoot, canonicalRoot, "completed"); process.env.TMUX = "1"; process.env.TMUX_PANE = paneId; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/${workerName}`; process.env.OMX_TEAM_WORKER = `${teamName}/${workerName}`; process.env.OMX_TEAM_STATE_ROOT = foreignRoot; process.env.OMX_TEAM_LEADER_CWD = cwd; const foreignStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd: workerCwd, session_id: "foreign-root-stop", }, { cwd: workerCwd }); assert.equal(foreignStop.outputJson?.decision, "block"); assert.equal(foreignStop.outputJson?.stopReason, "team_worker_worker-1_missing_state_dir"); assert.equal(existsSync(join(foreignRoot, "team", teamName, "workers", workerName, "worker-stop-nudge.json")), false); const foreignPostToolUse = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: workerCwd, session_id: "foreign-root-posttooluse", tool_name: "Bash", tool_input: { command: "printf ok" }, tool_response: { exit_code: 0 }, }, { cwd: workerCwd }); assert.equal(foreignPostToolUse.outputJson, null); assert.equal(existsSync(join(foreignRoot, "team", teamName, "workers", workerName, "heartbeat.json")), false); const markerForeignRoot = join(cwd, "marker-foreign-state"); const markerTeamRoot = join(markerForeignRoot, "team", teamName); await mkdir(join(markerTeamRoot, "workers", workerName), { recursive: true }); const markerMetadata = { name: teamName, team_state_root: canonicalRoot, workers: [{ name: workerName, team_state_root: canonicalRoot, worktree_path: workerCwd }], }; await writeJson(join(markerTeamRoot, "manifest.v2.json"), markerMetadata); await writeJson(join(markerTeamRoot, "config.json"), markerMetadata); process.env.OMX_TEAM_STATE_ROOT = markerForeignRoot; const markerForeignPostToolUse = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: workerCwd, session_id: "marker-foreign-root-posttooluse", tool_name: "Bash", tool_input: { command: "printf ok" }, tool_response: { exit_code: 0 }, }, { cwd: workerCwd }); assert.equal(markerForeignPostToolUse.outputJson, null); assert.equal(existsSync(join(markerForeignRoot, "team", teamName, "workers", workerName, "heartbeat.json")), false); process.env.OMX_TEAM_STATE_ROOT = canonicalRoot; process.env.OMX_TEAM_INTERNAL_WORKER = "malformed-internal"; process.env.OMX_TEAM_WORKER = `display-team/${workerName}`; const malformedInternalStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd: workerCwd, session_id: "malformed-internal-stop", }, { cwd: workerCwd }); assert.equal(malformedInternalStop.outputJson, null); const malformedInternalPostToolUse = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: workerCwd, session_id: "malformed-internal-posttooluse", tool_name: "Bash", tool_input: { command: "printf ok" }, tool_response: { exit_code: 0 }, }, { cwd: workerCwd }); assert.equal(malformedInternalPostToolUse.outputJson, null); assert.equal(existsSync(join(canonicalRoot, "team", teamName, "workers", workerName, "heartbeat.json")), false); assert.equal(existsSync(join(canonicalRoot, "team", teamName, "workers", workerName, "worker-stop-nudge.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop as a team-worker task failure when worker status is terminal but task evidence is not completed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-terminal-stale-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevLeaderCwd = process.env.OMX_TEAM_LEADER_CWD; try { await initTeamState( "worker-stale-team", "worker stale stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-stale" }, ); const stateDir = join(cwd, ".omx", "state"); const workerCwd = join(cwd, ".omx", "team", "worker-stale-team", "worktrees", "worker-1"); const workerDir = join(stateDir, "team", "worker-stale-team", "workers", "worker-1"); await mkdir(workerCwd, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: workerCwd, team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-stale-team", "tasks", "task-1.json"), { id: "1", subject: "stale hook task", description: "non-completed task should still block terminal worker Stop", status: "in_progress", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stale-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stale-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.OMX_TEAM_LEADER_CWD = cwd; const payload = { hook_event_name: "Stop", cwd: workerCwd, session_id: "sess-stop-team-worker-stale", thread_id: "thread-stop-team-worker-stale", }; const result = await dispatchCodexNativeHook(payload, { cwd: workerCwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true }, { cwd: workerCwd }, ); const camelReplay = await dispatchCodexNativeHook( { ...payload, stopHookActive: true }, { cwd: workerCwd }, ); assert.equal( (result.outputJson as { stopReason?: string } | null)?.stopReason, "team_worker_worker-1_1_in_progress", ); assert.equal(replay.outputJson, null); assert.equal(camelReplay.outputJson, null); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevLeaderCwd === "string") process.env.OMX_TEAM_LEADER_CWD = prevLeaderCwd; else delete process.env.OMX_TEAM_LEADER_CWD; await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks live team worker Stop replays but suppresses stale terminal worker repeats", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-repeat-")); try { await initTeamState( "worker-repeat-team", "worker stop repeat guard", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-repeat" }, ); const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-repeat-team", "workers", "worker-1"); const taskPath = join(stateDir, "team", "worker-repeat-team", "tasks", "task-1.json"); const workerCwd = join(cwd, ".omx", "team", "worker-repeat-team", "worktrees", "worker-1"); await mkdir(workerCwd, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: workerCwd, team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "working", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(taskPath, { id: "1", subject: "hook task", description: "finish hook task", status: "in_progress", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-repeat-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-repeat-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.OMX_TEAM_LEADER_CWD = cwd; const basePayload = { hook_event_name: "Stop", cwd: workerCwd, session_id: "sess-stop-team-worker-repeat", thread_id: "thread-stop-team-worker-repeat", turn_id: "turn-stop-team-worker-repeat-1", last_assistant_message: "I need to stop before this task is done.", }; const expectedInProgress = { decision: "block", reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (in_progress); continue the current assigned task or report a concrete blocker before stopping.", stopReason: "team_worker_worker-1_1_in_progress", systemMessage: "OMX team worker worker-1 is still assigned task 1 (in_progress).", }; const first = await dispatchCodexNativeHook(basePayload, { cwd: workerCwd }); const replay = await dispatchCodexNativeHook( { ...basePayload, stop_hook_active: true }, { cwd: workerCwd }, ); const freshTurn = await dispatchCodexNativeHook( { ...basePayload, turn_id: "turn-stop-team-worker-repeat-2", stop_hook_active: true }, { cwd: workerCwd }, ); const exempt = await dispatchCodexNativeHook( { ...basePayload, turn_id: "turn-stop-team-worker-exempt", stopReason: "context limit" }, { cwd: workerCwd }, ); await writeJson(taskPath, { id: "1", subject: "hook task", description: "finish hook task", status: "blocked", owner: "worker-1", created_at: new Date().toISOString(), }); const stateChanged = await dispatchCodexNativeHook( { ...basePayload, turn_id: "turn-stop-team-worker-repeat-3", stop_hook_active: true }, { cwd: workerCwd }, ); assert.deepEqual(first.outputJson, expectedInProgress); assert.deepEqual(replay.outputJson, expectedInProgress); assert.deepEqual(freshTurn.outputJson, expectedInProgress); assert.equal(exempt.outputJson, null); assert.deepEqual(stateChanged.outputJson, { decision: "block", reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (blocked); continue the current assigned task or report a concrete blocker before stopping.", stopReason: "team_worker_worker-1_1_blocked", systemMessage: "OMX team worker worker-1 is still assigned task 1 (blocked).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop for a team worker when assigned task is terminal and bypasses generic team blocking", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-terminal-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-team-terminal", "worker stop terminal fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-terminal" }, ); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "workers", "worker-1"); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "config.json"), { name: "worker-stop-team-terminal", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "manifest.v2.json"), { name: "worker-stop-team-terminal", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: cwd, team_state_root: join(cwd, ".omx", "state"), }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "tasks", "task-1.json"), { id: "1", subject: "hook task", description: "finish hook task", status: "completed", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stop-team-terminal/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-team-terminal/worker-1"; process.env.OMX_TEAM_STATE_ROOT = join(cwd, ".omx", "state"); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-terminal", }, { cwd }, ); const replay = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-terminal", turn_id: "turn-worker-stop-terminal-replay", }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(replay.outputJson, null); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); const stopNudges = tmuxLog.match(/send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/g) || []; assert.equal(stopNudges.length, 1, "allowed worker Stop should nudge leader exactly once inside cooldown"); const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(nudgeState.delivery, "sent"); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("steers worker Stop leader nudge directly when leader pane is busy", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-busy-leader-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-team-busy-leader", "worker stop busy leader", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-busy-leader" }, ); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { busyLeader: true })); await chmod(join(fakeBinDir, "tmux"), 0o755); const stateDir = join(cwd, ".omx", "state"); const teamDir = join(stateDir, "team", "worker-stop-team-busy-leader"); const workerDir = join(teamDir, "workers", "worker-1"); await writeJson(join(teamDir, "config.json"), { name: "worker-stop-team-busy-leader", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(teamDir, "manifest.v2.json"), { name: "worker-stop-team-busy-leader", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: cwd, team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(teamDir, "tasks", "task-1.json"), { id: "1", subject: "hook task", description: "finish hook task", status: "completed", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stop-team-busy-leader/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-team-busy-leader/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-busy-leader", }, { cwd }, ); assert.equal(result.outputJson, null); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[omx:team-notice-ledger:[a-f0-9]{24}\] Review current Team notices\./); assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l .*worker-stop-team-busy-leader|send-keys -t %42 Tab/); const submits = tmuxLog.match(/send-keys -t %42 C-m/g) || []; assert.equal(submits.length, 2, "busy worker-stop nudge should submit directly as steering, not queue via Tab"); const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(nudgeState.delivery, "steered"); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("dedupes allowed worker Stop leader nudges across workers in the same team window", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-team-dedupe-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-team-dedupe"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [ { name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }, { name: "worker-2", index: 2, pane_id: "%11", pid: 12311 }, ], }); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const first = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); const second = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-2" }, }); assert.equal(first.result, "sent"); assert.equal(second.result, "suppressed_team_cooldown"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); const stopNudges = tmuxLog.match(/send-keys -t %42 -l \[OMX\] worker-\d+ native Stop allowed/g) || []; assert.equal(stopNudges.length, 1, "same-team workers should share one leader nudge cooldown window"); const teamNudgeState = JSON.parse(await readFile(join(teamDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(teamNudgeState.worker, "worker-1"); assert.equal(teamNudgeState.delivery, "sent"); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("serializes concurrent allowed worker Stop leader nudges with a team lock", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-concurrent-dedupe-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-concurrent"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { sendDelayMs: 100 })); await chmod(join(fakeBinDir, "tmux"), 0o755); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [ { name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }, { name: "worker-2", index: 2, pane_id: "%11", pid: 12311 }, ], }); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const results = await Promise.all([ maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }), maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-2" }, }), ]); assert.equal(results.filter((result) => result.result === "sent").length, 1); assert.equal(results.filter((result) => result.result === "suppressed_team_lock_held").length, 1); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); const stopNudges = tmuxLog.match(/send-keys -t %42 -l \[OMX\] worker-\d+ native Stop allowed/g) || []; assert.equal(stopNudges.length, 1, "concurrent same-team workers should emit only one leader nudge"); assert.equal(existsSync(join(teamDir, "worker-stop-nudge.lock")), false); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("skips worker Stop leader nudge when team state is missing or shut down", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-missing-team-")); try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName: "removed-team", workerName: "worker-1" }, }); assert.equal(result.result, "team_state_gone_or_shutdown"); assert.equal(existsSync(join(stateDir, "team", "removed-team", "worker-stop-nudge.json")), false); await writeJson(join(stateDir, "team", "shutdown-team", "shutdown.json"), { started_at: new Date().toISOString(), }); const shutdownResult = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName: "shutdown-team", workerName: "worker-1" }, }); assert.equal(shutdownResult.result, "team_state_gone_or_shutdown"); assert.equal(existsSync(join(stateDir, "team", "shutdown-team", "worker-stop-nudge.json")), false); const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`); const deliveryEvents = (await readFile(deliveryLogPath, "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line)); const suppressedEvents = deliveryEvents.filter((event) => event.reason === "team_state_gone_or_shutdown"); assert.equal(suppressedEvents.length, 2, "late closed-team Stop nudges should be diagnostics, not queued prompts"); assert.equal(suppressedEvents.every((event) => event.result === "suppressed" && event.transport === "none"), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat old visible worker Stop transcript as pending queue state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-queue-dedupe-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "queued-stop-dedupe"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile( join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { busyLeader: true, captureText: `[OMX] worker-1 native Stop allowed. Run \`omx team status ${teamName}\`, read worker messages/results, then assign next task, reconcile completion, or shut down. [OMX_TMUX_INJECT]\n` + "• Working… (esc to interrupt)", }), ); await chmod(join(fakeBinDir, "tmux"), 0o755); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-2", index: 2, pane_id: "%11", pid: 12311 }], }); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-2" }, }); assert.equal(result.result, "steered"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[omx:team-notice-ledger:[a-f0-9]{24}\] Review current Team notices\./); assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l .*worker-2 native Stop allowed|send-keys -t %42 Tab/); const teamNudgeState = JSON.parse(await readFile(join(teamDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(teamNudgeState.worker, "worker-2"); assert.equal(teamNudgeState.delivery, "steered"); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("reports deferred when non-teardown persistence failure prevents worker Stop nudge cooldown state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-persist-fail-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-persist-fail"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeFile(join(teamDir, "workers"), "not a directory"); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); assert.equal(result.result, "deferred"); assert.equal(existsSync(join(teamDir, "worker-stop-nudge.json")), false); assert.equal(existsSync(join(teamDir, "workers", "worker-1", "worker-stop-nudge.json")), false); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`); const deliveryEvents = (await readFile(deliveryLogPath, "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line)); const deferredEvent = deliveryEvents.find((event) => event.event === "nudge_triggered" && event.result === "deferred"); assert.equal(deferredEvent?.team, teamName); assert.equal(deferredEvent?.from_worker, "worker-1"); assert.match(String(deferredEvent?.reason || ""), /EEXIST|ENOTDIR|not a directory|file already exists/); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("does not recreate team state when teardown removes it during worker Stop delivery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-teardown-race-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-teardown-race"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { removePathOnSend: teamDir })); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); assert.equal(result.result, "sent"); assert.equal(existsSync(teamDir), false, "worker Stop delivery must not recreate removed team state"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("does not recreate team state when teardown removes it before deferred worker Stop recording", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-deferred-teardown-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-deferred-teardown"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeFile( join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { currentCommand: "bash", captureText: "$ ", removePathOnCapture: teamDir, }), ); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); assert.equal(result.result, "team_state_gone_or_shutdown"); assert.equal(existsSync(teamDir), false, "deferred worker Stop recording must not recreate removed team state"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`); const deliveryEvents = (await readFile(deliveryLogPath, "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line)); assert.equal( deliveryEvents.some((event) => event.team === teamName && event.result === "suppressed" && event.transport === "none" && event.reason === "team_state_gone_or_shutdown" ), true, "teardown-race worker Stop nudges should be diagnostic suppression events, not queued prompts", ); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("allows worker Stop when the Stop nudge helper cannot deliver", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-helper-fail-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-helper-fail", "worker stop helper failure", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-helper-fail" }, ); const fakeBinDir = join(cwd, "fake-bin"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(join(cwd, "tmux.log"), { failSend: true })); await chmod(join(fakeBinDir, "tmux"), 0o755); const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-stop-helper-fail", "workers", "worker-1"); await writeJson(join(stateDir, "team", "worker-stop-helper-fail", "config.json"), { name: "worker-stop-helper-fail", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(stateDir, "team", "worker-stop-helper-fail", "manifest.v2.json"), { name: "worker-stop-helper-fail", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-stop-helper-fail", "tasks", "task-1.json"), { id: "1", status: "completed", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "worker-stop-helper-fail/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-helper-fail/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-helper-fail" }, { cwd }, ); assert.equal(result.outputJson, null); const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(nudgeState.delivery, "deferred"); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("does not treat failed or ambiguous worker task state as completed Stop evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-failed-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-failed-task", "worker stop failed task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-failed" }, ); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-stop-failed-task", "workers", "worker-1"); await writeJson(join(stateDir, "team", "worker-stop-failed-task", "config.json"), { name: "worker-stop-failed-task", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "failed", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-stop-failed-task", "tasks", "task-1.json"), { id: "1", status: "failed", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "worker-stop-failed-task/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-failed-task/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-failed", thread_id: "thread-stop-team-worker-failed", turn_id: "turn-stop-team-worker-failed", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.stopReason || ""), /non_completed_task_1_failed/); assert.match(JSON.stringify(result.outputJson), /team/i); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), false); const tmuxLog = existsSync(tmuxLogPath) ? await readFile(tmuxLogPath, "utf-8") : ""; assert.doesNotMatch(tmuxLog, /native Stop allowed/); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("blocks worker Stop on missing task assignment without relying on generic team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-missing-assignment-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-missing-assignment", "workers", "worker-1"); await mkdir(workerDir, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: [], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "idle", updated_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-missing-assignment/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-missing-assignment/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-missing-assignment", thread_id: "thread-stop-team-worker-missing-assignment", turn_id: "turn-stop-team-worker-missing-assignment", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_worker_worker-1_missing_task_assignment"); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), false); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("blocks unresolved worker Stop before generic auto-nudge can bypass it", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-missing-state-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.OMX_TEAM_WORKER = "worker-missing-state/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-missing-state/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-missing-state", thread_id: "thread-stop-team-worker-missing-state", turn_id: "turn-stop-team-worker-missing-state", last_assistant_message: "Should I proceed?", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_worker_worker-1_missing_state_dir"); assert.doesNotMatch(JSON.stringify(result.outputJson), /auto_nudge/); const tmuxLog = existsSync(tmuxLogPath) ? await readFile(tmuxLogPath, "utf-8") : ""; assert.doesNotMatch(tmuxLog, /native Stop allowed/); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("prefers canonical internal worker identity over public worker identity for Stop nudges", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-internal-env-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const workerDir = join(stateDir, "team", "internal-stop-team", "workers", "worker-1"); await writeJson(join(stateDir, "team", "internal-stop-team", "config.json"), { name: "internal-stop-team", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "internal-stop-team", "tasks", "task-1.json"), { id: "1", status: "completed", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "public-stop-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "internal-stop-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-internal-env", thread_id: "thread-stop-team-worker-internal-env", turn_id: "turn-stop-team-worker-internal-env", }, { cwd }, ); assert.equal(result.outputJson, null); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), true); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("blocks worker Stop when canonical task ownership has a newer non-terminal task", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-owned-task-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const workerDir = join(stateDir, "team", "worker-owned-task", "workers", "worker-1"); await writeJson(join(stateDir, "team", "worker-owned-task", "config.json"), { name: "worker-owned-task", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-owned-task", "tasks", "task-1.json"), { id: "1", status: "completed", owner: "worker-1", }); await writeJson(join(stateDir, "team", "worker-owned-task", "tasks", "task-2.json"), { id: "2", status: "in_progress", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "worker-owned-task/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-owned-task/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-owned-task", thread_id: "thread-stop-team-worker-owned-task", turn_id: "turn-stop-team-worker-owned-task", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_worker_worker-1_2_in_progress"); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), false); const tmuxLog = existsSync(tmuxLogPath) ? await readFile(tmuxLogPath, "utf-8") : ""; assert.doesNotMatch(tmuxLog, /native Stop allowed/); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output from canonical team state when coarse mode state is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-")); try { await initTeamState( "canonical-team", "canonical stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical" }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from canonical team state owned by another thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-other-thread-")); try { await initTeamState( "canonical-other-thread-team", "canonical other-thread stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-thread" }, ); const manifestPath = join(cwd, ".omx", "state", "team", "canonical-other-thread-team", "manifest.v2.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf-8")) as Record; await writeJson(manifestPath, { ...manifest, leader: { ...(manifest.leader as Record | undefined), thread_id: "thread-other", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-thread", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop from canonical team state owned by the current thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-current-thread-")); try { await initTeamState( "canonical-current-thread-team", "canonical current-thread stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-current-thread" }, ); const manifestPath = join(cwd, ".omx", "state", "team", "canonical-current-thread-team", "manifest.v2.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf-8")) as Record; await writeJson(manifestPath, { ...manifest, leader: { ...(manifest.leader as Record | undefined), thread_id: "thread-current", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-current-thread", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-current-thread-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits one concise final decision summary and auto-finalize guidance when release-readiness already has a stable final recommendation and no active worker tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-release-readiness-finalize-")); try { await initTeamState( "release-ready-team", "release readiness finalize", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-release-ready" }, ); await writeReleaseReadinessLeaderAttention( "release-ready-team", "sess-stop-release-ready", cwd, { workRemaining: false }, ); await writeReleaseReadinessStateMarker( "sess-stop-release-ready", "release-ready-team", cwd, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-release-ready", thread_id: "thread-stop-release-ready", turn_id: "turn-stop-release-ready-1", mode: "release-readiness", last_assistant_message: "Launch-ready: yes", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: 'Stable final recommendation already reached with no active worker tasks. Emit exactly one concise final decision summary aligned to "Launch-ready: yes." with no filler or residual acknowledgements (for example "yes"), then stop.', stopReason: "release_readiness_auto_finalize", systemMessage: "OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not auto-finalize non-release team stops that happen to contain a stable recommendation summary", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-non-release-readiness-control-")); try { await initTeamState( "general-review-team", "general team stop control", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-general-review" }, ); await writeReleaseReadinessLeaderAttention( "general-review-team", "sess-stop-general-review", cwd, { workRemaining: false }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-general-review", thread_id: "thread-stop-general-review", turn_id: "turn-stop-general-review-1", last_assistant_message: "Launch-ready: yes", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (general-review-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("honors terminal team run-state before later canonical-team Stop fallback", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-run-state-canonical-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-team-terminal-run-state"; await initTeamState( "terminal-run-state-team", "terminal team stop canonical fallback regression", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: sessionId }, ); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, mode: "team", active: false, outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-04-27T12:00:00.000Z", updated_at: "2026-04-27T12:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-team-terminal-run-state", turn_id: "turn-stop-team-terminal-run-state-1", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-fires canonical-team Stop output for a later fresh Stop reply when coarse mode state is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-refire-")); try { await initTeamState( "canonical-team-refire", "canonical stop fallback refire", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-refire" }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-refire", thread_id: "thread-stop-team-canonical-refire", turn_id: "turn-stop-team-canonical-refire-1", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-refire", thread_id: "thread-stop-team-canonical-refire", turn_id: "turn-stop-team-canonical-refire-2", stop_hook_active: true, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-team-refire) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from canonical team state alone when the canonical phase is terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-")); try { await initTeamState( "terminal-team", "terminal stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-terminal" }, ); await writeJson(join(cwd, ".omx", "state", "team", "terminal-team", "phase.json"), { current_phase: "complete", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-terminal", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output from canonical team state when manifest session ownership is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-legacy-")); try { await initTeamState( "legacy-team", "legacy stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-legacy" }, ); const manifestPath = join(cwd, ".omx", "state", "team", "legacy-team", "manifest.v2.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf-8")) as Record; await writeJson(manifestPath, { ...manifest, leader: { ...(manifest.leader as Record | undefined), session_id: "", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-legacy", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (legacy-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("reads canonical Stop fallback team state from OMX_TEAM_STATE_ROOT when configured", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-root-")); const sharedRoot = join(cwd, "shared-root"); const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = sharedRoot; await initTeamState( "canonical-root-team", "canonical stop root fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-root", OMX_TEAM_STATE_ROOT: sharedRoot }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-root", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-root-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); assert.equal(existsSync(join(sharedRoot, "team", "canonical-root-team", "phase.json")), true); } finally { if (typeof priorTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("ignores stale source-root team Stop fallback when OMX_TEAM_STATE_ROOT is authoritative", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-stale-source-root-")); const teamStateRoot = join(cwd, "shared-team-state"); const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await mkdir(join(teamStateRoot, "team", "stale-source-team"), { recursive: true }); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "stale-source-team", current_phase: "team-exec", }); await writeJson(join(teamStateRoot, "team", "stale-source-team", "phase.json"), { current_phase: "team-exec", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stale-source-team", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { if (typeof priorTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-")); const teamStateRoot = join(cwd, "shared-team-state"); const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; await initTeamState( "env-root-team", "env root stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-env-root", OMX_TEAM_STATE_ROOT: teamStateRoot, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-env-root", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (env-root-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("silently ignores Stop when a session-scoped team id is not bound to session.json", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-session-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-live-team"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-other-team" }); await writeJson(join(stateDir, "sessions", "sess-live-team", "team-state.json"), { active: true, mode: "team", current_phase: "team-exec", team_name: "session-live-team", }); await writeJson(join(stateDir, "team", "session-live-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-live-team", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output for active ralplan skill with matching active mode state and without active subagents", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-skill"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill", "ralplan-state.json"), { active: true, current_phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Status: continue_from_artifact/); assert.match(String(result.outputJson?.reason ?? ""), /ralplan is still active \(phase: planning\)/); assert.match(String(result.outputJson?.reason ?? ""), /continue from the current ralplan artifact/i); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); assert.match(String(result.outputJson?.systemMessage ?? ""), /complete, paused for review, waiting for input, or still continuing/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop release after owner-session ralplan CLI completion when only local lifecycle evidence is available", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-owner-alias-complete-")); const previousSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); const nativeSessionId = "native-id"; const ownerSessionId = "omx-owner-id"; await mkdir(join(stateDir, "sessions", nativeSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: nativeSessionId, native_session_id: nativeSessionId, owner_omx_session_id: ownerSessionId, cwd, }); await writeJson(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: nativeSessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: nativeSessionId }], }); await writeJson(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: nativeSessionId, }); const architectCompletedAt = "2026-06-30T00:00:00.000Z"; const criticStartedAt = "2026-06-30T00:01:00.000Z"; const criticCompletedAt = "2026-06-30T00:02:00.000Z"; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [nativeSessionId]: { session_id: nativeSessionId, leader_thread_id: "thread-leader", updated_at: criticCompletedAt, threads: { "thread-leader": { thread_id: "thread-leader", kind: "leader", first_seen_at: architectCompletedAt, last_seen_at: architectCompletedAt, turn_count: 1 }, "thread-architect": { thread_id: "thread-architect", kind: "subagent", first_seen_at: architectCompletedAt, last_seen_at: architectCompletedAt, completed_at: architectCompletedAt, turn_count: 1, mode: "architect" }, "thread-critic": { thread_id: "thread-critic", kind: "subagent", first_seen_at: criticStartedAt, last_seen_at: criticCompletedAt, completed_at: criticCompletedAt, turn_count: 1, mode: "critic" }, }, }, }, }); const consensusGate = { required: true, complete: true, sequence: ["architect-review", "critic-review"], planning_artifacts_are_not_consensus: true, required_review_roles: ["architect", "critic"], ralplan_architect_review: { agent_role: "architect", verdict: "approve", provenance_kind: "native_subagent", session_id: nativeSessionId, thread_id: "thread-architect", artifact_path: ".omx/artifacts/architect.md", tracker_path: ".omx/state/subagent-tracking.json", }, ralplan_critic_review: { agent_role: "critic", verdict: "approve", provenance_kind: "native_subagent", session_id: nativeSessionId, thread_id: "thread-critic", artifact_path: ".omx/artifacts/critic.md", tracker_path: ".omx/state/subagent-tracking.json", }, }; process.env.OMX_SESSION_ID = ownerSessionId; const writeResult = await executeStateOperation("state_write", { mode: "ralplan", active: false, current_phase: "complete", status: "complete", terminal_state: "complete", ralplan_consensus_gate: consensusGate, workingDirectory: cwd, }); assert.equal(writeResult.isError, true); assert.match( String((writeResult.payload as { error?: unknown }).error ?? ""), /documented_host_consensus_receipt_unavailable/, ); const preservedRalplan = JSON.parse( await readFile(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json"), "utf-8"), ) as Record; assert.equal(preservedRalplan.active, true); assert.equal(preservedRalplan.current_phase, "planning"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal( result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact", ); assert.match( String(result.outputJson?.reason ?? ""), /ralplan|planning/i, ); } finally { if (typeof previousSessionId === "string") process.env.OMX_SESSION_ID = previousSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale ralplan skill-active state when the matching mode state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-stale-skill"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-stale-skill" }); await writeJson(join(stateDir, "sessions", "sess-stop-stale-skill", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: "sess-stop-stale-skill", active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: "sess-stop-stale-skill", }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-stale-skill", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block when canonical root ralplan state is inactive but session ralplan state is stale active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralplan-root-inactive-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-stale-session-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "reviewing", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clears stale ralplan Stop cache when authoritative state is terminal inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-terminal-cache-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-terminal-cache"; const threadId = "thread-stop-ralplan-terminal-cache"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: sessionId, active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", status: "complete", run_outcome: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: sessionId, active_skills: [], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", status: "complete", run_outcome: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "native-stop-state.json"), { sessions: { [sessionId]: { last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`, updated_at: "2026-07-04T00:00:00.000Z", }, [threadId]: { last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`, updated_at: "2026-07-04T00:00:00.000Z", }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: threadId, stop_hook_active: true, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as { sessions?: Record }; assert.deepEqual(stopState.sessions, {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when canonical root inactive ralplan state lacks project context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-canonical-root-no-cwd-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-canonical-root-no-cwd"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: sessionId, active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when root inactive ralplan state belongs to another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-other-session-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-current-active-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: "sess-stop-old-ralplan", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: "sess-stop-old-ralplan", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when root inactive ralplan state is unscoped", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-unscoped-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-unscoped-root-current-active"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when unscoped root completion lacks a plan artifact", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-no-plan-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-unscoped-root-no-plan-artifact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", planning_complete: true, updated_at: "2026-06-21T08:05:00.000Z", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, updated_at: "2026-06-21T08:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not loop when newer unscoped root ralplan complete state shadows stale session planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralplan-newer-root-complete-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-stale-session-ralplan-newer-root"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, updated_at: "2026-06-21T08:00:00.000Z", }); await mkdir(join(cwd, ".omx", "plans"), { recursive: true }); await writeFile(join(cwd, ".omx", "plans", "prd-issue-2923.md"), "# PRD\n"); await writeFile(join(cwd, ".omx", "plans", "test-spec-issue-2923.md"), "# Test spec\n"); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", cwd, planning_complete: true, latest_plan_path: ".omx/plans/prd-issue-2923.md", updated_at: "2026-06-21T08:05:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, tool_name: "apply_patch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when newer unscoped root completion belongs to another worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-cross-worktree-root-complete-")); try { const otherWorktree = join(tmpdir(), "omx-native-hook-other-worktree-2925"); const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-cross-worktree-root"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, updated_at: "2026-06-21T08:00:00.000Z", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", cwd: otherWorktree, planning_complete: true, latest_plan_path: ".omx/plans/prd-issue-2925.md", updated_at: "2026-06-21T08:05:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, tool_name: "apply_patch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when newer unscoped root completion lacks project context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-root-complete-no-cwd-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-root-complete-no-cwd"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, updated_at: "2026-06-21T08:00:00.000Z", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", planning_complete: true, latest_plan_path: ".omx/plans/prd-issue-2925.md", updated_at: "2026-06-21T08:05:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, tool_name: "apply_patch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block stale session ralplan when root ralplan is terminal and another root skill is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-ralplan-other-root-skill-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-stale-ralplan-other-root-skill"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "intent-first", session_id: sessionId, active_skills: [{ skill: "deep-interview", phase: "intent-first", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking session ralplan when canonical root state is not inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-active-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-session-ralplan-root-active"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale ralplan skill-active when canonical run-state is terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-terminal-ralplan-run-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-terminal-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, mode: "ralplan", active: false, outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-01T00:00:00.000Z", updated_at: "2026-05-01T00:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale ralplan skill-active when pinned mode state belongs to another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-foreign-ralplan-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-current-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: "sess-other-ralplan", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns an explicit ralplan waiting status while subagents are still active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent", "ralplan-state.json"), { active: true, current_phase: "planning", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-stop-skill-subagent": { session_id: "sess-stop-skill-subagent", leader_thread_id: "leader-1", updated_at: new Date().toISOString(), threads: { "leader-1": { thread_id: "leader-1", kind: "leader", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, "sub-1": { thread_id: "sub-1", kind: "subagent", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-subagent", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Status: waiting/); assert.match(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/); assert.match(String(result.outputJson?.reason ?? ""), /then continue from the current ralplan artifact/i); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_waiting_subagent"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not report ralplan subagent waiting when notify-fallback already recorded completion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-complete-")); try { const stateDir = join(cwd, ".omx", "state"); const now = new Date().toISOString(); await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent-complete"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent-complete" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "ralplan-state.json"), { active: true, current_phase: "planning", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-stop-skill-subagent-complete": { session_id: "sess-stop-skill-subagent-complete", leader_thread_id: "leader-1", updated_at: now, threads: { "leader-1": { thread_id: "leader-1", kind: "leader", first_seen_at: now, last_seen_at: now, turn_count: 1, }, "sub-1": { thread_id: "sub-1", kind: "subagent", first_seen_at: now, last_seen_at: now, completed_at: now, last_completed_turn_id: "turn-complete-1", completion_source: "notify-fallback-watcher", turn_count: 2, }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-subagent-complete", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale root ralplan skill when the explicit session-scoped canonical skill state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-stale-root-skill", thread_id: "thread-stop-stale-root-skill", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop while autoresearch is active without validator completion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autoresearch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autoresearch"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-autoresearch", cwd }); await writeJson(join(stateDir, "sessions", "sess-stop-autoresearch", "autoresearch-state.json"), { active: true, mode: "autoresearch", current_phase: "executing", session_id: "sess-stop-autoresearch", validation_mode: "mission-validator-script", mission_validator_command: "node scripts/validate.js", completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json', }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autoresearch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX autoresearch is still active (phase: executing); continue until validator evidence is complete before stopping.", stopReason: "autoresearch_executing", systemMessage: "OMX autoresearch is still active (phase: executing); continue until validator evidence is complete before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop once autoresearch validator evidence is complete", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autoresearch-complete-")); try { const stateDir = join(cwd, ".omx", "state"); const specDir = join(cwd, '.omx', 'specs', 'autoresearch-demo'); await mkdir(join(stateDir, "sessions", "sess-stop-autoresearch-complete"), { recursive: true }); await mkdir(specDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-autoresearch-complete", cwd }); await writeJson(join(stateDir, "sessions", "sess-stop-autoresearch-complete", "autoresearch-state.json"), { active: true, mode: "autoresearch", current_phase: "reviewing", session_id: "sess-stop-autoresearch-complete", validation_mode: "mission-validator-script", mission_validator_command: "node scripts/validate.js", completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json', }); await writeJson(join(specDir, 'completion.json'), { status: 'passed', passed: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autoresearch-complete", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale root autoresearch state when the explicit session has no scoped autoresearch state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-autoresearch-")); try { const stateDir = join(cwd, ".omx", "state"); const specDir = join(cwd, '.omx', 'specs', 'autoresearch-demo'); await mkdir(join(stateDir, 'sessions', 'sess-current'), { recursive: true }); await mkdir(specDir, { recursive: true }); await writeJson(join(stateDir, 'session.json'), { session_id: 'sess-current', cwd }); await writeJson(join(stateDir, 'autoresearch-state.json'), { active: true, mode: 'autoresearch', current_phase: 'executing', validation_mode: 'mission-validator-script', mission_validator_command: 'node scripts/validate.js', completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json', }); const result = await dispatchCodexNativeHook( { hook_event_name: 'Stop', cwd, session_id: 'sess-current', }, { cwd }, ); assert.equal(result.omxEventName, 'stop'); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale root autoresearch state when the explicit session directory is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-missing-session-autoresearch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "autoresearch-state.json"), { active: true, mode: "autoresearch", current_phase: "executing", validation_mode: "mission-validator-script", mission_validator_command: "node scripts/validate.js", completion_artifact_path: ".omx/specs/autoresearch-demo/completion.json", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "missing-session", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop solely because deep-interview is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview", "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview", "deep-interview-state.json"), { active: true, current_phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop when deep-interview has a pending omx question obligation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question", thread_id: "thread-stop-deep-interview-question", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-stop-deep-interview-question", thread_id: "thread-stop-deep-interview-question", question_enforcement: { obligation_id: "obligation-1", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:20:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question", thread_id: "thread-stop-deep-interview-question", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop when a same-session deep-interview question obligation is pending even after the mode marked itself inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-inactive-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-inactive" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question-inactive", thread_id: "thread-stop-deep-interview-question-inactive", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive", "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "intent-first", lifecycle_outcome: "askuserQuestion", run_outcome: "blocked_on_user", completed_at: "2026-04-19T03:20:30.000Z", session_id: "sess-stop-deep-interview-question-inactive", thread_id: "thread-stop-deep-interview-question-inactive", question_enforcement: { obligation_id: "obligation-inactive", source: "omx-question", status: "pending", lifecycle_outcome: "askuserQuestion", requested_at: "2026-04-19T03:20:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question-inactive", thread_id: "thread-stop-deep-interview-question-inactive", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not re-block Stop after a same-session deep-interview question record is already answered", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-answered-")); try { const sessionId = "sess-stop-deep-interview-question-answered"; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(join(sessionDir, "questions"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: "thread-stop-deep-interview-question-answered", }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "intent-first", lifecycle_outcome: "askuserQuestion", run_outcome: "blocked_on_user", completed_at: "2026-04-19T03:20:30.000Z", session_id: sessionId, thread_id: "thread-stop-deep-interview-question-answered", question_enforcement: { obligation_id: "obligation-answered", source: "omx-question", status: "pending", lifecycle_outcome: "askuserQuestion", requested_at: "2026-04-19T03:20:00.000Z", }, }); await writeJson(join(sessionDir, "questions", "question-answered.json"), { kind: "omx.question/v1", question_id: "question-answered", session_id: sessionId, created_at: "2026-04-19T03:20:05.000Z", updated_at: "2026-04-19T03:20:10.000Z", status: "answered", question: "What should happen next?", options: [{ label: "Continue", value: "continue" }], allow_other: false, other_label: "Other", multi_select: false, type: "single-answerable", source: "deep-interview", answer: { kind: "option", value: "continue", selected_labels: ["Continue"], selected_values: ["continue"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-deep-interview-question-answered", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const state = JSON.parse( await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"), ) as { lifecycle_outcome?: string; question_enforcement?: { status?: string; question_id?: string; satisfied_at?: string }; run_outcome?: string; }; assert.equal(state.question_enforcement?.status, "satisfied"); assert.equal(state.question_enforcement?.question_id, "question-answered"); assert.ok(state.question_enforcement?.satisfied_at); assert.equal(state.lifecycle_outcome, undefined); assert.equal(state.run_outcome, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking pending deep-interview question Stop replays until the obligation changes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-replay-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-replay"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-replay" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-replay", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question-replay", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-replay", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: "obligation-replay", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:20:00.000Z", }, }); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question-replay", }; const expected = { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal(first.omxEventName, "stop"); assert.deepEqual(first.outputJson, expected); assert.equal(replay.omxEventName, "stop"); assert.deepEqual(replay.outputJson, expected); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop once the deep-interview question obligation is satisfied or cleared", async () => { for (const status of ["satisfied", "cleared"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-deep-interview-question-${status}-`)); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", `sess-stop-deep-interview-question-${status}`), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: `sess-stop-deep-interview-question-${status}` }); await writeJson(join(stateDir, "sessions", `sess-stop-deep-interview-question-${status}`, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: `sess-stop-deep-interview-question-${status}`, }); await writeJson(join(stateDir, "sessions", `sess-stop-deep-interview-question-${status}`, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: `obligation-${status}`, source: "omx-question", status, requested_at: "2026-04-19T03:20:00.000Z", ...(status === "satisfied" ? { question_id: "question-1", satisfied_at: "2026-04-19T03:21:00.000Z" } : { cleared_at: "2026-04-19T03:21:00.000Z", clear_reason: "error" }), }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: `sess-stop-deep-interview-question-${status}`, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("ignores pending deep-interview question obligations from another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-foreign-session-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-other"), { recursive: true }); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-other", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-other", }); await writeJson(join(stateDir, "sessions", "sess-other", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: "obligation-foreign", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:20:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks a new same-session deep-interview question obligation even after an earlier round was satisfied", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-next-round-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-next-round"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-next-round" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-next-round", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question-next-round", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-next-round", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: "obligation-next-round", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:22:00.000Z", question_id: "question-old-round", satisfied_at: "2026-04-19T03:21:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question-next-round", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores root skill-active fallback from a different thread when evaluating Stop", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-foreign-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: "", thread_id: "other-thread", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-main", thread_id: "main-thread", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns a non-blocking Stop document-refresh warning before auto-nudge when Ralph is not active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const output = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh", last_assistant_message: "Launch-ready: yes", }, { cwd })); assert.deepEqual(Object.keys(output).sort(), ["systemMessage"]); assert.equal("hookSpecificOutput" in output, false); assert.equal("decision" in output, false); assert.equal("reason" in output, false); assert.equal("stopReason" in output, false); assert.match(String(output.systemMessage ?? ""), /Document-refresh warning/); assert.match(String(output.systemMessage ?? ""), /staged \+ unstaged changes/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not warn on ordinary non-terminal Stop attempts before auto-nudge", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-nonterminal-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh-nonterminal", last_assistant_message: "Continuing implementation; next I will run focused tests.", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("dedupes identical Stop document-refresh warnings during active Stop-hook replays", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-dedupe-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh-dedupe", last_assistant_message: "Launch-ready: yes", } as const; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.match(JSON.stringify(first.outputJson), /Document-refresh warning/); assert.equal(replay.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses Stop document-refresh warning when the final handoff message includes an exemption", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-exempt-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh-exempt", last_assistant_message: `${DOCUMENT_REFRESH_EXEMPTION_PREFIX} internal-only behavior verified`, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Codex App Stop when Ralph is marked complete without completion-audit evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-missing-")); try { const sessionId = "sess-ralph-complete-missing"; const statePath = join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"); await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd }); await writeJson(statePath, { active: false, mode: "ralph", current_phase: "complete", session_id: sessionId, completed_at: "2026-05-10T12:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, last_assistant_message: "Done. Ralph complete.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); const reason = String(result.outputJson?.reason); assert.match(reason, /Ralph completion audit is missing required evidence/); assert.match(reason, /set "completion_audit" on the Ralph state object/); assert.doesNotMatch(reason, /state\.completion_audit/); assert.match(reason, /repo-relative JSON file/); assert.match(reason, /Markdown artifacts and flat top-level checklist\/evidence fields are not accepted/); assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit"); const reopened = JSON.parse(await readFile(statePath, "utf-8")) as Record; assert.equal(reopened.active, false); assert.equal(reopened.current_phase, "complete"); assert.equal(reopened.completion_audit_gate, "blocked"); assert.equal(reopened.completion_audit_missing_reason, "missing_completion_audit"); assert.equal(reopened.completed_at, "2026-05-10T12:00:00.000Z"); const repeat = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, last_assistant_message: "Done. Ralph complete.", }, { cwd }, ); assert.equal(repeat.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit"); assert.doesNotMatch(String(repeat.outputJson?.reason), /Ralph is still active/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Codex App Stop when complete Ralph state carries checklist and verification evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-present-")); try { const sessionId = "sess-ralph-complete-present"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd }); await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"), { active: false, mode: "ralph", current_phase: "complete", session_id: sessionId, completed_at: "2026-05-10T12:00:00.000Z", completion_audit: { passed: true, prompt_to_artifact_checklist: ["issue #2260 fixed", "tests added"], verification_evidence: ["node --test dist/scripts/__tests__/codex-native-hook.test.js"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, last_assistant_message: "Done with completion audit evidence recorded.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeFile( join(stateDir, "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", }), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_executing", systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("silently ignores Stop when a session-scoped Ralph id is not bound to session.json", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-session-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-live-ralph"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-other-ralph" }); await writeJson(join(stateDir, "sessions", "sess-live-ralph", "ralph-state.json"), { active: true, current_phase: "executing", session_id: "sess-live-ralph", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-live-ralph", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale session-scoped Ralph state that belongs to another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await mkdir(join(stateDir, "sessions", "sess-stale"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-stale", "ralph-state.json"), { active: true, current_phase: "starting", session_id: "sess-stale", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("bounds Stop replays when session.json points to an identity-indeterminate owner", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-current-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const nativeStopStatePath = join(stateDir, "native-stop-state.json"); await mkdir(join(stateDir, "sessions", "sess-dead"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-dead", cwd, pid: Number.MAX_SAFE_INTEGER, started_at: "2026-01-01T00:00:00.000Z", }); await writeJson(join(stateDir, "sessions", "sess-dead", "ralph-state.json"), { active: true, current_phase: "verifying", session_id: "sess-dead", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "team", phase: "team-exec", active_skills: [{ skill: "team", phase: "team-exec", active: true, session_id: "sess-dead" }], }); await writeJson(nativeStopStatePath, { sessions: { "sess-dead": { last_signature: "ralph-stop|sess-dead|thread-1|no-message|verifying", updated_at: "2026-04-20T21:00:00.000Z", }, }, }); const ralphStatePath = join(stateDir, "sessions", "sess-dead", "ralph-state.json"); const skillStatePath = join(stateDir, "skill-active-state.json"); const ralphStateBefore = await readFile(ralphStatePath, "utf-8"); const skillStateBefore = await readFile(skillStatePath, "utf-8"); const nativeStopStateBefore = await readFile(nativeStopStatePath, "utf-8"); const payload = { hook_event_name: "Stop" as const, cwd, session_id: "sess-dead", thread_id: "thread-1", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.equal(first.outputJson, null); assert.equal(replay.omxEventName, "stop"); assert.equal(replay.outputJson, null); assert.equal(await readFile(nativeStopStatePath, "utf-8"), nativeStopStateBefore); assert.equal(await readFile(ralphStatePath, "utf-8"), ralphStateBefore); assert.equal(await readFile(skillStatePath, "utf-8"), skillStateBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("never reinjects identical Stop authorization failures", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-malformed-session-pointer-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeFile(join(stateDir, "session.json"), "{not-json"); const payload = { hook_event_name: "Stop" as const, cwd, session_id: "sess-current", last_assistant_message: "The provenance tools are denied, so the pointer cannot be repaired.", }; const attempts = []; for (let index = 0; index < 26; index += 1) { attempts.push(await dispatchCodexNativeHook(payload, { cwd })); } assert.deepEqual(attempts.map((attempt) => attempt.omxEventName), Array(26).fill("stop")); assert.deepEqual(attempts.map((attempt) => attempt.outputJson), Array(26).fill(null)); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps Stop authorization failures terminal across session, cwd, and reason changes", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-auth-variants-")); try { const malformedCwd = join(root, "malformed"); const foreignCwd = join(root, "foreign"); const unmatchedCwd = join(root, "unmatched"); await mkdir(join(malformedCwd, ".omx", "state"), { recursive: true }); await mkdir(join(foreignCwd, ".omx", "state"), { recursive: true }); await writeFile(join(malformedCwd, ".omx", "state", "session.json"), "{not-json"); await writeJson(join(foreignCwd, ".omx", "state", "session.json"), { session_id: "foreign-owner", native_session_id: "foreign-owner", cwd: join(root, "other-worktree"), pid: process.pid, }); await writeSessionStart(unmatchedCwd, "selected-owner", { nativeSessionId: "selected-owner", pid: process.pid, }); const cases = [ { cwd: malformedCwd, session_id: "malformed-session" }, { cwd: foreignCwd, session_id: "foreign-owner" }, { cwd: unmatchedCwd, session_id: "unmatched-session" }, ]; for (const payload of cases) { const first = await dispatchCodexNativeHook({ hook_event_name: "Stop", ...payload }, { cwd: payload.cwd }); const changed = await dispatchCodexNativeHook({ hook_event_name: "Stop", ...payload, session_id: `${payload.session_id}-changed`, stop_hook_active: true, }, { cwd: payload.cwd }); assert.equal(first.outputJson, null); assert.equal(changed.outputJson, null); assert.equal(existsSync(join(payload.cwd, ".omx", "state", "native-stop-state.json")), false); } } finally { await rm(root, { recursive: true, force: true }); } }); it("does not inherit authority across sessions and restores ordinary Stop behavior after progress", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-auth-session-isolation-")); try { const stateDir = join(cwd, ".omx", "state"); const ownerSessionId = "selected-owner"; await writeSessionStart(cwd, ownerSessionId, { nativeSessionId: ownerSessionId, pid: process.pid, }); await writeSessionSkillActiveState(stateDir, ownerSessionId, "ralph", "executing"); const ralphStatePath = join(stateDir, "sessions", ownerSessionId, "ralph-state.json"); await writeJson(ralphStatePath, { active: true, mode: "ralph", current_phase: "executing", session_id: ownerSessionId, workingDirectory: cwd, }); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const foreignStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "foreign-session", }, { cwd }); assert.equal(foreignStop.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); const authorizedBlocked = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: ownerSessionId, }, { cwd }); assert.equal(authorizedBlocked.outputJson?.decision, "block"); assert.match(String(authorizedBlocked.outputJson?.stopReason), /^ralph_/); await writeJson(ralphStatePath, { active: false, mode: "ralph", current_phase: "complete", session_id: ownerSessionId, workingDirectory: cwd, completion_audit: { passed: true, prompt_to_artifact_checklist: ["Stop authorization remained session-scoped."], verification_evidence: ["The selected owner retained ordinary Stop enforcement."], }, }); await writeJson(join(stateDir, "sessions", ownerSessionId, "skill-active-state.json"), { active: false, skill: "ralph", phase: "complete", session_id: ownerSessionId, active_skills: [], }); const authorizedAfterProgress = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: ownerSessionId, }, { cwd }); assert.equal(authorizedAfterProgress.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not hard-block Stop on stale session-scoped Ralph starting state after visible active modes are cleared", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cleared-stale-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-cleared-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: sessionId, }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralph", active_skills: [], }); const listActive = await executeStateOperation("state_list_active", { workingDirectory: cwd, }); assert.deepEqual(listActive.payload, { active_modes: [] }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop from stale orphaned session-scoped Ralph starting iteration zero state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-orphan-starting-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stale-orphan-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", iteration: 0, session_id: sessionId, updated_at: "2000-01-01T00:00:00.000Z", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", session_id: sessionId, active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: sessionId }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-verifier-terminal", last_assistant_message: "APPROVE: read-only verifier evidence is fresh and sufficient.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop on visible active session-scoped Ralph starting state and reports its path", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-visible-starting-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-visible-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: sessionId }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/sess-visible-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_starting", systemMessage: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/sess-visible-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("retires prompt-seeded Ralph starting state when canonical Ralph already completed with audit", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-shadowed-starting-")); try { const stateDir = join(cwd, ".omx", "state"); const nativeSessionId = "native-hook-seed"; const canonicalSessionId = "omx-runtime-session"; await mkdir(join(stateDir, "sessions", nativeSessionId), { recursive: true }); await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: nativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: nativeSessionId, iteration: 0, task_slug: "mvp-h-local-method-preflight-execution", started_at: "2026-05-14T07:00:00.000Z", }); await writeJson(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", session_id: nativeSessionId, active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: nativeSessionId }], }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: false, mode: "ralph", current_phase: "complete", session_id: canonicalSessionId, completed_at: "2026-05-14T07:30:00.000Z", completion_audit: { passed: true, prompt_to_artifact_checklist: ["task evidence mapped"], verification_evidence: ["fresh verification evidence recorded"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const retiredState = JSON.parse(await readFile(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), "utf-8")); assert.equal(retiredState.active, false); assert.equal(retiredState.current_phase, "complete"); assert.equal(retiredState.stop_reason, "shadowed_by_completed_canonical_ralph"); assert.equal(retiredState.shadowed_by_completed_canonical_ralph.session_id, canonicalSessionId); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not retire prompt-seeded Ralph starting state from a completed canonical Ralph owned by another thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-shadowed-thread-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const nativeSessionId = "native-hook-seed"; const canonicalSessionId = "omx-runtime-session"; await mkdir(join(stateDir, "sessions", nativeSessionId), { recursive: true }); await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: nativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: nativeSessionId, iteration: 0, task_slug: "mvp-h-local-method-preflight-execution", started_at: "2026-05-14T07:00:00.000Z", }); await writeJson(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", session_id: nativeSessionId, active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: nativeSessionId }], }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: false, mode: "ralph", current_phase: "complete", session_id: canonicalSessionId, owner_codex_thread_id: "thread-A", completed_at: "2026-05-14T07:30:00.000Z", completion_audit: { passed: true, prompt_to_artifact_checklist: ["task evidence mapped"], verification_evidence: ["fresh verification evidence recorded"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, thread_id: "thread-B", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/native-hook-seed/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_starting", systemMessage: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/native-hook-seed/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); const preservedState = JSON.parse(await readFile(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), "utf-8")); assert.equal(preservedState.active, true); assert.equal(preservedState.current_phase, "starting"); assert.equal(preservedState.stop_reason, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from another session-scoped Ralph state when an explicit session_id has no active Ralph state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-explicit-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-other"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-other", "ralph-state.json"), { active: true, current_phase: "starting", session_id: "sess-other", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block a question-only pane from Ralph state owned by another Codex session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-question-pane-")); const previousTmuxPane = process.env.TMUX_PANE; try { const stateDir = join(cwd, ".omx", "state"); const questionSessionId = "sess-question-pane"; const questionNativeSessionId = "codex-question-pane"; await mkdir(join(stateDir, "sessions", questionSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: questionSessionId, native_session_id: questionNativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", questionSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: questionSessionId, owner_omx_session_id: "sess-ralph-owner", owner_codex_session_id: "codex-ralph-owner", thread_id: "thread-ralph-owner", tmux_pane_id: "%41", }); process.env.TMUX_PANE = "%99"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: questionNativeSessionId, thread_id: "thread-question-pane", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { if (typeof previousTmuxPane === "string") process.env.TMUX_PANE = previousTmuxPane; else delete process.env.TMUX_PANE; await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when Ralph skill-active initialization points at another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-stale-skill-active-")); try { const stateDir = join(cwd, ".omx", "state"); const currentSessionId = "sess-current-ralph"; await mkdir(join(stateDir, "sessions", currentSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: currentSessionId, native_session_id: currentSessionId, cwd, }); await writeJson(join(stateDir, "sessions", currentSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", session_id: currentSessionId, owner_omx_session_id: currentSessionId, task_slug: "stale-rebound-task", }); await writeJson(join(stateDir, "sessions", currentSessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "verifying", session_id: currentSessionId, initialized_mode: "ralph", initialized_state_path: ".omx/state/sessions/sess-old-ralph/ralph-state.json", active_skills: [{ skill: "ralph", phase: "verifying", active: true, session_id: currentSessionId }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: currentSessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks same-session Ralph Stop continuation when ownership identifiers match", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-owned-session-")); const previousTmuxPane = process.env.TMUX_PANE; try { const stateDir = join(cwd, ".omx", "state"); const omxSessionId = "sess-ralph-owned"; const nativeSessionId = "codex-ralph-owned"; await mkdir(join(stateDir, "sessions", omxSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: omxSessionId, native_session_id: nativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", omxSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: omxSessionId, owner_omx_session_id: omxSessionId, owner_codex_session_id: nativeSessionId, thread_id: "thread-ralph-owned", tmux_pane_id: "%42", }); process.env.TMUX_PANE = "%42"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, thread_id: "thread-ralph-owned", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: executing; state: .omx/state/sessions/sess-ralph-owned/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_executing", systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/sessions/sess-ralph-owned/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { if (typeof previousTmuxPane === "string") process.env.TMUX_PANE = previousTmuxPane; else delete process.env.TMUX_PANE; await rm(cwd, { recursive: true, force: true }); } }); it("allows native verifier subagent Stop to complete while leader Ralph remains active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-subagent-verdict-")); try { const stateDir = join(cwd, ".omx", "state"); const omxSessionId = "sess-ralph-leader-verifier"; const leaderNativeSessionId = "codex-ralph-leader-verifier"; const childNativeSessionId = "codex-verifier-child"; await mkdir(join(stateDir, "sessions", omxSessionId), { recursive: true }); await writeSessionStart(cwd, omxSessionId, { nativeSessionId: leaderNativeSessionId, }); await writeJson(join(stateDir, "sessions", omxSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", session_id: omxSessionId, owner_omx_session_id: omxSessionId, owner_codex_session_id: leaderNativeSessionId, }); const transcriptPath = join(cwd, "verifier-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, depth: 1, agent_nickname: "Verifier", agent_role: "verifier", }, }, }, agent_nickname: "Verifier", agent_role: "verifier", }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const childStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, last_assistant_message: "Verdict: APPROVED. Evidence is sufficient.", }, { cwd }, ); assert.equal(childStop.omxEventName, "stop"); assert.equal(childStop.outputJson, null); const leaderStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: leaderNativeSessionId, thread_id: leaderNativeSessionId, last_assistant_message: "Waiting on verification integration.", }, { cwd }, ); assert.equal(leaderStop.omxEventName, "stop"); assert.deepEqual(leaderStop.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: verifying; state: .omx/state/sessions/sess-ralph-leader-verifier/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_verifying", systemMessage: "OMX Ralph is still active (phase: verifying; state: .omx/state/sessions/sess-ralph-leader-verifier/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("prefers canonical run-state terminal lifecycle before stale session Ralph state during Stop", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-canonical-run-state-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-canonical-run-state-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, mode: "ralph", active: false, outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-04-27T12:00:00.000Z", updated_at: "2026-04-27T12:00:00.000Z", }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root Ralph fallback when the current session has no scoped Ralph state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd }); await writeJson(join(stateDir, "ralph-state.json"), { active: true, current_phase: "executing", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when the current session Ralph state is cancelled even if stale root fallback remains", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cancelled-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd }); await writeJson(join(stateDir, "sessions", "sess-current", "ralph-state.json"), { active: false, current_phase: "cancelled", completed_at: "2026-04-10T23:30:38.000Z", session_id: "sess-current", }); await writeJson(join(stateDir, "ralph-state.json"), { active: true, current_phase: "starting", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("no-ops Stop when session.json points to another worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-cwd-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-elsewhere", cwd: join(cwd, "..", "different-worktree"), }); await writeJson(join(stateDir, "ralph-state.json"), { active: true, current_phase: "executing", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking Ralph Stop replays until the active task advances", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-replay-")); const previousOmxSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeFile( join(stateDir, "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", }), ); process.env.OMX_SESSION_ID = "sess-stop-ralph-replay"; const payload = { hook_event_name: "Stop", cwd, last_assistant_message: "Next active targets:\n\n1. scheduler integration\n\nI am continuing.", }; const expected = { decision: "block", reason: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_executing", systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.deepEqual(first.outputJson, expected); assert.equal(replay.omxEventName, "stop"); assert.deepEqual(replay.outputJson, expected); } finally { if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("lets dispatcher dedupe identical native stop hook replays after Stop payload normalization", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-hook-dedupe-")); const previousOmxSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ralph-hook-dedupe"), { recursive: true }); await writeHookCounterPlugin(cwd); await writeFile( join(stateDir, "sessions", "sess-stop-ralph-hook-dedupe", "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", session_id: "sess-stop-ralph-hook-dedupe", }), ); process.env.OMX_SESSION_ID = "sess-stop-ralph-hook-dedupe"; const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-ralph-hook-dedupe", thread_id: "thread-stop-ralph-hook-dedupe", turn_id: "turn-stop-ralph-hook-dedupe-1", last_assistant_message: "Next active targets:\n\n1. scheduler integration\n\nI am continuing.", }; await dispatchCodexNativeHook(payload, { cwd }); await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); const marker = JSON.parse( await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"), ) as { count: number }; assert.equal(marker.count, 1); } finally { if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("preserves per-turn native stop hook delivery even when stop_hook_active remains true", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-hook-refire-")); const previousOmxSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ralph-hook-refire"), { recursive: true }); await writeHookCounterPlugin(cwd); await writeFile( join(stateDir, "sessions", "sess-stop-ralph-hook-refire", "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", session_id: "sess-stop-ralph-hook-refire", }), ); process.env.OMX_SESSION_ID = "sess-stop-ralph-hook-refire"; const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-ralph-hook-refire", thread_id: "thread-stop-ralph-hook-refire", turn_id: "turn-stop-ralph-hook-refire-1", last_assistant_message: "Continuing current task.", }; await dispatchCodexNativeHook(payload, { cwd }); await dispatchCodexNativeHook( { ...payload, turn_id: "turn-stop-ralph-hook-refire-2", stop_hook_active: true, }, { cwd }, ); await writeFile( join(stateDir, "sessions", "sess-stop-ralph-hook-refire", "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", session_id: "sess-stop-ralph-hook-refire", }), ); await dispatchCodexNativeHook( { ...payload, turn_id: "turn-stop-ralph-hook-refire-3", stop_hook_active: true, }, { cwd }, ); const marker = JSON.parse( await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"), ) as { count: number }; assert.equal(marker.count, 3); } finally { if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output for native auto-nudge stall prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows a native subagent Stop instead of auto-nudging it into another turn", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-subagent-stop-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-stop-auto-child": { session_id: "sess-stop-auto-child", leader_thread_id: "thread-leader", updated_at: new Date().toISOString(), threads: { "thread-leader": { thread_id: "thread-leader", kind: "leader", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, "thread-child": { thread_id: "thread-child", kind: "subagent", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, mode: "executor", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-child", thread_id: "thread-child", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("bounds repeated ordinary working Stop loops with a diagnostic summary", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-working-loop-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-working-loop"; process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS = "2"; process.env.OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS = "0"; const payload = { hook_event_name: "Stop", cwd, session_id: "sess-working-loop", thread_id: "thread-working-loop", turn_id: "turn-working-loop-1", last_assistant_message: "Keep going and finish the cleanup.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(first.outputJson?.stopReason, "auto_nudge"); const repeated = await dispatchCodexNativeHook( { ...payload, turn_id: "turn-working-loop-2", stop_hook_active: true, }, { cwd }, ); assert.equal(repeated.omxEventName, "stop"); assert.equal(repeated.outputJson?.decision, "block"); assert.equal(repeated.outputJson?.stopReason, "ordinary_task_no_progress_guard"); assert.match(String(repeated.outputJson?.systemMessage), /no-progress guard triggered/); assert.match(String(repeated.outputJson?.systemMessage), /diagnostic summary/); assert.match(String(repeated.outputJson?.systemMessage), /complete, blocked, failed, or needs missing information/); const persisted = JSON.parse( await readFile(join(cwd, ".omx", "state", "native-stop-state.json"), "utf-8"), ) as { sessions: Record }; assert.equal(persisted.sessions["sess-working-loop"]?.ordinary_no_progress_guard?.repeat_count, 2); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks duplicate native auto-nudge replays for the same Stop reply", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-once-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-once"; await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-once", thread_id: "thread-stop-auto", turn_id: "turn-stop-auto-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-once", thread_id: "thread-stop-auto", turn_id: "turn-stop-auto-1", stop_hook_active: true, last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks duplicate native auto-nudge replays across native/canonical session-id drift", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-session-drift-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "omx-canonical"; await writeJson(join(stateDir, "session.json"), { session_id: "omx-canonical", native_session_id: "codex-native", }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-native", thread_id: "thread-stop-auto-drift", turn_id: "turn-stop-auto-drift-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-auto-drift", turn_id: "turn-stop-auto-drift-1", stop_hook_active: true, last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); const persisted = JSON.parse( await readFile(join(stateDir, "native-stop-state.json"), "utf-8"), ) as { sessions?: Record }; assert.deepEqual(Object.keys(persisted.sessions ?? {}), ["omx-canonical"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("dedupes native stop hook replay across owner launch SessionStart reconciliation drift", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-dispatch-session-drift-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "omx-canonical"), { recursive: true }); await writeHookCounterPlugin(cwd); process.env.OMX_SESSION_ID = "omx-canonical"; await writeSessionStart(cwd, "omx-canonical"); await writeJson(join(stateDir, "sessions", "omx-canonical", "ralph-state.json"), { active: true, current_phase: "executing", session_id: "omx-canonical", }); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-native-new", }, { cwd, sessionOwnerPid: process.pid }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-native-new", thread_id: "thread-stop-hook-drift", turn_id: "turn-stop-hook-drift-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-hook-drift", turn_id: "turn-stop-hook-drift-1", stop_hook_active: true, last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const marker = JSON.parse( await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"), ) as { count: number }; assert.equal(marker.count, 1); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, "omx-canonical"); assert.equal(sessionState.native_session_id, "codex-native-new"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-fires native auto-nudge for a later fresh Stop reply even when stop_hook_active is true", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-refire-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-refire"; await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-refire", thread_id: "thread-stop-auto-refire", turn_id: "turn-stop-auto-refire-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-refire", thread_id: "thread-stop-auto-refire", turn_id: "turn-stop-auto-refire-2", stop_hook_active: true, last_assistant_message: "Continue with the cleanup from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("auto-continues native Stop on permission-seeking prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-permission-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-permission"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-permission", last_assistant_message: "Would you like me to continue with the cleanup?", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("auto-continues native Stop on \"if you want\" permission-seeking prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-if-you-want-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-if-you-want"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-if-you-want", last_assistant_message: "If you want, I can continue with the cleanup from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not auto-continue native Stop while deep-interview is waiting on an intent-first question", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-question-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-auto-question"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-question"; await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-question" }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-question", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-auto-question", thread_id: "thread-stop-auto-question", input_lock: { active: true, scope: "deep-interview-auto-approval", blocked_inputs: ["yes", "proceed"], message: "Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.", }, }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-question", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-question", thread_id: "thread-stop-auto-question", turn_id: "turn-stop-auto-question-1", last_assistant_message: [ "Round 2 | Target: Decision boundary | Ambiguity: 24%", "", "If an existing project spider still declares session_mode = \"owned\", should ZenX fail loudly so the stale attribute is removed, or should it ignore the attribute and initialize the session pool anyway?", "Keep going once I have your answer.", ].join("\n"), }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses native auto-nudge re-fire while session-scoped deep-interview state is still active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-state-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-auto-interview"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-interview"; await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-interview" }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-interview", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-interview", thread_id: "thread-stop-auto-interview", turn_id: "turn-stop-auto-interview-2", stop_hook_active: true, last_assistant_message: "If you want, I can keep going from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses native auto-nudge when root deep-interview mode state is active and no session is known", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-mode-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, turn_id: "turn-stop-auto-mode-1", last_assistant_message: "Would you like me to continue with the next step?", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats inherited OMX_SESSION_ID as session-aware for native auto-nudge Stop checks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-env-session-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-mode"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-stop-auto-env-session", turn_id: "turn-stop-auto-env-session-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as Record; assert.ok((stopState.sessions as Record)["sess-stop-auto-mode"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores generic SESSION_ID for native auto-nudge Stop session scoping", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-generic-session-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.SESSION_ID = "generic-shell-session"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-stop-auto-generic-session", turn_id: "turn-stop-auto-generic-session-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as Record; const sessions = stopState.sessions as Record; assert.equal(sessions["generic-shell-session"], undefined); assert.ok(sessions["thread-stop-auto-generic-session"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from stale root deep-interview mode state when the explicit session-scoped mode state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-stale-root-mode-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-stale-root-mode"; await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-stale-root-mode", thread_id: "thread-stop-auto-stale-root-mode", turn_id: "turn-stop-auto-stale-root-mode-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from stale root deep-interview skill state when the explicit session-scoped canonical skill state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-stale-root-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-stale-root-skill"; await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-stale-root-skill", thread_id: "thread-stop-auto-stale-root-skill", turn_id: "turn-stop-auto-stale-root-skill-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from stale root deep-interview input lock when the explicit session-scoped canonical skill state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-stale-root-lock-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-stale-root-lock"; await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", input_lock: { active: true, scope: "deep-interview-auto-approval", blocked_inputs: ["yes", "proceed"], message: "Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-stale-root-lock", thread_id: "thread-stop-auto-stale-root-lock", turn_id: "turn-stop-auto-stale-root-lock-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from active root deep-interview state when the current scoped mode state is explicitly inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-inactive-scoped-mode-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-auto-inactive-mode"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-inactive-mode"; await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-inactive-mode" }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-inactive-mode", "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "completed", }); await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-inactive-mode", thread_id: "thread-stop-auto-inactive-mode", turn_id: "turn-stop-auto-inactive-mode-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clears stale root skill-active state when current session ralplan is terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-terminal-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-terminal-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "completed", lifecycle_outcome: "finished", run_outcome: "finish", final_artifact: "proposed_plan", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ultrawork", phase: "planning", source: "keyword-detector", active_skills: [ { skill: "ultrawork", phase: "planning", active: true }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-terminal-ralplan", turn_id: "turn-stop-terminal-ralplan-1", last_assistant_message: "Done.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const rootSkillState = JSON.parse( await readFile(join(stateDir, "skill-active-state.json"), "utf-8"), ) as { active?: boolean; active_skills?: unknown[]; reconciliation_reason?: string }; assert.equal(rootSkillState.active, false); assert.deepEqual(rootSkillState.active_skills, []); assert.equal(rootSkillState.reconciliation_reason, "stop_hook_session_state_terminal"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves legitimate session-scoped ultrawork blocking while reconciling root skill-active state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-active-root-skill-session-mode-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-active-ultrawork"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "ultrawork-state.json"), { active: true, mode: "ultrawork", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ultrawork", phase: "planning", source: "keyword-detector", active_skills: [ { skill: "ultrawork", phase: "planning", active: true, session_id: sessionId }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-active-ultrawork", turn_id: "turn-stop-active-ultrawork-1", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultrawork_executing", systemMessage: "OMX ultrawork is still active (phase: executing).", }); const rootSkillState = JSON.parse( await readFile(join(stateDir, "skill-active-state.json"), "utf-8"), ) as { active?: boolean; active_skills?: Array<{ skill?: string }> }; assert.equal(rootSkillState.active, true); assert.deepEqual(rootSkillState.active_skills?.map((entry) => entry.skill), ["ultrawork"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("reconciles stale root skill-active state under OMX_ROOT boxed state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-boxed-source-")); const omxRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-boxed-root-")); const previousOmxRoot = process.env.OMX_ROOT; try { process.env.OMX_ROOT = omxRoot; const stateDir = join(omxRoot, ".omx", "state"); const sourceStateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-boxed-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "completed", lifecycle_outcome: "finished", run_outcome: "finish", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ultrawork", phase: "planning", source: "keyword-detector", active_skills: [ { skill: "ultrawork", phase: "planning", active: true }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-boxed-ralplan", turn_id: "turn-stop-boxed-ralplan-1", last_assistant_message: "Done.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const boxedRootSkillState = JSON.parse( await readFile(join(stateDir, "skill-active-state.json"), "utf-8"), ) as { active?: boolean; active_skills?: unknown[]; reconciliation_reason?: string }; assert.equal(boxedRootSkillState.active, false); assert.deepEqual(boxedRootSkillState.active_skills, []); assert.equal(boxedRootSkillState.reconciliation_reason, "stop_hook_session_state_terminal"); assert.equal(existsSync(join(sourceStateDir, "skill-active-state.json")), false); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; await rm(cwd, { recursive: true, force: true }); await rm(omxRoot, { recursive: true, force: true }); } }); it("auto-continues native Stop for permission-seeking prompts even outside OMX runtime", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-plain-session-")); try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "plain-stop-session", }, { cwd, sessionOwnerPid: process.pid, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "plain-stop-session", thread_id: "plain-thread", turn_id: "plain-turn-1", last_assistant_message: "If you want, I can continue with the cleanup from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-fires team Stop output for a later fresh Stop reply while the team is still active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-refire-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "team-exec", team_name: "review-team", session_id: "sess-stop-team-refire", thread_id: "thread-stop-team-refire", }); await writeJson(join(stateDir, "team", "review-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-refire", thread_id: "thread-stop-team-refire", turn_id: "turn-stop-team-refire-1", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-refire", thread_id: "thread-stop-team-refire", turn_id: "turn-stop-team-refire-2", stop_hook_active: true, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (review-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses duplicate team Stop replays across native/canonical session-id drift", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-session-drift-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "omx-canonical"), { recursive: true }); process.env.OMX_SESSION_ID = "omx-canonical"; await writeJson(join(stateDir, "session.json"), { session_id: "omx-canonical", native_session_id: "codex-native", }); await writeJson(join(stateDir, "sessions", "omx-canonical", "team-state.json"), { active: true, current_phase: "starting", team_name: "current-team", session_id: "omx-canonical", }); await writeJson(join(stateDir, "team", "current-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 1, transitions: [], updated_at: new Date().toISOString(), }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-native", thread_id: "thread-stop-team-drift", turn_id: "turn-stop-team-drift-1", }, { cwd }, ); const duplicate = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-team-drift", turn_id: "turn-stop-team-drift-1", stop_hook_active: true, }, { cwd }, ); assert.equal(duplicate.omxEventName, "stop"); assert.deepEqual(duplicate.outputJson, { decision: "block", reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); const fresh = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-team-drift", turn_id: "turn-stop-team-drift-2", stop_hook_active: true, }, { cwd }, ); assert.equal(fresh.omxEventName, "stop"); assert.deepEqual(fresh.outputJson, { decision: "block", reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); const persisted = JSON.parse( await readFile(join(stateDir, "native-stop-state.json"), "utf-8"), ) as { sessions?: Record }; assert.deepEqual(Object.keys(persisted.sessions ?? {}), ["omx-canonical"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses duplicate ultrawork Stop replays while stop_hook_active stays true", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultrawork-repeat-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ultrawork-repeat"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-ultrawork-repeat", "ultrawork-state.json"), { active: true, current_phase: "executing", }); const first = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork-repeat", thread_id: "thread-stop-ultrawork-repeat", turn_id: "turn-stop-ultrawork-repeat-1", }, { cwd }, ); const repeated = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork-repeat", thread_id: "thread-stop-ultrawork-repeat", turn_id: "turn-stop-ultrawork-repeat-1", stop_hook_active: true, }, { cwd }, ); const fresh = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork-repeat", thread_id: "thread-stop-ultrawork-repeat", turn_id: "turn-stop-ultrawork-repeat-2", stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.deepEqual(repeated.outputJson, null); assert.equal(fresh.omxEventName, "stop"); assert.deepEqual(fresh.outputJson, { decision: "block", reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultrawork_executing", systemMessage: "OMX ultrawork is still active (phase: executing).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks active ralplan skill state on repeated Stop hooks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-repeat-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-skill-repeat"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-repeat" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-repeat", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-repeat", "ralplan-state.json"), { active: true, current_phase: "planning", }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-repeat", thread_id: "thread-stop-skill-repeat", turn_id: "turn-stop-skill-repeat-1", }, { cwd }, ); const repeated = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-repeat", thread_id: "thread-stop-skill-repeat", turn_id: "turn-stop-skill-repeat-1", stop_hook_active: true, }, { cwd }, ); assert.equal(repeated.omxEventName, "stop"); assert.equal(repeated.outputJson?.decision, "block"); assert.match(String(repeated.outputJson?.reason ?? ""), /Status: continue_from_artifact/); assert.match(String(repeated.outputJson?.reason ?? ""), /continue from the current ralplan artifact/i); assert.equal(repeated.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes while ralplan is active without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "critic-review", session_id: sessionId, }); await writeCanonicalLeaderFixture(stateDir, sessionId, "thread-ralplan-pretool-block", cwd); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-pretool-block", agent_id: "thread-ralplan-pretool-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); assert.match( String((result.outputJson?.hookSpecificOutput as { additionalContext?: string } | undefined)?.additionalContext ?? ""), /\$ultragoal.*\$team.*\$ralph/i, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes while Autopilot is supervising deep-interview without a persisted phase transition", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-deep-interview-pretool-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-deep-interview-pretool-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "deep-interview", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, handoff_artifacts: { deep_interview: null, ralplan: null, ultragoal: null, code_review: null, ultraqa: null, }, ralplan_consensus_gate: { ralplan_architect_review: null, ralplan_critic_review: null, complete: false, }, }); await writeCanonicalLeaderFixture(stateDir, sessionId, "thread-autopilot-deep-interview-pretool-block", cwd); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-deep-interview-pretool-block", agent_id: "thread-autopilot-deep-interview-pretool-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Deep-interview is active .*implementation\/write tools are blocked/i); assert.match( String((result.outputJson?.hookSpecificOutput as { additionalContext?: string } | undefined)?.additionalContext ?? ""), /To implement, first ask for or process an explicit transition/i, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes while Autopilot is supervising ralplan without handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-pretool-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-pretool-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, state: { handoff_artifacts: { ralplan_consensus_gate: { required: true, complete: false }, }, }, }); await writeCanonicalLeaderFixture(stateDir, sessionId, "thread-autopilot-ralplan-pretool-block", cwd); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-ralplan-pretool-block", agent_id: "thread-autopilot-ralplan-pretool-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); for (const command of [ "sed -i 's/old/new/' src/runtime.ts", "perl -pi -e 's/old/new/' src/runtime.ts", ]) { const writeIntentResult = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-ralplan-pretool-block", agent_id: "thread-autopilot-ralplan-pretool-block", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal((writeIntentResult.outputJson as { decision?: string } | null)?.decision, "block", command); assert.match( String((writeIntentResult.outputJson as { reason?: string } | null)?.reason ?? ""), /Bash .* (?:write intent|mutation target) .*not workflow state\/ledger\/mailbox\/handoff metadata|target |not under allowed planning artifact paths or metadata paths/, ); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Autopilot planning handoffs with active:true state writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-planning-state-write-allow-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-planning-state-write-allow"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "planning", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "planning", session_id: sessionId, state: { handoff_artifacts: { ralplan_consensus_gate: { required: true, complete: false }, }, }, }); await writeCanonicalLeaderFixture( stateDir, sessionId, "thread-autopilot-planning-state-write-allow", cwd, ); const allowedPlanningHandoff = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-planning-state-write-allow", agent_id: "thread-autopilot-planning-state-write-allow", tool_name: "mcp__omx_state__state_write", tool_input: { mode: "autopilot", active: true, current_phase: "planning", session_id: sessionId, workingDirectory: cwd, }, }, { cwd }, ); assert.equal(allowedPlanningHandoff.outputJson, null); const blockedPlanningDeactivation = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-planning-state-write-allow", agent_id: "thread-autopilot-planning-state-write-allow", tool_name: "mcp__omx_state__state_write", tool_input: { mode: "autopilot", active: false, current_phase: "planning", session_id: sessionId, workingDirectory: cwd, }, }, { cwd }, ); assert.equal((blockedPlanningDeactivation.outputJson as { decision?: string } | null)?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes when Autopilot ralplan is visible only in skill-active phase", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-skill-ralplan-pretool-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-skill-ralplan-pretool-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "autopilot:ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "planning", session_id: sessionId, state: { handoff_artifacts: { ralplan_consensus_gate: { required: true, complete: false }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-skill-ralplan-pretool-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Autopilot planning is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores stale Autopilot ralplan skill mirrors after detail state leaves planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-stale-ralplan-mirror-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-stale-ralplan-mirror"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "autopilot:ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }], }); for (const phase of ["ultragoal", "code-review", "completing", "complete"]) { await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: phase, session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-stale-ralplan-mirror", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null, `stale skill-active ralplan mirror must not block when Autopilot detail phase is ${phase}`); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows explicit blank Autopilot detail phase to use a ralplan skill mirror", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-blank-phase-mirror-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-blank-phase-mirror"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "autopilot:ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-blank-phase-mirror", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Autopilot planning is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block implementation writes from Autopilot ralplan detail state without canonical skill state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-no-canonical-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-no-canonical"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-ralplan-no-canonical", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-terminal-pretool-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-terminal-pretool"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-30T00:00:00.000Z", updated_at: "2026-05-30T00:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-ralplan-terminal-pretool", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /(?:Ralplan|Autopilot planning) is active \(phase: ralplan\)/); assert.match(JSON.stringify(result.outputJson), /implementation\/write tools are blocked/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks bash implementation writes while Autopilot is supervising ralplan without handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-pretool-bash-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-pretool-bash-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-ralplan-pretool-bash-block", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes when ralplan and Autopilot ralplan are both active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-autopilot-mixed-planning-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-autopilot-mixed-planning"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId }, { skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }, ], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-autopilot-mixed-planning", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes while Autopilot is supervising replan without handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-replan-pretool-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-replan-pretool-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "replan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "replan", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "replan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-replan-pretool-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-native-map-block"; const nativeSessionId = "019e-autopilot-ralplan-native"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-autopilot-ralplan-native-map-block", tool_name: "apply_patch", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks bash implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-bash-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-native-map-bash"; const nativeSessionId = "019e-autopilot-ralplan-native-bash"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-autopilot-ralplan-native-map-bash", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks standalone ralplan writes when native Codex id maps to OMX session state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-native-map-block"; const nativeSessionId = "019e-ralplan-native-map"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-native-map-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks deep-interview writes when native Codex id maps to OMX session state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-native-map-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-deep-interview-native-map-block"; const nativeSessionId = "019e-deep-interview-native-map"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "deep-interview", "interview"); await writeJson(join(stateDir, "sessions", sessionId, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "interview", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-deep-interview-native-map-block", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Deep-interview is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows canonical leader mapped ralplan planning artifact writes without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-native-map-artifact"; const nativeSessionId = "019e-ralplan-native-map-artifact"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId, "thread-ralplan-native-map-artifact"); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-native-map-artifact", agent_id: "thread-ralplan-native-map-artifact", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > .omx/plans/prd-native-map.md\nplanning\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); const allowedDraftPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-native-map-artifact", agent_id: "thread-ralplan-native-map-artifact", tool_name: "apply_patch", tool_input: { input: `*** Begin Patch\n*** Add File: ${join(cwd, ".omx", "drafts", "issue-3105.md")}\n+# Draft\n*** End Patch\n`, }, }, { cwd }, ); assert.equal(allowedDraftPatch.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3358 keeps Team and update_plan denied while hardening exact read-only discovery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3358-transport-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3358-transport"; const leaderThreadId = "thread-3358-transport"; const childThreadId = "child-3358-transport"; execFileSync("git", ["init", "-q"], { cwd, env: { ...process.env, GIT_CONFIG_COUNT: "0", GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_SYSTEM: process.platform === "win32" ? "NUL" : "/dev/null", }, }); await writeNativeMappedSessionState(cwd, stateDir, sessionId, sessionId, leaderThreadId); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", parent_thread_id: leaderThreadId }, }, }, }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const previousPath = process.env.PATH; process.env.PATH = trustedPath; try { const rootPayload = (toolName: string, toolInput: Record, overrides: Record = {}) => ({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, turn_id: "turn-3358-transport", tool_name: toolName, tool_use_id: `tool-3358-${Math.random()}`, tool_input: toolInput, ...overrides, }); const runBash = (command: string, overrides: Record = {}) => dispatchCodexNativeHook( rootPayload("Bash", { command }, overrides), { cwd }, ); const safeGitEnvironment = { GIT_ATTR_NOSYSTEM: "1", GIT_CONFIG_COUNT: "0", GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_SYSTEM: process.platform === "win32" ? "NUL" : "/dev/null", GIT_EDITOR: "", GIT_EXTERNAL_DIFF: "", GIT_PAGER: "", GIT_SEQUENCE_EDITOR: "", PAGER: "", }; const assignmentWord = (name: string, value: string) => value === "" ? `${name}=` : `${name}=${JSON.stringify(value)}`; const hardenedGitStatus = (overrides: Record = {}, extraAssignments: string[] = []) => { const environment = { ...safeGitEnvironment, ...overrides }; return [ ...Object.entries(environment).map(([name, value]) => assignmentWord(name, value)), ...extraAssignments, "git --no-pager --no-optional-locks -c core.fsmonitor=false -c core.untrackedCache=false -c pager.status=false status --short --branch --untracked-files=normal --ignore-submodules=all --no-renames", ].join(" "); }; const runFixtureGit = (args: string[]) => execFileSync("git", args, { cwd, env: { ...process.env, GIT_CONFIG_COUNT: "0", GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_SYSTEM: process.platform === "win32" ? "NUL" : "/dev/null", GIT_EXTERNAL_DIFF: "", GIT_PAGER: "", PAGER: "", }, }); const assertAllowed = async (name: string, command: string) => { const result = await runBash(command); assert.equal(result.outputJson, null, `${name}: ${JSON.stringify(result)}`); }; const assertBlocked = async (name: string, command: string) => { const result = await runBash(command); assert.equal(result.outputJson?.decision, "block", `${name}: ${JSON.stringify(result)}`); }; await assertAllowed("hardened git status", hardenedGitStatus()); await assertAllowed("hardened git status command-local PATH", hardenedGitStatus({}, [assignmentWord("PATH", trustedPath)])); await assertAllowed("bounded find files", "find . -maxdepth 2 -type f"); await assertAllowed("bounded find command-local PATH", `${assignmentWord("PATH", trustedPath)} find . -maxdepth 2 -type f`); await assertAllowed("bounded find directories", "find . -mindepth 1 -maxdepth 3 -type d -print"); // The CI image does not guarantee ripgrep; assert the real trusted binary only when the authenticated PATH contains it. if (existsSync("/usr/bin/rg") || existsSync("/bin/rg")) { await assertAllowed("real rg", "rg -n \"transport\" README.md"); } for (const [name, command] of [ ["plain git status", "git status --short --branch"], ["git status path operand", `${hardenedGitStatus()} src`], ["git status unmodeled option", "git status --porcelain"], ["git status chain", `${hardenedGitStatus()}; true`], ["git status redirect", `${hardenedGitStatus()} > status.txt`], ["git status pipeline", `${hardenedGitStatus()} | cat`], ["git status substituted executable", hardenedGitStatus().replace(/\bgit /, "$(printf git) ")], ["git status wrapper", hardenedGitStatus().replace(/\bgit /, "command git ")], ["git status function", `git() { printf x > pwned; }; ${hardenedGitStatus()}`], ["git status alias", `shopt -s expand_aliases; alias git='printf x > pwned'; ${hardenedGitStatus()}`], ["git status nested shell", `bash -c ${JSON.stringify(hardenedGitStatus())}`], ["find exec", "find . -type f -exec sh -c 'printf x > pwned' \\;"], ["find delete", "find . -type f -delete"], ["find file output", "find . -type f -fprint pwned"], ["find dynamic depth", "find . -maxdepth \"$DEPTH\" -type f"], ["unbounded find", "find . -type f"], ["outside workspace find", "find .. -maxdepth 2 -type f"], ["absolute workspace escape find", "find /tmp -maxdepth 2 -type f"], ["excessive find depth", "find . -maxdepth 33 -type f"], ["huge find depth", "find . -maxdepth 999999999 -type f"], ["find wrapper", "command find . -maxdepth 2 -type f"], ["find function", "find() { printf x > pwned; }; find . -maxdepth 2 -type f"], ["find alias", "shopt -s expand_aliases; alias find='printf x > pwned'; find . -maxdepth 2 -type f"], ["find nested shell", "bash -c 'find . -maxdepth 2 -type f'"], ["find chain", "find . -maxdepth 2 -type f; true"], ["find redirect", "find . -maxdepth 2 -type f > files.txt"], ["find pipeline", "find . -maxdepth 2 -type f | cat"], ["find substitution", "find \"$(printf .)\" -maxdepth 2 -type f"], ["find brace expansion", "find . {-exec,sh,-c,'touch pwned',';'} -maxdepth 0"], ["find pathname expansion", "find . -maxdepth 2 -name *"], ["find extglob expansion", "find . -maxdepth 2 -name @(src|docs)"], ] as const) await assertBlocked(name, command); const outsideFindRoot = await mkdtemp(join(tmpdir(), "omx-3358-find-outside-")); const outsideFindLink = join(cwd, "outside-find-link"); try { await mkdir(join(outsideFindRoot, "nested"), { recursive: true }); await symlink(outsideFindRoot, outsideFindLink, process.platform === "win32" ? "junction" : "dir"); await assertBlocked("symlinked find workspace escape", "find outside-find-link/nested -maxdepth 2 -type f"); } finally { await rm(outsideFindLink, { force: true }); await rm(outsideFindRoot, { recursive: true, force: true }); } process.env["BASH_FUNC_git%%"] = "() { printf x > pwned; }"; process.env["BASH_FUNC_find%%"] = "() { printf x > pwned; }"; try { await assertBlocked("exported git function", hardenedGitStatus()); await assertBlocked("exported find function", "find . -maxdepth 2 -type f"); } finally { delete process.env["BASH_FUNC_git%%"]; delete process.env["BASH_FUNC_find%%"]; } for (const [name, overrides] of [ ["GIT_PAGER override", { GIT_PAGER: "cat" }], ["PAGER override", { PAGER: "cat" }], ["external diff override", { GIT_EXTERNAL_DIFF: "sh -c 'printf x > pwned'" }], ["config count override", { GIT_CONFIG_COUNT: "1" }], ["GIT_DIR override", { GIT_DIR: join(cwd, "foreign-git-dir") }], ] as const) await assertBlocked(name, hardenedGitStatus(overrides)); const previousExternalDiff = process.env.GIT_EXTERNAL_DIFF; const previousGitPager = process.env.GIT_PAGER; process.env.GIT_EXTERNAL_DIFF = "sh -c 'printf x > pwned'"; process.env.GIT_PAGER = "sh -c 'printf x > pwned'"; try { await assertBlocked( "append external diff neutralizer", hardenedGitStatus().replace("GIT_EXTERNAL_DIFF=", "GIT_EXTERNAL_DIFF+="), ); await assertBlocked( "append pager neutralizer", hardenedGitStatus().replace("GIT_PAGER=", "GIT_PAGER+="), ); } finally { if (previousExternalDiff === undefined) delete process.env.GIT_EXTERNAL_DIFF; else process.env.GIT_EXTERNAL_DIFF = previousExternalDiff; if (previousGitPager === undefined) delete process.env.GIT_PAGER; else process.env.GIT_PAGER = previousGitPager; } const configPath = join(cwd, ".git", "config"); const originalGitConfig = await readFile(configPath, "utf-8"); for (const [name, config] of [ ["git alias config", "[alias]\n status-safe = !printf x > pwned\n"], ["core pager config", "[core]\n pager = sh -c 'printf x > pwned'\n"], ["core fsmonitor config", "[core]\n fsmonitor = sh -c 'printf x > pwned'\n"], ["core worktree config", `[core]\n worktree = ${join(cwd, "foreign-worktree")}\n`], ["core excludes config", `[core]\n excludesFile = ${join(cwd, "foreign-excludes")}\n`], ["core hooks config", `[core]\n hooksPath = ${join(cwd, "foreign-hooks")}\n`], ["core attributes config", `[core]\n attributesFile = ${join(cwd, "foreign-attributes")}\n`], ["include config", `[include]\n path = ${join(cwd, "foreign-config")}\n`], ["conditional include config", `[includeIf \"gitdir:${cwd}/\"]\n path = ${join(cwd, "foreign-config")}\n`], ["interactive diff filter config", "[interactive]\n diffFilter = sh -c 'printf x > pwned'\n"], ["status submodule summary config", "[status]\n submoduleSummary = true\n"], ["diff driver command config", "[diff \"evil\"]\n command = sh -c 'printf x > pwned'\n"], ["diff textconv config", "[diff \"evil\"]\n textconv = sh -c 'printf x > pwned'\n"], ["external diff config", "[diff]\n external = sh -c 'printf x > pwned'\n"], ["filter helper config", "[filter \"evil\"]\n clean = sh -c 'printf x > pwned'\n"], ["submodule helper config", "[submodule \"child\"]\n update = !printf x > pwned\n"], ] as const) { await writeFile(configPath, `${originalGitConfig}\n${config}`); await assertBlocked(name, hardenedGitStatus()); await writeFile(configPath, originalGitConfig); } await writeFile(join(cwd, ".gitmodules"), "[submodule \"child\"]\n path = child\n url = ./child\n"); runFixtureGit(["add", "--", ".gitmodules"]); await assertBlocked("gitmodules submodule", hardenedGitStatus()); runFixtureGit(["rm", "--cached", "-q", "--", ".gitmodules"]); await rm(join(cwd, ".gitmodules"), { force: true }); await writeFile(join(cwd, ".gitattributes"), "*.txt filter=evil\n"); await assertBlocked("untracked worktree attributes helper", hardenedGitStatus()); runFixtureGit(["add", "--", ".gitattributes"]); await assertBlocked("tracked worktree attributes helper", hardenedGitStatus()); runFixtureGit(["rm", "--cached", "-q", "--", ".gitattributes"]); await rm(join(cwd, ".gitattributes"), { force: true }); await mkdir(join(cwd, "nested"), { recursive: true }); await writeFile(join(cwd, "nested", "tracked.txt"), "tracked\n"); runFixtureGit(["add", "--", "nested/tracked.txt"]); await writeFile(join(cwd, "nested", ".gitattributes"), "*.txt filter=evil\n"); await assertBlocked("nested untracked worktree attributes helper", hardenedGitStatus()); await rm(join(cwd, "nested", ".gitattributes"), { force: true }); runFixtureGit(["rm", "--cached", "-q", "--", "nested/tracked.txt"]); await rm(join(cwd, "nested"), { recursive: true, force: true }); await mkdir(join(cwd, ".git", "info"), { recursive: true }); await writeFile(join(cwd, ".git", "info", "attributes"), "*.txt diff=evil\n"); await assertBlocked("git info attributes helper", hardenedGitStatus()); await rm(join(cwd, ".git", "info", "attributes"), { force: true }); const attackerDir = await mkdtemp(join(tmpdir(), "omx-3358-discovery-shadow-")); try { for (const commandName of ["git", "find"]) { await writeFile(join(attackerDir, commandName), "#!/bin/sh\nprintf x > pwned\n"); await chmod(join(attackerDir, commandName), 0o755); } const shadowedPath = `${attackerDir}:${trustedPath}`; await assertBlocked("PATH-shadowed git", hardenedGitStatus({}, [assignmentWord("PATH", shadowedPath)])); await assertBlocked("foreign absolute git", hardenedGitStatus().replace(/\bgit /, `${join(attackerDir, "git")} `)); await assertBlocked("PATH-shadowed find", `${assignmentWord("PATH", shadowedPath)} find . -maxdepth 2 -type f`); await assertBlocked("foreign absolute find", `${join(attackerDir, "find")} . -maxdepth 2 -type f`); } finally { await rm(attackerDir, { recursive: true, force: true }); } const trimPrefixDir = await mkdtemp(join(tmpdir(), "omx-3358-trim-prefix-")); try { for (const [name, prefix] of [["BOM", "\uFEFF"], ["NBSP", "\u00A0"], ["CR", "\r"]] as const) { for (const executableName of [`${prefix}find`, `${prefix}GIT_ATTR_NOSYSTEM=1`]) { await writeFile(join(trimPrefixDir, executableName), "#!/bin/sh\nprintf x > pwned\n"); await chmod(join(trimPrefixDir, executableName), 0o755); } process.env.PATH = `${trimPrefixDir}:${trustedPath}`; await assertBlocked(`${name}-prefixed find`, `${prefix}find . -maxdepth 2 -type f`); await assertBlocked(`${name}-prefixed git environment`, `${prefix}${hardenedGitStatus()}`); } } finally { process.env.PATH = trustedPath; await rm(trimPrefixDir, { recursive: true, force: true }); } const updatePlan = await dispatchCodexNativeHook( rootPayload("update_plan", { explanation: "bounded metadata", plan: [{ step: "reproduce transports", status: "in_progress" }], }), { cwd }, ); assert.equal(updatePlan.outputJson?.decision, "block", JSON.stringify(updatePlan)); assert.match(String(updatePlan.outputJson?.reason ?? ""), /not a recognized read-only or explicitly authorized Conductor mutation transport/); const foreignUpdatePlan = await dispatchCodexNativeHook( rootPayload("update_plan", { plan: [{ step: "foreign", status: "pending" }] }, { session_id: "foreign-session" }), { cwd }, ); assert.equal(foreignUpdatePlan.outputJson?.decision, "block", JSON.stringify(foreignUpdatePlan)); assert.match(String(foreignUpdatePlan.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); const exactTeam = await runBash('omx team 1:executor "Implement the bounded issue 3358 slice"'); assert.equal(exactTeam.outputJson?.decision, "block", JSON.stringify(exactTeam)); for (const [toolName, toolInput] of [ ["collaboration.spawn_agent", { agent_type: "executor", message: "forged root spawn" }], ["collaboration.send_message", { agent_id: childThreadId, message: "forged root report" }], ["create_goal", { objective: "forged root lifecycle" }], ["update_goal", { status: "complete" }], ["mcp__omx_team__start", { workers: 1, role: "executor", task: "forged root team" }], ] as const) { const forgedLeader = await dispatchCodexNativeHook( rootPayload(toolName, toolInput, { agent_id: leaderThreadId, thread_id: leaderThreadId, }), { cwd }, ); assert.equal(forgedLeader.outputJson?.decision, "block", `${toolName}: ${JSON.stringify(forgedLeader)}`); } const childWrite = await dispatchCodexNativeHook( rootPayload("apply_patch", { command: "*** Begin Patch\n*** Add File: src/child.ts\n+export {};\n*** End Patch", }, { agent_id: childThreadId, agent_type: "executor", turn_id: "turn-3358-child", }), { cwd }, ); assert.equal(childWrite.outputJson?.decision, "block", JSON.stringify(childWrite)); assert.match(String(childWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const ownLeaderReport = await dispatchCodexNativeHook( rootPayload("collaboration.send_message", { agent_id: leaderThreadId, message: "Verification complete; no mutation attempted.", }, { agent_id: childThreadId, agent_type: "verifier", turn_id: "turn-3358-child-report", }), { cwd }, ); assert.equal(ownLeaderReport.outputJson?.decision, "block", JSON.stringify(ownLeaderReport)); assert.match(String(ownLeaderReport.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); for (const [name, overrides, targetAgentId] of [ ["cross-child report", { agent_id: childThreadId, agent_type: "verifier" }, childThreadId], ["unregistered child report", { agent_id: "unknown-child", agent_type: "verifier" }, leaderThreadId], ["foreign-session child report", { agent_id: childThreadId, agent_type: "verifier", session_id: "foreign-session" }, leaderThreadId], ["contradictory parent report", { agent_id: childThreadId, agent_type: "verifier", source: { subagent: { thread_spawn: { parent_thread_id: "foreign-parent" } } }, }, leaderThreadId], ] as const) { const result = await dispatchCodexNativeHook( rootPayload("collaboration.send_message", { agent_id: targetAgentId, message: name }, overrides), { cwd }, ); assert.equal(result.outputJson?.decision, "block", `${name}: ${JSON.stringify(result)}`); } } finally { if (previousPath === undefined) delete process.env.PATH; else process.env.PATH = previousPath; } }); }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows trusted omx state read with structured input under ultragoal conductor planning (#3343)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3343-state-read-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3343-state-read"; const leaderThreadId = "thread-3343-state-read"; await mkdir(join(cwd, ".git"), { recursive: true }); await writeNativeMappedSessionState(cwd, stateDir, sessionId, leaderThreadId, leaderThreadId); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const inheritedPath = process.env.PATH; process.env.PATH = trustedPath; try { const payload = JSON.stringify({ mode: "ultragoal", session_id: sessionId, workingDirectory: cwd }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: "tool-3343-omx-state-read-input", tool_input: { command: `omx state read --input '${payload}' --json`, }, }, { cwd }, ); const assertCommandAllowed = async (command: string) => { const commandResult = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: `tool-3397-${Math.random()}`, tool_input: { command }, }, { cwd }, ); assert.equal(commandResult.outputJson, null, command); }; const assertCommandBlocked = async (command: string) => { const commandResult = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: `tool-3397-${Math.random()}`, tool_input: { command }, }, { cwd }, ); assert.equal(commandResult.outputJson?.decision, "block", command); }; await symlink(join(trustedPath.split(":", 1)[0] ?? "", "omx"), join(trustedPath.split(":", 1)[0] ?? "", "gjc")); await assertCommandAllowed("gjc ultragoal create --brief 'Issue 3397' --force --json"); await assertCommandAllowed( "omx ultragoal create-goals --brief 'Issue 3397' --goal 'Fix::Correct parser' --goal 'Verify::Run regressions' --codex-goal-mode aggregate --force --json", ); for (const command of [ "omx ultragoal create-goals --brief 'Issue 3397' --json", "omx ultragoal create-goals --brief 'Issue 3397' --force --force --json", "omx ultragoal create-goals --brief 'Issue 3397' --force --unknown --json", "omx ultragoal create-goals --brief-file brief.md --force --json", "omx ultragoal create-goals --from-stdin --force --json", "omx ultragoal create-goals 'Issue 3397' --force --json", "omx ultragoal create-goals --brief \"$(printf Issue3397)\" --force --json", "omx ultragoal create-goals --brief 'Issue 3397' --force --json > metadata.json", "omx ultragoal create-goals --brief 'Issue 3397' --force --json && printf done", "env NODE_OPTIONS=--require=./payload.cjs omx ultragoal create-goals --brief 'Issue 3397' --force --json", ]) { await assertCommandBlocked(command); } assert.equal(result.outputJson, null); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }); }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("enforces the exact same-root session lock inspect grammar under ultragoal Conductor (#3411)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3411-lock-inspect-")); const foreignCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3411-foreign-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3411-lock-inspect"; const leaderThreadId = "thread-3411-lock-inspect"; await mkdir(join(cwd, ".git"), { recursive: true }); await writeNativeMappedSessionState(cwd, stateDir, sessionId, leaderThreadId, leaderThreadId); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const runCommand = async (command: string, overrides: Record = {}) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: `tool-3411-lock-inspect-${Math.random()}`, tool_input: { command }, ...overrides, }, { cwd }, ); const assertAllowed = async (command: string) => { const result = await runCommand(command); assert.equal(result.outputJson, null, `${command}: ${JSON.stringify(result)}`); }; const assertBlocked = async (command: string, overrides?: Record) => { const result = await runCommand(command, overrides); assert.equal(result.outputJson?.decision, "block", `${command} (policy cwd ${cwd}, foreign cwd ${foreignCwd}): ${JSON.stringify(result)}`); }; const inheritedPath = process.env.PATH; process.env.PATH = trustedPath; try { for (const command of [ "omx session lock inspect --json", `omx session lock inspect --cwd ${cwd} --json`, `omx session lock inspect --json --cwd=${cwd}`, `omx session lock inspect --cwd=${cwd}`, ]) await assertAllowed(command); for (const command of [ `omx session lock inspect --cwd ${foreignCwd} --json`, "omx session lock recover --json", `omx session lock recover --cwd ${cwd} --json`, "omx session lock inspect --unknown --json", "omx session lock inspect --json --json", `omx session lock inspect --cwd ${cwd} --cwd ${cwd} --json`, "omx session lock inspect --cwd --json", "omx session lock inspect --cwd= --json", "omx session lock inspect positional --json", "omx session lock inspect --cwd $(pwd) --json", "omx session lock inspect --cwd `pwd` --json", "omx session lock inspect --cwd $PWD --json", "printf ready && omx session lock inspect --json", "omx session lock inspect --json | cat", "sh -c 'omx session lock inspect --json'", "(omx session lock inspect --json)", ]) await assertBlocked(command); await assertBlocked(`omx session lock inspect --cwd ${cwd} --json`, { session_id: "foreign-session", }); await assertBlocked(`omx session lock inspect --cwd ${cwd} --json`, { agent_id: "child-thread", thread_id: "child-thread", }); const attackerDir = await mkdtemp(join(tmpdir(), "omx-native-hook-3411-impostor-")); try { await writeFile(join(attackerDir, "omx"), "#!/bin/sh\nexit 0\n", { mode: 0o755 }); process.env.PATH = `${attackerDir}:${trustedPath}`; await assertBlocked("omx session lock inspect --json"); } finally { await rm(attackerDir, { recursive: true, force: true }); process.env.PATH = trustedPath; } process.env.NODE_OPTIONS = "--require=./payload.cjs"; try { await assertBlocked("omx session lock inspect --json"); } finally { delete process.env.NODE_OPTIONS; } } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }); }); } finally { await rm(cwd, { recursive: true, force: true }); await rm(foreignCwd, { recursive: true, force: true }); } }); it("blocks an untrusted omx shim ahead of the trusted workspace CLI on PATH under ultragoal conductor planning (#3343)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3343-untrusted-shim-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3343-untrusted-shim"; const leaderThreadId = "thread-3343-untrusted-shim"; await mkdir(join(cwd, ".git"), { recursive: true }); await writeNativeMappedSessionState(cwd, stateDir, sessionId, leaderThreadId, leaderThreadId); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const attackerDir = await mkdtemp(join(tmpdir(), "omx-3343-untrusted-shim-attacker-")); await writeFile(join(attackerDir, "omx"), "#!/bin/sh\necho pwned\n"); await chmod(join(attackerDir, "omx"), 0o755); const inheritedPath = process.env.PATH; // Attacker directory sits ahead of the trusted workspace bin dir on PATH. process.env.PATH = `${attackerDir}:${trustedPath}`; try { const payload = JSON.stringify({ mode: "ultragoal", session_id: sessionId, workingDirectory: cwd }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: "tool-3343-untrusted-shim-state-read-input", tool_input: { command: `omx state read --input '${payload}' --json`, }, }, { cwd }, ); assert.notEqual(result.outputJson, null); assert.equal(result.outputJson?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /PATH/); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; await rm(attackerDir, { recursive: true, force: true }); } }); }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("diagnoses Autopilot lifecycle and skill-mirror phase mismatches (#3344)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3344-phase-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3344-phase-mismatch"; const leaderThreadId = "thread-3344-phase-mismatch"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, leaderThreadId, leaderThreadId); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "planning", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, workingDirectory: cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Write", tool_use_id: "tool-3344-phase-mismatch-write", tool_input: { file_path: "src/runtime.ts", content: "export {};\n" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /STATE_PHASE_MISMATCH/); assert.match(String(result.outputJson?.reason ?? ""), /lifecycle phase: deep-interview/); assert.match(String(result.outputJson?.reason ?? ""), /skill mirror phase: planning/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets omx cancel terminalize Autopilot deep-interview even when the skill mirror advanced (#3344)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3344-cancel-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3344-cancel-mismatch"; const leaderThreadId = "thread-3344-cancel-mismatch"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, leaderThreadId, leaderThreadId); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "planning", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const inheritedPath = process.env.PATH; process.env.PATH = trustedPath; try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: "tool-3344-cancel-mismatch", tool_input: { command: "omx cancel" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /cancelled_exact_session/); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }); }); const nextAutopilot = JSON.parse(await readFile(join(stateDir, "sessions", sessionId, "autopilot-state.json"), "utf-8")); const nextSkill = JSON.parse(await readFile(join(stateDir, "sessions", sessionId, "skill-active-state.json"), "utf-8")); assert.equal(nextAutopilot.active, false); assert.equal(nextAutopilot.current_phase, "cancelled"); assert.equal(nextSkill.active, false); assert.equal(nextSkill.phase, "cancelled"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks mapped implementation writes when explicit ultragoal conductor handoff is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-handoff-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-native-map-handoff"; const nativeSessionId = "019e-ralplan-native-map-handoff"; await writeNativeMappedSessionState( cwd, stateDir, sessionId, nativeSessionId, "thread-ralplan-native-map-handoff", ); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ultragoal", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId }, { skill: "ultragoal", phase: "planning", active: true, session_id: sessionId }, ], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "complete", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-native-map-handoff", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: planning\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks mapped implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-terminal-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-native-map-terminal"; const nativeSessionId = "019e-autopilot-ralplan-native-terminal"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-30T00:00:00.000Z", updated_at: "2026-05-30T00:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-autopilot-ralplan-native-map-terminal", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /(?:Ralplan|Autopilot planning) is active \(phase: ralplan\)/); assert.match(JSON.stringify(result.outputJson), /implementation\/write tools are blocked/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for implementation writes when a different live root session owns active ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-live-root-conflict-")); const ownerCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-live-root-owner-")); try { const stateDir = join(cwd, ".omx", "state"); const ownerSessionId = "sess-ralplan-live-root-owner"; const ownerNativeSessionId = "019e-ralplan-live-root-owner"; await writeLiveNativeMappedSessionState(ownerCwd, stateDir, ownerSessionId, ownerNativeSessionId); await writeSessionSkillActiveState(stateDir, ownerSessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", ownerSessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: ownerSessionId, cwd: ownerCwd, }); const blockedForeignDraft = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-ralplan-live-root-unresolved-current", thread_id: "thread-ralplan-live-root-conflict", tool_name: "apply_patch", tool_input: { input: `*** Begin Patch\n*** Add File: ${join(cwd, ".omx", "drafts", "issue-3105.md")}\n+# Draft\n*** End Patch\n`, }, }, { cwd }, ); assert.equal(blockedForeignDraft.outputJson?.decision, "block"); assert.match(String(blockedForeignDraft.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-ralplan-live-root-unresolved-current", thread_id: "thread-ralplan-live-root-conflict", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); const blockedMcpStateClear = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-ralplan-live-root-unresolved-current", thread_id: "thread-ralplan-live-root-conflict", tool_name: "mcp__omx_state__state_clear", tool_input: { mode: "ralplan" }, }, { cwd }, ); assert.equal((blockedMcpStateClear.outputJson as { decision?: string } | null)?.decision, "block"); const blockedMcpStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-ralplan-live-root-unresolved-current", thread_id: "thread-ralplan-live-root-conflict", tool_name: "mcp__omx_state__state_write", tool_input: { mode: "ralplan", active: true, current_phase: "critic-review" }, }, { cwd }, ); assert.equal((blockedMcpStateWrite.outputJson as { decision?: string } | null)?.decision, "block"); const blockedScopedBashStateWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-ralplan-live-root-unresolved-current", thread_id: "thread-ralplan-live-root-conflict", tool_name: "Bash", tool_input: { command: `OMX_SESSION_ID=${ownerSessionId} omx state write --input '${JSON.stringify({ mode: "ralplan", active: true, current_phase: "critic-review", session_id: ownerSessionId, workingDirectory: ownerCwd })}' --json`, }, }, { cwd }, ); assert.equal((blockedScopedBashStateWrite.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(String(blockedScopedBashStateWrite.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); const blockedOmittedSessionTerminalWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-ralplan-live-root-unresolved-current", thread_id: "thread-ralplan-live-root-conflict", tool_name: "Bash", tool_input: { command: "omx state write --mode ralplan --input '{\"active\":false,\"current_phase\":\"complete\"}' --json", }, }, { cwd }, ); assert.equal((blockedOmittedSessionTerminalWrite.outputJson as { decision?: string } | null)?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); await rm(ownerCwd, { recursive: true, force: true }); } }); it("preserves canonical leader planning artifact writes with a live root session pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-live-root-owner-pass-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-live-root-owner-pass"; const nativeSessionId = "019e-ralplan-live-root-owner-pass"; await writeLiveNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId, "thread-ralplan-live-root-owner-pass"); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-live-root-owner-pass", agent_id: "thread-ralplan-live-root-owner-pass", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > .omx/plans/live-root-owner.md\nplanning\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); const allowedDraftPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-live-root-owner-pass", agent_id: "thread-ralplan-live-root-owner-pass", tool_name: "apply_patch", tool_input: { input: `*** Begin Patch\n*** Add File: ${join(cwd, ".omx", "drafts", "live-root-owner.md")}\n+# Draft\n*** End Patch\n`, }, }, { cwd }, ); assert.equal(allowedDraftPatch.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks foreign native Codex ids when current OMX session mapping does not match", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-unrelated-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-native-map-owner"; const ownerNativeSessionId = "019e-ralplan-native-owner"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, ownerNativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-unrelated-native-session", thread_id: "thread-ralplan-native-map-unrelated", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks mapped Autopilot ralplan writes from the authoritative team state root", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-team-root-")); const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-")); const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; const stateDir = teamStateRoot; const sessionId = "sess-autopilot-ralplan-team-root"; const nativeSessionId = "019e-autopilot-ralplan-team-root"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-autopilot-ralplan-team-root", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); assert.equal(existsSync(join(cwd, ".omx", "state", "session.json")), false); } finally { if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); await rm(teamStateRoot, { recursive: true, force: true }); } }); it("blocks foreign native Codex ids from the authoritative team state root", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-team-root-unrelated-")); const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-unrelated-")); const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; const stateDir = teamStateRoot; const sessionId = "sess-ralplan-team-root-owner"; const nativeSessionId = "019e-ralplan-team-root-owner"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "019e-unrelated-team-root-native", thread_id: "thread-ralplan-team-root-unrelated", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); } finally { if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); await rm(teamStateRoot, { recursive: true, force: true }); } }); it("allows canonical leader ralplan planning artifact writes without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-artifact"; const leaderThreadId = "thread-ralplan-pretool-artifact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Write", tool_input: { file_path: ".omx/plans/prd-issue-2603.md" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks bash implementation writes while ralplan is active without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-bash-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-bash-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-pretool-bash-block", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows canonical leader Bash planning artifact writes while ralplan is active without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-bash-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-bash-artifact"; const leaderThreadId = "thread-ralplan-pretool-bash-artifact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command: "cat <<'EOF' > .omx/plans/prd-issue-2603.md\nplanning\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows implementation writes when an explicit execution handoff is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-handoff-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-handoff"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ralplan-pretool-handoff", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-ralplan-pretool-handoff", threads: { "thread-ralplan-pretool-handoff": { thread_id: "thread-ralplan-pretool-handoff", kind: "leader" } } } } }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ultragoal", phase: "planning", session_id: sessionId, active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId }, { skill: "ultragoal", phase: "planning", active: true, session_id: sessionId }, ], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "complete", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-pretool-handoff", agent_id: "thread-ralplan-pretool-handoff", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: planning\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks mapped native-session Main-root conductor writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-native-map-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralph-conductor-native-map"; const nativeSessionId = "019e-ralph-conductor-native-map"; await writeNativeMappedSessionState( cwd, stateDir, sessionId, nativeSessionId, "thread-ralph-conductor-native-map", ); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralph-conductor-native-map", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ralph phase: executing\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks standalone ultragoal conductor writes without requiring ralplan active_skills", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-standalone-conductor-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ultragoal-standalone-conductor"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ultragoal-standalone-conductor", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-ultragoal-standalone-conductor", threads: { "thread-ultragoal-standalone-conductor": { thread_id: "thread-ultragoal-standalone-conductor", kind: "leader" } } } } }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ultragoal-standalone-conductor", agent_id: "thread-ultragoal-standalone-conductor", tool_name: "Write", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: planning\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows the exact installed omx CLI by absolute path for Main-root read-only commands (#3333)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-absolute-cli-readonly-")); const installRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-user-install-")); const originalOmxEnvironment = Object.fromEntries( Object.entries(process.env).filter(([name]) => /^(?:OMX|GJC)_/.test(name)), ); const originalPath = process.env.PATH; const originalBashEnv = process.env.BASH_ENV; try { for (const name of Object.keys(process.env)) { if (/^(?:OMX|GJC)_/.test(name)) delete process.env[name]; } process.env.OMX_ROOT = cwd; process.env.PATH = `${dirname(process.execPath)}:/usr/bin:/bin`; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-absolute-cli-readonly"; const leaderThreadId = "thread-conductor-absolute-cli-readonly-leader"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeCanonicalLeaderFixture(stateDir, sessionId, leaderThreadId, cwd); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: sessionId, }); const absoluteOmx = join(installRoot, "bin", "omx"); await mkdir(dirname(absoluteOmx), { recursive: true }); await symlink(realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")), absoluteOmx); const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command: `${absoluteOmx} ultragoal status --json` }, }, { cwd }); assert.equal(result.outputJson, null); process.env.BASH_ENV = join(installRoot, "inherited-bash-env"); const checkpoint = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command: `${absoluteOmx} ultragoal checkpoint --goal-id G001-ship --status complete --evidence 'G001 complete verified by tests reviews deployment .omx/ultragoal/goals.json .omx/ultragoal/ledger.jsonl' --codex-goal-json .omx/ultragoal/g001-codex-goal-complete.json --quality-gate-json .omx/ultragoal/g001-quality-gate.json --json`, }, }, { cwd }); assert.equal(checkpoint.outputJson, null); const lookalikeOmx = join(installRoot, "lookalike", "bin", "omx"); await mkdir(dirname(lookalikeOmx), { recursive: true }); await writeFile(lookalikeOmx, "#!/usr/bin/env node\n", "utf-8"); await chmod(lookalikeOmx, 0o755); const lookalike = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command: `${lookalikeOmx} ultragoal status --json` }, }, { cwd }); assert.equal(lookalike.outputJson?.decision, "block"); const nested = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command: `bash -c '${absoluteOmx} ultragoal checkpoint --goal-id G001-ship --status complete --evidence done --codex-goal-json goal.json --quality-gate-json gate.json --json'` }, }, { cwd }); assert.equal(nested.outputJson?.decision, "block"); } finally { for (const name of Object.keys(process.env)) { if (/^(?:OMX|GJC)_/.test(name)) delete process.env[name]; } Object.assign(process.env, originalOmxEnvironment); if (originalPath === undefined) delete process.env.PATH; else process.env.PATH = originalPath; if (originalBashEnv === undefined) delete process.env.BASH_ENV; else process.env.BASH_ENV = originalBashEnv; await rm(cwd, { recursive: true, force: true }); await rm(installRoot, { recursive: true, force: true }); } }); it("allows finite Codex goal reads but denies lifecycle mutation without documented Main-root proof (#3300)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3300-goal-tool-transport-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3300-goal-tool-transport"; const leaderThreadId = "thread-3300-goal-tool-transport"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "checkpointing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "checkpointing", session_id: sessionId, }); const dispatch = (tool_name: string, tool_input: Record = {}) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name, tool_input, }, { cwd }); for (const [tool_name, tool_input] of [ ["Read", { file_path: "README.md" }], ["get_goal", {}], ["functions.get_goal", {}], ["functionsget_goal", {}], ] as const) { const result = await dispatch(tool_name, tool_input); assert.equal(result.outputJson, null, tool_name); } for (const [tool_name, tool_input] of [ ["create_goal", { objective: "complete the ultragoal plan" }], ["update_goal", { status: "complete" }], ["functions.create_goal", { objective: "complete the ultragoal plan" }], ["functions.update_goal", { status: "complete" }], ["functionscreate_goal", { objective: "complete the ultragoal plan" }], ["functionsupdate_goal", { status: "complete" }], ] as const) { const result = await dispatch(tool_name, tool_input); assert.equal(result.outputJson?.decision, "block", tool_name); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED|documented host-authenticated Main-root authority/, tool_name); } for (const tool_name of ["getgoal", "get_goals", "updateGoal", "createGoal", "functions.get_goals"]) { const blocked = await dispatch(tool_name, {}); assert.equal(blocked.outputJson?.decision, "block", tool_name); const reason = String(blocked.outputJson?.reason ?? ""); assert.match(reason, /OWNER_CONFIRMATION_REQUIRED|not a recognized read-only or explicitly authorized Conductor mutation transport/); assert.match(reason, new RegExp(tool_name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))); } // Stop reconciliation must be able to require a fresh get_goal without PreToolUse self-block. const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, }, { cwd }); const stopMessage = JSON.stringify(stop.outputJson ?? {}); // Stop may still emit guidance, but must not claim get_goal is an unrecognized Conductor transport. assert.doesNotMatch(stopMessage, /get_goal is not a recognized read-only or explicitly authorized Conductor mutation transport/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("hook-owns session-scoped omx cancel under active ultragoal conductor while impostors stay blocked", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-conductor-cancel-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ultragoal-conductor-cancel"; const leaderThreadId = "thread-ultragoal-conductor-cancel"; const sessionDir = join(stateDir, "sessions", sessionId); const ultragoalPath = join(sessionDir, "ultragoal-state.json"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } } } } }); const resetUltragoal = async () => { await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(ultragoalPath, { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); }; await resetUltragoal(); const bash = (command: string) => dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command }, }, { cwd }); const assertHandled = async (command: string, label: string) => { await resetUltragoal(); const result = await bash(command); assert.equal(result.outputJson?.decision, "block", label); assert.match(JSON.stringify(result.outputJson), /cancelled_exact_session/, label); assert.equal(JSON.parse(await readFile(ultragoalPath, "utf8")).active, false, label); }; await withCleanRunnerNodeEnvironment(async () => { await assertHandled("omx cancel", "plain cancellation"); await assertHandled("omx cancel --force", "force cancellation"); }); for (const [label, envName, envValue] of [ ["inherited bash startup file", "BASH_ENV", "/tmp/prelude.sh"], ["imported omx function shadow", "BASH_FUNC_omx%%", "() { printf owned > src/pwned.ts; }"], ["inherited node loader override", "NODE_OPTIONS", "--require=./payload.cjs"], ["inherited openssl config", "OPENSSL_CONF", "/tmp/evil.cnf"], ["inherited dynamic loader preload", "LD_PRELOAD", "/tmp/payload.so"], ["inherited node coverage output", "NODE_V8_COVERAGE", "/tmp/coverage-out"], ] as const) { const previousValue = process.env[envName]; process.env[envName] = envValue; try { await assertHandled("omx cancel --force", label); } finally { if (previousValue === undefined) delete process.env[envName]; else process.env[envName] = previousValue; } } for (const [label, command] of [ ["openssl config injection", "OPENSSL_CONF=/tmp/evil.cnf omx cancel"], ["path override", "PATH=/tmp/attacker omx cancel"], ["path-qualified impostor", "/tmp/omx cancel --force"], ["chained cancellation", "omx cancel --force && rm -rf x"], ["omx root override", "OMX_ROOT=/tmp/other omx cancel"], ["byte-order-mark lookalike executable", "\ufeffomx cancel"], ["carriage-return suffix lookalike", "omx cancel\r"], ["unicode nbsp separator", "omx\u00a0cancel"], ] as const) { await resetUltragoal(); const impostor = await bash(command); assert.equal(impostor.outputJson?.decision, "block", label); assert.doesNotMatch(JSON.stringify(impostor.outputJson), /cancelled_exact_session/, label); assert.equal(JSON.parse(await readFile(ultragoalPath, "utf8")).active, true, label); } await resetUltragoal(); const stateClear = await bash("omx state clear --force --mode ultragoal --json"); assert.equal(stateClear.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks unsupported active conductor source edits with native delegation recovery guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unsupported-conductor-source-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-unsupported-conductor-source"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-unsupported-conductor-source", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-unsupported-conductor-source", threads: { "thread-unsupported-conductor-source": { thread_id: "thread-unsupported-conductor-source", kind: "leader" } } } } }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); await writeJson(join(stateDir, "native-subagent-support.json"), { schema_version: 1, status: "unsupported", reason: "multi_agent_v1_unavailable", session_id: sessionId, evidence: "unknown tool: multi_agent_v1.spawn_agent", observed_at: new Date().toISOString(), cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-unsupported-conductor-source", agent_id: "thread-unsupported-conductor-source", tool_name: "Write", tool_input: { file_path: "src/runtime.ts", content: "export const value = 1;\n" }, }, { cwd }, ); const output = result.outputJson as { decision?: string; hookSpecificOutput?: { additionalContext?: string } } | null; const context = String(output?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(output?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: planning\)/); assert.match(context, /Native subagent support is unavailable in this environment/); assert.match(context, /Reason: multi_agent_v1_unavailable/); assert.match(context, /blocked\/cancelled/); assert.match(context, /do not call multi_agent_v1\.close_agent/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the Conductor unblocked: quoted redirect/source text is not a write target and terminal blocked-state writes survive genuinely unsupported native delegation (#3119)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-3119-quote-deadlock-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-3119-quote-deadlock"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-conductor-3119-quote-deadlock", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-conductor-3119-quote-deadlock", threads: { "thread-conductor-3119-quote-deadlock": { thread_id: "thread-conductor-3119-quote-deadlock", kind: "leader" } } } } }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); // Native delegation is genuinely unsupported (explicit negative evidence). await writeJson(join(stateDir, "native-subagent-support.json"), { schema_version: 1, status: "unsupported", reason: "multi_agent_v1_unavailable", session_id: sessionId, evidence: "unknown tool: multi_agent_v1.spawn_agent", observed_at: new Date().toISOString(), cwd, }); const dispatch = (command: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-3119-quote-deadlock", agent_id: "thread-conductor-3119-quote-deadlock", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); // Deadlock prevention (defect B): the Conductor must still terminalize its // own workflow state even when delegation is genuinely unsupported, even // when the JSON payload contains a `>` character. const terminalBlockedWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-3119-quote-deadlock", agent_id: "thread-conductor-3119-quote-deadlock", tool_name: "mcp__omx_state__state_write", tool_input: { mode: "ultragoal", active: true, current_phase: "blocked", reason: "native delegation unavailable -> terminalized", session_id: sessionId, workingDirectory: cwd, }, }, { cwd }); assert.notEqual((terminalBlockedWrite.outputJson as { decision?: string } | null)?.decision, "block"); // Defect C: quoted regex/source text with redirect metacharacters is not a // write target, so issue creation is not falsely blocked. const issueCreate = await dispatch( "printf '%s\\n' 'Guard regex /[^>]+>{1,2}/ is quoted data, not a redirect'", ); assert.notEqual((issueCreate.outputJson as { decision?: string } | null)?.decision, "block"); // Fail-closed preserved: a REAL unquoted redirect to a non-metadata path is // still blocked. const realRedirect = await dispatch("printf pwn > src/runtime.ts"); assert.equal((realRedirect.outputJson as { decision?: string } | null)?.decision, "block"); assert.match( String((realRedirect.outputJson as { reason?: string } | null)?.reason ?? ""), /not workflow state\/ledger\/mailbox\/handoff metadata|Bash redirect target is not a static workflow metadata leaf/, ); // A real unquoted redirect to allowed workflow metadata still passes. const metadataRedirect = await dispatch("printf blocked > .omx/state/conductor.log"); assert.notEqual((metadataRedirect.outputJson as { decision?: string } | null)?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fail-closed: escaped-quote and ANSI-C quoted redirects to source stay blocked while legit quoted data and nested shells behave (#3119)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-3119-escaped-quote-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-3119-escaped-quote"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-conductor-3119-escaped-quote", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-conductor-3119-escaped-quote", threads: { "thread-conductor-3119-escaped-quote": { thread_id: "thread-conductor-3119-escaped-quote", kind: "leader" } } } } }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const dispatch = (command: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-3119-escaped-quote", agent_id: "thread-conductor-3119-escaped-quote", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); // A top-level escaped quote (\' \") is a LITERAL char in bash, not a quote // opener, and $'...' is ANSI-C quoting. None of these may hide the real // `>` redirect \u2014 all of these genuinely write src/runtime.ts and MUST block. const mustBlock = [ "printf pwn > src/runtime.ts", // plain control "printf pwn \\' > src/runtime.ts \\'", // escaped single quote "printf pwn \\\" > src/runtime.ts \\\"", // escaped double quote "printf pwn $'\\'' > src/runtime.ts $'\\''", // ANSI-C escaped quote "bash -c 'printf pwn > src/runtime.ts'", // nested shell redirect ]; for (const command of mustBlock) { const result = await dispatch(command); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); assert.match( String((result.outputJson as { reason?: string } | null)?.reason ?? ""), /not workflow state\/ledger\/mailbox\/handoff metadata|Bash redirect target is not a static workflow metadata leaf/, command, ); } // Legitimate quoted DATA metacharacters (single quotes, double quotes, // ANSI-C) are not redirects and must not be falsely blocked. const mustNotBlock = [ "echo \"value > threshold\"", "printf $'a>b'", ]; for (const command of mustNotBlock) { const result = await dispatch(command); assert.notEqual((result.outputJson as { decision?: string } | null)?.decision, "block", command); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root ralplan writes even when payload has only a typed agent_role", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-agent-role-main-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-agent-role-main"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ralplan-agent-role-main", }); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralplan-agent-role-main", agent_role: "executor", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active \(phase: planning\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation when only a typed agent_role is present", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-agent-role-main-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-agent-role-main"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-conductor-agent-role-main", }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-agent-role-main", agent_role: "executor", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation for typed child source, unowned, and conductor-metadata mutations", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-tracked-subagent-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-tracked-subagent"; const leaderThreadId = "thread-conductor-leader"; const childThreadId = "thread-conductor-child"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeFile(join(cwd, "README.md"), "read-only source\n", "utf-8"); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId, }, }, }, tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /Main-root/); assert.match( String((result.outputJson?.hookSpecificOutput as { additionalContext?: unknown } | undefined)?.additionalContext ?? ""), /OWNER_CONFIRMATION_REQUIRED/, ); const unownedWriter = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "writer", tool_name: "Write", tool_input: { file_path: "unowned/issue-3127.txt", content: "unowned\n" }, }, { cwd }, ); assert.equal(unownedWriter.outputJson?.decision, "block"); assert.match(String(unownedWriter.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(unownedWriter.outputJson?.reason ?? ""), /Main-root/); const metadataWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", tool_name: "Write", tool_input: { file_path: ".omx/state/conductor-child.json", content: "{}\n" }, }, { cwd }, ); assert.equal(metadataWrite.outputJson?.decision, "block"); assert.match(String(metadataWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(metadataWrite.outputJson?.reason ?? ""), /Main-root/); for (const command of [ "printf '{}' > .omx/state/conductor-child.json", "touch .omx/handoffs/run-1/conductor-child.json", ]) { const metadataBash = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal(metadataBash.outputJson?.decision, "block", command); assert.match(String(metadataBash.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, command); assert.doesNotMatch(String(metadataBash.outputJson?.reason ?? ""), /Main-root/, command); } const readOnlyBash = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", tool_name: "Bash", tool_input: { command: "cat src/conductor-owned.ts" }, }, { cwd }, ); assert.equal(readOnlyBash.outputJson, null); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("uses hook-native agent_id as child provenance without borrowing Team or legacy identity", async () => withCleanAmbientNodeRuntimeEnvironment(async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-agent-id-")); const originalTeamWorker = process.env.OMX_TEAM_WORKER; const originalInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const originalOmxSessionId = process.env.OMX_SESSION_ID; const originalGjcSessionId = process.env.GJC_SESSION_ID; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-hook-native-agent-id"; const leaderThreadId = "thread-conductor-hook-native-agent-id-leader"; delete process.env.OMX_SESSION_ID; process.env.GJC_SESSION_ID = sessionId; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await mkdir(join(cwd, ".git"), { recursive: true }); await mkdir(join(cwd, "src", "shared"), { recursive: true }); await mkdir(join(cwd, "src", "state"), { recursive: true }); await mkdir(join(cwd, ".omx", "state", "tmp"), { recursive: true }); await mkdir(join(cwd, ".omx", "state", "inbox"), { recursive: true }); await symlink(join(cwd, "src", "dangling-target.ts"), join(cwd, ".omx", "state", "inbox", "dangling")); await mkdir(join(cwd, "src", "subdir"), { recursive: true }); await mkdir(join(stateDir, "bash-home"), { recursive: true }); await writeFile(join(cwd, "src", "runtime.ts"), "export {};\n", "utf-8"); await writeFile(join(cwd, "a"), "finite metadata source\n", "utf-8"); await mkdir(join(stateDir, "zsh-home"), { recursive: true }); await writeFile(join(stateDir, "zsh-home", ".zshenv"), "touch src/zsh-owned.ts\n", "utf-8"); await writeFile(join(stateDir, "inbox", "time"), "#!/bin/sh\ntouch src/time-wrapper-owned.ts\n", "utf-8"); await chmod(join(stateDir, "inbox", "time"), 0o755); await writeFile(join(stateDir, "inbox", "node"), "#!/bin/sh\ntouch src/shebang-owned.ts\n", "utf-8"); await chmod(join(stateDir, "inbox", "node"), 0o755); await writeFile(join(stateDir, "bash-home", ".bashrc"), "touch src/interactive-owned.ts\n", "utf-8"); await symlink(join(cwd, "src", "subdir"), join(stateDir, "link")); await symlink(join(cwd, "src"), join(stateDir, "inbox", "product-dir")); await symlink(join(cwd, "src", "runtime.ts"), join(stateDir, "curl-glob-1.log")); await writeFile(join(stateDir, "conductor-ledger.json"), "{}\n", "utf-8"); await writeFile(join(stateDir, "reference-copy"), "metadata reference target\n", "utf-8"); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: sessionId, }); const workspacePackageCli = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const trustedPackageBin = join(cwd, "node_modules", ".bin", "omx"); await mkdir(dirname(trustedPackageBin), { recursive: true }); await symlink(workspacePackageCli, trustedPackageBin); const trustedPackagePath = `${dirname(trustedPackageBin)}:/usr/bin:/bin`; const dispatchWrite = (identity: Record) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, ...identity, tool_name: "Write", tool_input: { file_path: "src/hook-native-agent-id.ts", content: "export {};\n" }, }, { cwd }, ); const dispatchBash = (name: string, identity: Record, command: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, ...identity, tool_name: "Bash", tool_use_id: `tool-hook-native-agent-id-${name}`, tool_input: { command }, }, { cwd }, ); const dispatchBashWithTrustedPackageCli = async (name: string, identity: Record, command: string) => { const inheritedPath = process.env.PATH; process.env.PATH = trustedPackagePath; try { return await dispatchBash(name, identity, command); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }; const directNpmBinPathShadow = join(stateDir, "inbox", "cat"); await symlink("/usr/bin/touch", directNpmBinPathShadow); const inheritedPathForNpmBinPathScan = process.env.PATH; process.env.PATH = `${dirname(trustedPackageBin)}:${join(stateDir, "inbox")}:/usr/bin:/bin`; try { for (const [name, identity] of [ ["main", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-npm-bin-path-scan" }], ] as const) { const result = await dispatchBash(`npm-bin-path-scan-${name}`, identity, "cat src/path-owned.ts"); assert.equal(result.outputJson?.decision, "block", name); } } finally { if (inheritedPathForNpmBinPathScan === undefined) delete process.env.PATH; else process.env.PATH = inheritedPathForNpmBinPathScan; } const directPackageNodeShadow = join(dirname(trustedPackageBin), "node"); await writeFile(directPackageNodeShadow, "#!/bin/sh\ntouch src/omx-status-owned.ts\n", "utf-8"); await chmod(directPackageNodeShadow, 0o755); const inheritedPathForPackageRead = process.env.PATH; process.env.PATH = `${dirname(trustedPackageBin)}:/usr/bin:/bin`; try { for (const [name, identity] of [ ["main", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-package-read" }], ] as const) { const result = await dispatchBash(`package-read-node-shadow-${name}`, identity, "omx status"); assert.equal(result.outputJson?.decision, "block", name); } } finally { if (inheritedPathForPackageRead === undefined) delete process.env.PATH; else process.env.PATH = inheritedPathForPackageRead; await rm(directPackageNodeShadow, { force: true }); } delete process.env.OMX_TEAM_INTERNAL_WORKER; process.env.OMX_TEAM_WORKER = "hook-native-agent-id/worker"; // Codex 0.142.5 supplies agent_id without thread_id or source for spawned children. const writerAgentId = await dispatchWrite({ agent_id: "agent-hook-native-writer", agent_role: "writer" }); assert.equal(writerAgentId.outputJson?.decision, "block"); assert.match(String(writerAgentId.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(writerAgentId.outputJson?.reason ?? ""), /Main-root|PROVENANCE_DENIED/); // An identity conflict must not borrow the Team-worker environment either. const conflictingIdentity = await dispatchWrite({ agent_id: "agent-hook-native-conflict", thread_id: "thread-hook-native-conflict", agent_role: "executor", }); assert.equal(conflictingIdentity.outputJson?.decision, "block"); assert.match(String(conflictingIdentity.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); assert.doesNotMatch(String(conflictingIdentity.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED|Main-root/); for (const identity of [ { agent_id: leaderThreadId, thread_id: "thread-hook-native-foreign" }, { agent_id: "agent-hook-native-foreign", thread_id: leaderThreadId }, ]) { const conflict = await dispatchWrite(identity); assert.equal(conflict.outputJson?.decision, "block"); assert.match(String(conflict.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); } for (const [name, identity] of [ ["owner-current-thread", { owner_codex_thread_id: leaderThreadId, thread_id: "thread-hook-native-owner-conflict" }], ["session-alias", { sessionId: "foreign-session", agent_id: leaderThreadId, thread_id: leaderThreadId }], ["reversed-session-alias", { session_id: "foreign-session", sessionId, agent_id: leaderThreadId, thread_id: leaderThreadId }], ["owner-agent", { owner_codex_thread_id: "thread-hook-native-owner-conflict", agent_id: leaderThreadId }], ] as const) { const conflict = await dispatchWrite(identity); assert.equal(conflict.outputJson?.decision, "block", name); assert.match(String(conflict.outputJson?.reason ?? ""), /PROVENANCE_DENIED/, name); } for (const [name, sessionAliases] of [ ["canonical-first", { session_id: sessionId, sessionId: "foreign-session" }], ["foreign-first", { session_id: "foreign-session", sessionId }], ] as const) { for (const [transport, toolName, toolInput] of [ ["path", "Write", { file_path: "src/alias-bypass.ts", content: "owned\n" }], ["bash", "Bash", { command: "printf pwn >& src/alias-bypass.ts" }], ["state", "mcp__omx_state__state_write", { mode: "ultragoal", active: true }], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, ...sessionAliases, agent_id: leaderThreadId, thread_id: leaderThreadId, tool_name: toolName, tool_input: toolInput, }, { cwd }); assert.equal(result.outputJson?.decision, "block", `${name}/${transport}`); assert.match(String(result.outputJson?.reason ?? ""), /PROVENANCE_DENIED/, `${name}/${transport}`); } } const consistentLeaderIdentity = await dispatchWrite({ agent_id: leaderThreadId, thread_id: leaderThreadId }); assert.equal(consistentLeaderIdentity.outputJson?.decision, "block"); assert.match(String(consistentLeaderIdentity.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); const sessionClaimedAsLeader = await dispatchWrite({ owner_codex_session_id: leaderThreadId, owner_omx_session_id: sessionId, }); assert.equal(sessionClaimedAsLeader.outputJson?.decision, "block"); assert.match(String(sessionClaimedAsLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(sessionClaimedAsLeader.outputJson?.reason ?? ""), /Main-root|PROVENANCE_DENIED/); if (originalTeamWorker === undefined) delete process.env.OMX_TEAM_WORKER; else process.env.OMX_TEAM_WORKER = originalTeamWorker; if (originalInternalTeamWorker === undefined) delete process.env.OMX_TEAM_INTERNAL_WORKER; else process.env.OMX_TEAM_INTERNAL_WORKER = originalInternalTeamWorker; for (const [name, identity] of [ ["foreign-active-session", { session_id: "foreign-session", agent_id: leaderThreadId, thread_id: leaderThreadId }], ["absent-active-session", { agent_id: leaderThreadId, thread_id: leaderThreadId }], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, ...identity, tool_name: "Write", tool_input: { file_path: "src/session-bypass.ts", content: "owned\n" }, }, { cwd }); assert.equal(result.outputJson?.decision, "block", name); assert.match(String(result.outputJson?.reason ?? ""), /PROVENANCE_DENIED/, name); } const identitylessNativeSessionRemote = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: leaderThreadId, tool_name: "mcp__omx_wiki__wiki_delete", tool_use_id: "identityless-native-session-remote", tool_input: { path: "src/session-bypass.ts" }, }, { cwd }); assert.equal(identitylessNativeSessionRemote.outputJson?.decision, "block"); assert.match(String(identitylessNativeSessionRemote.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED|Main-root Conductor mode is active/); for (const [name, toolInput] of [ ["direct-state-write-foreign-routing", { mode: "ultragoal", workingDirectory: "src", session_id: "foreign", active: false }], ["direct-state-write-unknown-key", { mode: "ultragoal", active: true, child_marker: "forbidden" }], ["direct-state-write-missing-session", { mode: "ultragoal", workingDirectory: cwd, active: true }], ["direct-state-write-missing-cwd", { mode: "ultragoal", session_id: sessionId, active: true }], ] as const) { const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, agent_id: leaderThreadId, thread_id: leaderThreadId, tool_name: "mcp__omx_state__state_write", tool_input: toolInput, }, { cwd }); assert.equal(result.outputJson?.decision, "block", name); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, name); } const directCanonicalStateWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, agent_id: leaderThreadId, thread_id: leaderThreadId, tool_name: "mcp__omx_state__state_write", tool_input: { mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: cwd }, }, { cwd }); assert.equal(directCanonicalStateWrite.outputJson, null); const directStateClear = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, agent_id: leaderThreadId, thread_id: leaderThreadId, tool_name: "mcp__omx_state__state_clear", tool_input: { mode: "ultragoal" }, }, { cwd }); assert.equal(directStateClear.outputJson?.decision, "block"); assert.match(String(directStateClear.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); const inheritedOmxSessionId = process.env.OMX_SESSION_ID; const inheritedGjcSessionId = process.env.GJC_SESSION_ID; try { process.env.OMX_SESSION_ID = "foreign"; process.env.GJC_SESSION_ID = "foreign"; const inheritedSessionWrite = await dispatchBashWithTrustedPackageCli( "inherited-foreign-session-state-write", { agent_id: leaderThreadId, thread_id: leaderThreadId }, `omx state write --input '{"mode":"ultragoal","active":true}' --json`, ); assert.equal(inheritedSessionWrite.outputJson?.decision, "block"); assert.match(String(inheritedSessionWrite.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); } finally { if (inheritedOmxSessionId === undefined) delete process.env.OMX_SESSION_ID; else process.env.OMX_SESSION_ID = inheritedOmxSessionId; if (inheritedGjcSessionId === undefined) delete process.env.GJC_SESSION_ID; else process.env.GJC_SESSION_ID = inheritedGjcSessionId; } const noncanonicalCwdPathWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd: join(cwd, "src"), session_id: sessionId, agent_id: leaderThreadId, thread_id: leaderThreadId, tool_name: "Write", tool_input: { file_path: ".omx/state/inbox/cwd-bypass", content: "owned\n" }, }, { cwd: join(cwd, "src") }); assert.equal(noncanonicalCwdPathWrite.outputJson?.decision, "block"); assert.match(String(noncanonicalCwdPathWrite.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); const executorAgentId = await dispatchWrite({ agent_id: "agent-hook-native-executor", agent_role: "executor" }); assert.equal(executorAgentId.outputJson?.decision, "block"); assert.match(String(executorAgentId.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(executorAgentId.outputJson?.reason ?? ""), /Main-root|PROVENANCE_DENIED/); const currentRuntimeNodePath = process.execPath; const untrustedExternalBin = await mkdtemp(join(tmpdir(), "omx-native-hook-external-path-")); const untrustedExternalCat = join(untrustedExternalBin, "cat"); const untrustedExternalGh = join(untrustedExternalBin, "gh"); const untrustedExternalNode = join(untrustedExternalBin, "node"); const untrustedExternalNodeLookalike = join(untrustedExternalBin, "node-copy"); const untrustedExternalEnv = join(untrustedExternalBin, "env"); for (const [nodeName, nodeCommand] of [ ["current-runtime-node-bare-read", `node -e "require('fs').readFileSync('src/victim.ts','utf8')"`], ["current-runtime-node-absolute-read", `${currentRuntimeNodePath} -e "require('fs').readFileSync('src/victim.ts','utf8')"`], ] as const) { for (const [actor, identity] of [ ["main", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-current-runtime" }], ] as const) { const result = await dispatchBash(nodeName, identity, nodeCommand); assert.equal(result.outputJson, null, `${actor}/${nodeName}`); } } for (const [actor, identity] of [ ["main", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-current-runtime-loader" }], ] as const) { const result = await dispatchBash( "current-runtime-node-loader", identity, `LD_PRELOAD=.omx/state/mutator.so ${currentRuntimeNodePath} -e "require('fs').readFileSync('src/victim.ts','utf8')"`, ); assert.equal(result.outputJson?.decision, "block", actor); assert.match( String(result.outputJson?.reason ?? ""), actor === "main" ? /Main-root Conductor mode is active/ : /OWNER_CONFIRMATION_REQUIRED/, actor, ); } try { for (const [name, identity] of [ ["main", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-external-path" }], ] as const) { const result = await dispatchBash( `external-path-without-cat-candidate-${name}`, identity, `PATH=${untrustedExternalBin}:${process.env.PATH || "/usr/bin:/bin"} cat src/conductor-owned.ts`, ); assert.equal(result.outputJson, null, name); } await symlink("/bin/cat", untrustedExternalCat); await writeFile(untrustedExternalGh, "#!/bin/sh\nexit 0\n", "utf-8"); await chmod(untrustedExternalGh, 0o755); await writeFile(untrustedExternalNode, "#!/bin/sh\nexit 0\n", "utf-8"); await chmod(untrustedExternalNode, 0o755); await writeFile(untrustedExternalNodeLookalike, "#!/bin/sh\nexit 0\n", "utf-8"); await chmod(untrustedExternalNodeLookalike, 0o755); await writeFile(untrustedExternalEnv, "#!/bin/sh\nexit 0\n", "utf-8"); await chmod(untrustedExternalEnv, 0o755); for (const [name, identity] of [ ["main", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-external-path" }], ] as const) { for (const command of [ `${untrustedExternalCat} src/conductor-owned.ts`, `PATH=${untrustedExternalBin}:${process.env.PATH || "/usr/bin:/bin"} cat src/conductor-owned.ts`, `${untrustedExternalGh} issue create --title x --body y`, `PATH=${untrustedExternalBin}:${process.env.PATH || "/usr/bin:/bin"} gh issue create --title x --body y`, `${untrustedExternalNode} -e "require('fs').readFileSync('src/victim.ts','utf8')"`, `PATH=${untrustedExternalBin}:/usr/bin:/bin node -e "require('fs').readFileSync('src/victim.ts','utf8')"`, `${untrustedExternalNodeLookalike} -e "require('fs').readFileSync('src/victim.ts','utf8')"`, `PATH=${untrustedExternalBin}:/usr/bin:/bin env cat src/conductor-owned.ts`, ]) { const result = await dispatchBash(`untrusted-external-${name}`, identity, command); assert.equal(result.outputJson?.decision, "block", `${name}/${command}`); assert.match( String(result.outputJson?.reason ?? ""), name === "main" ? /Main-root Conductor mode is active/ : /OWNER_CONFIRMATION_REQUIRED/, `${name}/${command}`, ); } } } finally { await rm(untrustedExternalBin, { recursive: true, force: true }); } for (const [name, command] of [...WGET_REVIEW_MUTATION_COMMANDS, ...ARGUMENT_PRODUCING_RUNTIME_DENIAL_COMMANDS]) { const nativeChildBash = await dispatchBash(`${name}-child`, { agent_id: `agent-hook-native-${name}` }, command); assert.equal(nativeChildBash.outputJson?.decision, "block", name); assert.match(String(nativeChildBash.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); const mainRootBash = await dispatchBash(`${name}-main`, { agent_id: leaderThreadId }, command); assert.equal(mainRootBash.outputJson?.decision, "block", name); assert.match(String(mainRootBash.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, name); } for (const [name, command] of WGET_MAIN_METADATA_MUTATION_COMMANDS) { const nativeChildBash = await dispatchBash(`${name}-child`, { agent_id: `agent-hook-native-${name}` }, command); assert.equal(nativeChildBash.outputJson?.decision, "block", name); assert.match(String(nativeChildBash.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); const mainRootBash = await dispatchBash(`${name}-main`, { agent_id: leaderThreadId }, command); assert.equal(mainRootBash.outputJson, null, name); } const compiledCliStateWrite = `omx state write --input '${JSON.stringify({ mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: cwd })}' --json`; const nativeChildCliStateWrite = await dispatchBashWithTrustedPackageCli( "cli-state-write-child", { agent_id: "agent-hook-native-cli-state-write" }, compiledCliStateWrite, ); assert.equal(nativeChildCliStateWrite.outputJson?.decision, "block", "cli-state-write-child"); assert.match(String(nativeChildCliStateWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "cli-state-write-child"); const mainRootCliStateWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "mcp__omx_state__state_write", tool_use_id: "cli-state-write-main", tool_input: { mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: cwd, }, }, { cwd }); assert.equal(mainRootCliStateWrite.outputJson, null, "cli-state-write-main"); for (const [name, prefix] of [ ["poisoned-omx-root", `OMX_ROOT=${join(cwd, "src")} PATH=${trustedPackagePath}`], ["poisoned-omx-state-root", `OMX_STATE_ROOT=${join(cwd, "src", "state")} PATH=${trustedPackagePath}`], ["unknown-omx-runtime-output", `OMX_UNREVIEWED_OUTPUT=src/output PATH=${trustedPackagePath}`], ["unknown-omx-runtime-environment", `OMX_UNREVIEWED_HELPER=src/mutator PATH=${trustedPackagePath}`], ["unknown-gjc-runtime-environment", `GJC_UNREVIEWED_HELPER=src/mutator PATH=${trustedPackagePath}`], ] as const) { for (const [surface, command] of [ ["state-write", compiledCliStateWrite], ["read-only-omx", "omx status"], ] as const) { const result = await dispatchBash(`${name}-${surface}`, { agent_id: leaderThreadId }, `${prefix} ${command}`); assert.equal(result.outputJson?.decision, "block", `${name}-${surface}`); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, `${name}-${surface}`); } } const compiledFunctionPersistedRsyncEnvironment = `poison(){ export RSYNC_PARTIAL_DIR=${join(cwd, "src", "rsync-partials")}; }; poison; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`; const compiledFunctionPersistedOmxEnvironment = `poison(){ export OMX_STATE_ROOT=${join(cwd, "src", "state")}; }; poison; omx state write --input '{"mode":"ultragoal","active":true}' --json`; for (const [name, command] of [ ["function-persisted-rsync-runtime-environment", compiledFunctionPersistedRsyncEnvironment], ["function-persisted-omx-runtime-environment", compiledFunctionPersistedOmxEnvironment], ["function-persisted-gjc-runtime-environment", `poison(){ export GJC_UNREVIEWED_HELPER=src/mutator; }; poison; omx status`], ["nameref-rsync-runtime-environment", `declare -n poison=RSYNC_PARTIAL_DIR; poison=src/rsync-partials; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`], ["joined-rsync-runtime-environment", `if true; then export RSYNC_PARTIAL_DIR=src/rsync-partials; fi; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`], ["nested-omx-runtime-environment", `export OMX_STATE_ROOT=${join(cwd, "src", "state")}; bash --noprofile --norc -c 'omx status'`], ["function-readonly-omx-root-failed-unset", `poison(){ readonly OMX_ROOT=${join(cwd, "src")}; unset OMX_ROOT; }; poison; omx status`], ["function-readonly-rsync-runtime-failed-unset", `poison(){ readonly RSYNC_PARTIAL_DIR=src/rsync-partials; unset RSYNC_PARTIAL_DIR; }; poison; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`], ["function-readonly-gjc-runtime-failed-unset", `poison(){ readonly GJC_UNREVIEWED_HELPER=src/mutator; unset GJC_UNREVIEWED_HELPER; }; poison; omx status`], ] as const) { for (const [actor, identity] of [ ["main-root", { agent_id: leaderThreadId }], ["native-child", { agent_id: "agent-hook-native-function-persisted-environment" }], ] as const) { const result = await dispatchBashWithTrustedPackageCli(`${name}-${actor}`, identity, command); assert.equal(result.outputJson?.decision, "block", `${name}-${actor}`); assert.match(String(result.outputJson?.reason ?? ""), actor === "main-root" ? /Main-root Conductor mode is active/ : /OWNER_CONFIRMATION_REQUIRED/, `${name}-${actor}`); } } const compiledModeCliStateWrite = `OMX_SESSION_ID=${sessionId} omx state write --mode=ultragoal --input '${JSON.stringify({ active: true, current_phase: "blocked", reason: "native delegation unavailable -> terminalized", session_id: sessionId, workingDirectory: cwd })}' --json`; const nativeChildModeCliStateWrite = await dispatchBashWithTrustedPackageCli( "mode-cli-state-write-child", { agent_id: "agent-hook-native-mode-cli-state-write" }, compiledModeCliStateWrite, ); assert.equal(nativeChildModeCliStateWrite.outputJson?.decision, "block", "mode-cli-state-write-child"); assert.match(String(nativeChildModeCliStateWrite.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, "mode-cli-state-write-child"); const mainRootModeCliStateWrite = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "mcp__omx_state__state_write", tool_use_id: "mode-cli-state-write-main", tool_input: { mode: "ultragoal", active: true, current_phase: "blocked", reason: "native delegation unavailable -> terminalized", session_id: sessionId, workingDirectory: cwd, }, }, { cwd }); assert.equal(mainRootModeCliStateWrite.outputJson, null, "mode-cli-state-write-main"); const compiledDynamicCliStateWrite = `omx state write --input "$STATE_INPUT" --json`; for (const [actor, identity] of [ ["native-child", { agent_id: "agent-hook-native-dynamic-cli-state-write" }], ["main-root", { agent_id: leaderThreadId }], ] as const) { const dynamicCliStateWrite = await dispatchBashWithTrustedPackageCli(`dynamic-cli-state-write-${actor}`, identity, compiledDynamicCliStateWrite); assert.equal(dynamicCliStateWrite.outputJson?.decision, "block", `dynamic-cli-state-write-${actor}`); assert.match( String(dynamicCliStateWrite.outputJson?.reason ?? ""), actor === "native-child" ? /OWNER_CONFIRMATION_REQUIRED/ : /Main-root Conductor mode is active/, `dynamic-cli-state-write-${actor}`, ); } const compiledUnknownStateWriteFlag = `omx state write --unexpected --input '{"mode":"ultragoal","active":true}' --json`; for (const [actor, identity] of [ ["native-child", { agent_id: "agent-hook-native-unknown-state-write-flag" }], ["main-root", { agent_id: leaderThreadId }], ] as const) { const unknownStateWriteFlag = await dispatchBashWithTrustedPackageCli(`unknown-state-write-flag-${actor}`, identity, compiledUnknownStateWriteFlag); assert.equal(unknownStateWriteFlag.outputJson?.decision, "block", `unknown-state-write-flag-${actor}`); assert.match( String(unknownStateWriteFlag.outputJson?.reason ?? ""), actor === "native-child" ? /OWNER_CONFIRMATION_REQUIRED/ : /Main-root Conductor mode is active/, `unknown-state-write-flag-${actor}`, ); } for (const [name, command] of [ ["foreign-routing-state-write", `omx state write --input '{"mode":"ultragoal","workingDirectory":"src","session_id":"foreign","active":false}' --json`], ["unknown-state-write-payload-key", `omx state write --input '{"mode":"ultragoal","active":true,"child_marker":"forbidden"}' --json`], ["conflicting-mode-state-write", `omx state write --mode=ultragoal --input '{"mode":"ralph","active":true}' --json`], ["foreign-session-environment-state-write", `OMX_SESSION_ID=foreign omx state write --input '{"mode":"ultragoal","active":true}' --json`], ["unset-session-environment-state-write", `env -uOMX_SESSION_ID omx state write --input '{"mode":"ultragoal","active":true}' --json`], ["shell-unset-session-state-write", `unset OMX_SESSION_ID; omx state write --input '{"mode":"ultragoal","active":true}' --json`], ["noncanonical-cwd-state-write", `cd src; omx state write --input '{"mode":"ultragoal","active":true}' --json`], ["canonical-session-missing-cwd-state-write", `OMX_SESSION_ID=${sessionId} omx state write --input '${JSON.stringify({ mode: "ultragoal", session_id: sessionId, active: true })}' --json`], ["canonical-session-blank-cwd-state-write", `OMX_SESSION_ID=${sessionId} omx state write --input '${JSON.stringify({ mode: "ultragoal", session_id: sessionId, workingDirectory: "", active: true })}' --json`], ] as const) { for (const [actor, identity] of [ ["main-root", { agent_id: leaderThreadId }], ["native-child", { agent_id: `agent-hook-native-${name}` }], ] as const) { const result = await dispatchBashWithTrustedPackageCli(`${name}-${actor}`, identity, command); assert.equal(result.outputJson?.decision, "block", `${name}-${actor}`); assert.match( String(result.outputJson?.reason ?? ""), actor === "main-root" ? /Main-root Conductor mode is active/ : /OWNER_CONFIRMATION_REQUIRED/, `${name}-${actor}`, ); } } const [mixedReferenceStateName, mixedReferenceStateCommand] = NATIVE_CHILD_MIXED_REFERENCE_STATE_WRITE; const referenceOnlyCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy`; const mainReferenceOnly = await dispatchBash( `${mixedReferenceStateName}-main-reference-only`, { agent_id: leaderThreadId }, referenceOnlyCommand, ); assert.equal(mainReferenceOnly.outputJson, null, `${mixedReferenceStateName}-main-reference-only`); const childReferenceOnly = await dispatchBash( `${mixedReferenceStateName}-native-child-reference-only`, { agent_id: "agent-hook-native-reference-only" }, referenceOnlyCommand, ); assert.equal(childReferenceOnly.outputJson?.decision, "block", `${mixedReferenceStateName}-native-child-reference-only`); assert.match(String(childReferenceOnly.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const nativeChildMixedReferenceState = await dispatchBashWithTrustedPackageCli( `${mixedReferenceStateName}-child`, { agent_id: `agent-hook-native-${mixedReferenceStateName}` }, mixedReferenceStateCommand, ); assert.equal(nativeChildMixedReferenceState.outputJson?.decision, "block", mixedReferenceStateName); assert.match(String(nativeChildMixedReferenceState.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, mixedReferenceStateName); const mainRootMixedReferenceState = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "mcp__omx_state__state_write", tool_use_id: `${mixedReferenceStateName}-main`, tool_input: { mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: cwd, }, }, { cwd }); assert.equal(mainRootMixedReferenceState.outputJson, null, mixedReferenceStateName); const [nativeChildRsyncAuthorityName, nativeChildRsyncAuthorityCommand] = NATIVE_CHILD_RSYNC_AUTHORITY_TARGET; const nativeChildRsyncAuthority = await dispatchBash( nativeChildRsyncAuthorityName, { agent_id: `agent-hook-native-${nativeChildRsyncAuthorityName}` }, nativeChildRsyncAuthorityCommand, ); assert.equal(nativeChildRsyncAuthority.outputJson?.decision, "block", nativeChildRsyncAuthorityName); assert.match(String(nativeChildRsyncAuthority.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, nativeChildRsyncAuthorityName); const [referenceUnknownName, referenceUnknownCommand] = NATIVE_CHILD_REFERENCE_UNKNOWN_COMMAND; const nativeChildReferenceUnknown = await dispatchBash( `${referenceUnknownName}-child`, { agent_id: `agent-hook-native-${referenceUnknownName}` }, referenceUnknownCommand, ); assert.equal(nativeChildReferenceUnknown.outputJson?.decision, "block", referenceUnknownName); assert.match(String(nativeChildReferenceUnknown.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, referenceUnknownName); const mainRootReferenceUnknown = await dispatchBash( `${referenceUnknownName}-main`, { agent_id: leaderThreadId }, referenceUnknownCommand, ); assert.equal(mainRootReferenceUnknown.outputJson?.decision, "block", referenceUnknownName); assert.match(String(mainRootReferenceUnknown.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, referenceUnknownName); for (const [name, command] of [ ["gh-api-dynamic-method", `gh api --method "$GH_METHOD" /repos/OWNER/REPO/issues`], ["omx-unknown-mutation", `omx unrecognized mutate --status failed`], ] as const) { for (const [actor, identity] of [ ["native-child", { agent_id: `agent-hook-native-${name}` }], ["main-root", { agent_id: leaderThreadId }], ] as const) { const result = await dispatchBash(`${name}-${actor}`, identity, command); assert.equal(result.outputJson?.decision, "block", `${name}-${actor}`); assert.match( String(result.outputJson?.reason ?? ""), actor === "native-child" ? /OWNER_CONFIRMATION_REQUIRED/ : /Main-root Conductor mode is active/, `${name}-${actor}`, ); } } const originalPosixlyCorrect = process.env.POSIXLY_CORRECT; try { process.env.POSIXLY_CORRECT = "1"; for (const [name, command] of WGET_INHERITED_POSIX_COMMANDS) { const nativeChildBash = await dispatchBash(`${name}-child`, { agent_id: `agent-hook-native-${name}` }, command); assert.equal(nativeChildBash.outputJson?.decision, "block", name); assert.match(String(nativeChildBash.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); const mainRootBash = await dispatchBash(`${name}-main`, { agent_id: leaderThreadId }, command); assert.equal(mainRootBash.outputJson?.decision, "block", name); assert.match(String(mainRootBash.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, name); } } finally { if (originalPosixlyCorrect === undefined) delete process.env.POSIXLY_CORRECT; else process.env.POSIXLY_CORRECT = originalPosixlyCorrect; } for (const [name, command] of WGET_READ_ONLY_CONTROL_COMMANDS) { const nativeChildBash = await dispatchBash(`${name}-child`, { agent_id: `agent-hook-native-${name}` }, command); assert.equal(nativeChildBash.outputJson, null, name); const mainRootBash = await dispatchBash(`${name}-main`, { agent_id: leaderThreadId }, command); assert.equal(mainRootBash.outputJson, null, name); } for (const [name, command] of [ ["cleared-environment-empty-path", `env -i PATH= sh -c 'wget --no-config -O - https://example.test/file'`], ["cleared-environment-relative-path", `env -i PATH=. sh -c 'wget --no-config -O - https://example.test/file'`], ["cleared-environment-repository-path", `env -i PATH=${cwd} sh -c 'wget --no-config -O - https://example.test/file'`], ["cleared-exec-empty-path", `exec -c env PATH= sh -c 'wget --no-config -O - https://example.test/file'`], ["cleared-exec-relative-path", `exec -c env PATH=. sh -c 'wget --no-config -O - https://example.test/file'`], ["cleared-exec-repository-path", `exec -c env PATH=${cwd} sh -c 'wget --no-config -O - https://example.test/file'`], ] as const) { const nativeChildBash = await dispatchBash(`${name}-child`, { agent_id: `agent-hook-native-${name}` }, command); assert.equal(nativeChildBash.outputJson?.decision, "block", name); assert.match(String(nativeChildBash.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); const mainRootBash = await dispatchBash(`${name}-main`, { agent_id: leaderThreadId }, command); assert.equal(mainRootBash.outputJson?.decision, "block", name); assert.match(String(mainRootBash.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, name); } const genericAgentId = await dispatchWrite({ agent_id: "agent-hook-native-default", agent_type: "default" }); assert.equal(genericAgentId.outputJson?.decision, "block"); assert.match(String(genericAgentId.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(genericAgentId.outputJson?.reason ?? ""), /Main-root|PROVENANCE_DENIED/); const unofficialCamelCaseAgentId = await dispatchWrite({ agentId: "agent-hook-native-camel-case", agent_role: "executor" }); assert.equal(unofficialCamelCaseAgentId.outputJson?.decision, "block"); assert.match(String(unofficialCamelCaseAgentId.outputJson?.reason ?? ""), /PROVENANCE_DENIED/); assert.doesNotMatch(String(unofficialCamelCaseAgentId.outputJson?.reason ?? ""), /Main-root/); const leaderValuedCamelCaseAgentId = await dispatchBash( "leader-valued-camel-case-agent-id", { agentId: leaderThreadId }, "chmod --reference=.omx/state/session.json .omx/state/reference-copy", ); assert.equal(leaderValuedCamelCaseAgentId.outputJson?.decision, "block"); assert.doesNotMatch(String(leaderValuedCamelCaseAgentId.outputJson?.reason ?? ""), /Main-root/); const agentTypeOnly = await dispatchWrite({ agent_type: "writer" }); assert.equal(agentTypeOnly.outputJson?.decision, "block"); assert.match(String(agentTypeOnly.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(agentTypeOnly.outputJson?.reason ?? ""), /Main-root|PROVENANCE_DENIED/); const leaderAgentId = await dispatchWrite({ agent_id: leaderThreadId, agent_role: "executor" }); assert.equal(leaderAgentId.outputJson?.decision, "block"); assert.match(String(leaderAgentId.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); assert.doesNotMatch(String(leaderAgentId.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); await writeJson(join(stateDir, "session.json"), { session_id: "sess-conductor-hook-native-agent-id-foreign", native_session_id: "thread-conductor-hook-native-agent-id-foreign-leader", }); const foreignRootPointer = await dispatchWrite({ agent_id: "agent-hook-native-foreign-root", agent_role: "writer" }); assert.equal(foreignRootPointer.outputJson?.decision, "block"); assert.match(String(foreignRootPointer.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (originalTeamWorker === undefined) delete process.env.OMX_TEAM_WORKER; else process.env.OMX_TEAM_WORKER = originalTeamWorker; if (originalInternalTeamWorker === undefined) delete process.env.OMX_TEAM_INTERNAL_WORKER; else process.env.OMX_TEAM_INTERNAL_WORKER = originalInternalTeamWorker; if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; if (originalOmxSessionId === undefined) delete process.env.OMX_SESSION_ID; else process.env.OMX_SESSION_ID = originalOmxSessionId; if (originalGjcSessionId === undefined) delete process.env.GJC_SESSION_ID; else process.env.GJC_SESSION_ID = originalGjcSessionId; await rm(cwd, { recursive: true, force: true }); } })); it("keeps active Ralph starting phase behind the PreToolUse write guard", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-starting-pretool-")); const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralph-starting-pretool"; const leaderThreadId = "thread-ralph-starting-leader"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } } } }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "starting"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: sessionId, }); const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, agent_id: "agent-ralph-starting-child", tool_name: "Write", tool_input: { file_path: "src/starting-bypass.ts", content: "owned\n" }, }, { cwd }); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; if (previousOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = previousOmxStateRoot; if (previousOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = previousOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation for tracked typed native subagent writes without thread_spawn source", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-tracked-subagent-no-source-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-tracked-subagent-no-source"; const leaderThreadId = "thread-conductor-leader-no-source"; const childThreadId = "thread-conductor-child-no-source"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "executor", tool_name: "apply_patch", tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /Main-root/); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("blocks conductor writes when corrupt tracker state labels the leader as a subagent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-corrupt-leader-subagent-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-corrupt-leader-subagent"; const leaderThreadId = "thread-conductor-corrupt-leader"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_role: "executor", tool_name: "apply_patch", tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: executing\)/); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation when a corrupt kind:subagent tracker is the only apparent leader source (#3117 P2)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3117-corrupt-leader-no-lead-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3117-corrupt-leader-no-lead"; const leaderThreadId = "thread-3117-corrupt-leader-no-lead-leader"; const childThreadId = "thread-3117-corrupt-leader-no-lead-child"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); // A native session id is a shared routing alias, not a per-actor leader identity. await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "collaboration-child" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child" }, }, }, }, }); // Without a tracker-recorded leader identity, the untyped caller must fail closed. const untypedLeader = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(untypedLeader.outputJson?.decision, "block"); assert.match(String(untypedLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); // A genuine non-leader child is identified separately but still lacks write authority. const untypedChild = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(untypedChild.outputJson?.decision, "block"); assert.match(String(untypedChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(untypedChild.outputJson?.reason ?? ""), /Main-root/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for untyped provenance when no authoritative leader anchor exists (#3117 P2)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3117-no-leader-anchor-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3117-no-leader-anchor"; const leaderThreadId = "thread-3117-no-leader-anchor-leader"; const childThreadId = "thread-3117-no-leader-anchor-child"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); // No native_session_id / owner ids and no tracker leader_thread_id: the leader is // unidentifiable, so untyped provenance must not be inferred from kind:"subagent". await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "collaboration-child" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child" }, }, }, }, }); for (const threadId of [leaderThreadId, childThreadId]) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block", threadId); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /Main-root/); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not borrow leader anchors from a foreign root session.json when evaluating another session (#3117 P3)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3117-cross-session-anchor-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const foreignSessionId = "sess-3117-p3-foreign-A"; const foreignLeaderThreadId = "thread-3117-p3-A-leader"; const sessionId = "sess-3117-p3-checked-B"; const leaderThreadId = "thread-3117-p3-B-leader"; const childThreadId = "thread-3117-p3-B-child"; const sessionPath = join(stateDir, "session.json"); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); // Evaluated session B has a corrupt tracker: no leader_thread_id, leader mislabeled kind:"subagent". await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "collaboration-child" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child" }, }, }, }, }); const dispatchThread = async (threadId: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); // Root session.json owns a DIFFERENT session A: its native/owner ids must not // anchor session B, so B's mislabeled leader stays blocked (fail closed). await writeJson(sessionPath, { session_id: foreignSessionId, native_session_id: foreignLeaderThreadId }); const foreignLeader = await dispatchThread(leaderThreadId); assert.equal(foreignLeader.outputJson?.decision, "block"); assert.match(String(foreignLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(foreignLeader.outputJson?.reason ?? ""), /Main-root/); const foreignChild = await dispatchThread(childThreadId); assert.equal(foreignChild.outputJson?.decision, "block"); assert.match(String(foreignChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(foreignChild.outputJson?.reason ?? ""), /Main-root/); // A matching native_session_id remains a routing alias, not a leader identity. await writeJson(sessionPath, { session_id: sessionId, native_session_id: leaderThreadId }); const ownedLeader = await dispatchThread(leaderThreadId); assert.equal(ownedLeader.outputJson?.decision, "block"); assert.match(String(ownedLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const ownedChild = await dispatchThread(childThreadId); assert.equal(ownedChild.outputJson?.decision, "block"); assert.match(String(ownedChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(ownedChild.outputJson?.reason ?? ""), /Main-root/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when session.json carries only owner session ids without a leader thread anchor (#3117 P4)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3117-owner-only-anchor-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3117-p4-owner-only"; const leaderThreadId = "thread-3117-p4-leader"; const childThreadId = "thread-3117-p4-child"; const untrackedThreadId = "thread-3117-p4-untracked"; const sessionPath = join(stateDir, "session.json"); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); // Corrupt tracker for the evaluated session: no leader_thread_id, leader mislabeled kind:"subagent". await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "collaboration-child" }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child" }, }, }, }, }); const dispatchThread = async (threadId: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); // session.json maps to the evaluated session via owner ids but has NO // native_session_id: owner session ids are not leader thread anchors, so trust // must fail closed rather than treat their presence as an anchor (#3117 P4). await writeJson(sessionPath, { session_id: sessionId, owner_omx_session_id: "owner-omx-3117-p4", owner_codex_session_id: "owner-codex-3117-p4", }); const ownerOnlyLeader = await dispatchThread(leaderThreadId); assert.equal(ownerOnlyLeader.outputJson?.decision, "block"); assert.match(String(ownerOnlyLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(ownerOnlyLeader.outputJson?.reason ?? ""), /Main-root/); // Control: an untracked thread also stays blocked under the same state. const ownerOnlyUntracked = await dispatchThread(untrackedThreadId); assert.equal(ownerOnlyUntracked.outputJson?.decision, "block"); // A matching native_session_id remains insufficient without a leader thread anchor. await writeJson(sessionPath, { session_id: sessionId, native_session_id: leaderThreadId }); const anchoredLeader = await dispatchThread(leaderThreadId); assert.equal(anchoredLeader.outputJson?.decision, "block"); assert.match(String(anchoredLeader.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); const anchoredChild = await dispatchThread(childThreadId); assert.equal(anchoredChild.outputJson?.decision, "block"); assert.match(String(anchoredChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(anchoredChild.outputJson?.reason ?? ""), /Main-root/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks conductor writes when thread_spawn provenance is attached to the leader thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-thread-spawn-leader-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-thread-spawn-leader"; const leaderThreadId = "thread-conductor-thread-spawn-leader"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_role: "executor", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId, }, }, }, tool_name: "apply_patch", tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: executing\)/); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot; await rm(cwd, { recursive: true, force: true }); } }); it("denies Main-root conductor apply_patch from the session leader thread during active Ralph (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-root-denial-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-root-denial"; const leaderThreadId = "thread-3116-root-denial-leader"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ralph phase: executing\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation for a tracked generic collaboration.spawn_agent child during active Ralph (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-trusted-child-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-trusted-child"; const leaderThreadId = "thread-3116-trusted-child-leader"; const childThreadId = "thread-3116-trusted-child-worker"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, leader_thread_id: leaderThreadId }, }, }, }, }); // Generic native-child provenance identifies the child but does not grant write authority. const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, agent_role: "collaboration-child", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId, depth: 1, agent_nickname: "Helper", }, }, }, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /Main-root/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation for tracked and runtime-proven collaboration descendants (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-descendant-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-descendant"; const leaderThreadId = "thread-3116-descendant-leader"; const childThreadId = "thread-3116-descendant-child"; const grandchildThreadId = "thread-3116-descendant-grandchild"; const greatGrandchildThreadId = "thread-3116-descendant-great-grandchild"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, leader_thread_id: leaderThreadId }, [grandchildThreadId]: { thread_id: grandchildThreadId, kind: "subagent", mode: "collaboration-child", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, leader_thread_id: childThreadId }, }, }, }, }); // A tracked descendant has same-session provenance but no product-write authority. const trackedDescendant = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: grandchildThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(trackedDescendant.outputJson?.decision, "block"); assert.match(String(trackedDescendant.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(trackedDescendant.outputJson?.reason ?? ""), /Main-root/); // A runtime-proven descendant has same-session provenance but no product-write authority. const chainedDescendant = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: greatGrandchildThreadId, agent_role: "collaboration-child", source: { subagent: { thread_spawn: { parent_thread_id: grandchildThreadId, depth: 3, }, }, }, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(chainedDescendant.outputJson?.decision, "block"); assert.match(String(chainedDescendant.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(chainedDescendant.outputJson?.reason ?? ""), /Main-root/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resolves session-pinned Ralph state so child provenance receives owner confirmation while the leader stays blocked (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-pinned-state-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-pinned-state"; const leaderThreadId = "thread-3116-pinned-state-leader"; const childThreadId = "thread-3116-pinned-state-child"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); // Native session id differs from the canonical session id: the guard must resolve it. await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); const ralphStatePath = join(stateDir, "sessions", sessionId, "ralph-state.json"); await writeJson(ralphStatePath, { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, [childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "collaboration-child", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, leader_thread_id: leaderThreadId }, }, }, }, }); // A payload carrying the native session id maps to the canonical session and remains a child. const childEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: leaderThreadId, thread_id: childThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(childEdit.outputJson?.decision, "block"); assert.match(String(childEdit.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(childEdit.outputJson?.reason ?? ""), /Main-root/); const leaderEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: leaderThreadId, thread_id: leaderThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(leaderEdit.outputJson?.decision, "block"); assert.match(String(leaderEdit.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ralph phase: executing\)/); // The active Ralph starting guard remains in force; only terminal state can // release the leader from the active workflow write boundary. await writeJson(ralphStatePath, { active: true, mode: "ralph", current_phase: "starting", session_id: sessionId, }); const leaderEditAfterStarting = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: leaderThreadId, thread_id: leaderThreadId, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(leaderEditAfterStarting.outputJson?.decision, "block"); assert.match(String(leaderEditAfterStarting.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ralph phase: starting\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires owner confirmation for a runtime-proven native collaboration child during active Ralph (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-collab-surface-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-collab-surface"; const leaderThreadId = "thread-3116-collab-surface-leader"; const childThreadId = "thread-3116-collab-surface-child"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); // Child SessionStart has not been recorded yet: only the leader is tracked. await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); // No typed agent_role: raw runtime child provenance still does not grant write authority. const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: childThreadId, source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId, depth: 1, agent_nickname: "Implementer", }, }, }, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies spoofed or untrusted collaboration provenance while the main root stays protected during active Ralph (#3116)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3116-spoof-denial-")); process.env.OMX_ROOT = cwd; try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3116-spoof-denial"; const leaderThreadId = "thread-3116-spoof-denial-leader"; const nowIso = new Date().toISOString(); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: nowIso, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 }, }, }, }, }); // (a) Explicit non-anchor thread identity with an untrusted declared parent. const orphanParent = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-3116-spoof-orphan", agent_role: "collaboration-child", source: { subagent: { thread_spawn: { parent_thread_id: "thread-3116-spoof-outsider" }, }, }, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(orphanParent.outputJson?.decision, "block"); assert.match(String(orphanParent.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(orphanParent.outputJson?.reason ?? ""), /Main-root/); // (b) Explicit non-anchor thread identity with no provenance at all. const noProvenance = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-3116-spoof-bare", agent_role: "collaboration-child", tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(noProvenance.outputJson?.decision, "block"); assert.match(String(noProvenance.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); assert.doesNotMatch(String(noProvenance.outputJson?.reason ?? ""), /Main-root/); // (c) The positive leader anchor retains Main-root authority despite self-spawn provenance. const leaderSelfSpawn = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_role: "collaboration-child", source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId }, }, }, tool_name: "apply_patch", tool_input: { file_path: "src/feature.ts" }, }, { cwd }, ); assert.equal(leaderSelfSpawn.outputJson?.decision, "block"); assert.match(String(leaderSelfSpawn.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ralph phase: executing\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root ralph conductor source and planning artifact writes while allowing .omx workflow state writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralph-conductor-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ralph-conductor-write", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-ralph-conductor-write", threads: { "thread-ralph-conductor-write": { thread_id: "thread-ralph-conductor-write", kind: "leader" } } } } }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const blocked = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", agent_id: "thread-ralph-conductor-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(blocked.outputJson?.decision, "block"); assert.match(String(blocked.outputJson?.reason ?? ""), /ralph phase: executing/); for (const [toolName, filePath] of [ ["Write", ".omx/plans/conductor-owned-plan.md"], ["Edit", ".omx/specs/conductor-owned-spec.md"], ] as const) { const planningArtifactWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", agent_id: "thread-ralph-conductor-write", tool_name: toolName, tool_input: { file_path: filePath }, }, { cwd }, ); assert.equal(planningArtifactWrite.outputJson?.decision, "block", `${toolName} ${filePath}`); assert.match(String(planningArtifactWrite.outputJson?.reason ?? ""), /plan\/code writes are blocked/); } const allowed = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", agent_id: "thread-ralph-conductor-write", tool_name: "Write", tool_input: { file_path: ".omx/state/sessions/sess-ralph-conductor-write/ralph-state.json" }, }, { cwd }, ); assert.equal(allowed.outputJson?.decision, "block"); const protectedRawState = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", agent_id: "thread-ralph-conductor-write", tool_name: "Write", tool_input: { file_path: ".omx/state/sessions/sess-ralph-conductor-write/autopilot-state.json" }, }, { cwd }, ); assert.equal(protectedRawState.outputJson?.decision, "block"); const safeTransport = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", agent_id: "thread-ralph-conductor-write", tool_name: "mcp__omx_state__state_write", tool_input: { mode: "ralph", current_phase: "executing", active: true, session_id: sessionId, workingDirectory: cwd, }, }, { cwd }, ); assert.equal(safeTransport.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks dynamic ultragoal steering cleanup through nested shell loops", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-steer-cleanup-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ultragoal-steer-cleanup"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ultragoal-steer-cleanup", }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const result = await withTrustedWorkspaceOmxCli( cwd, (_omxCommand, trustedPath) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ultragoal-steer-cleanup", tool_name: "Bash", tool_input: { command: `PATH="${trustedPath}" bash -c 'for goal_id in G001-atomized G002-atomized; do omx ultragoal steer --kind mark_blocked_superseded --target-goal-id "$goal_id" --evidence ".omx/ultragoal cleanup supersedes atomized pseudo-goals." --rationale "Structured steering cleanup keeps durable Ultragoal metadata auditable." --json; done'`, }, }, { cwd }, ), "assignment", ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Bash nested shell execution is dynamic and cannot be validated/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks unsafe dynamic nested shell writes in conductor mode", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-dynamic-shell-write-block-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-dynamic-shell-write-block"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-dynamic-shell-write-block", tool_name: "Bash", tool_input: { command: `bash -lc 'cp "$SOURCE_FILE" src/runtime.ts'` }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Bash nested shell execution is dynamic and cannot be validated/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-bash-mutations-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-bash-mutations"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeFile(join(stateDir, "conductor-ledger.json"), "{}\n", "utf-8"); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-conductor-bash-mutations" }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-conductor-bash-mutations", threads: { "thread-conductor-bash-mutations": { thread_id: "thread-conductor-bash-mutations", kind: "leader" } }, }, }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(cwd, "a"), "finite metadata source\n", "utf-8"); await writeFile(join(cwd, "src", "source.ts"), "export {};\n", "utf-8"); const blockedCommands = [ "mv src/old.ts src/new.ts", "cp package.json src/package-copy.json", "touch src/generated.ts", "mkdir -p src/generated", "rm -f src/generated.ts", "chmod 600 src/runtime.ts", "sudo -n cp README.md src/readme-copy.md", "env cp package.json src/package-copy.json", "exec cp package.json src/package-copy.json", "env FOO=1 mv src/a.ts src/b.ts", "cp -t src .omx/state/conductor-ledger.json", "mv --target-directory src .omx/state/foo", "install -t src .omx/state/foo", "touch .omx/plans/conductor-owned-plan.md", "cat <<'EOF' > .omx/specs/conductor-owned-spec.md\n# Spec\nEOF", "python3 <<'PY'\nfrom pathlib import Path\nPath('src/x.ts').write_text('x')\nPY", "python3 - <<'PY'\nimport shutil\nshutil.copyfile('a', 'src/foo')\nPY", "bash -lc \"mv src/old.ts src/new.ts\"", "sh -c 'cp package.json src/package-copy.json'", "bash -lc \"sed -i 's/old/new/' src/runtime.ts\"", "bash -lc \"perl -pi -e 's/old/new/' src/runtime.ts\"", "printf ok; cp package.json src/package-copy.json", "(cp .omx/state/foo src/foo)", "{ cp .omx/state/foo src/foo; }", "curl -q -o src/downloaded.json https://example.invalid/data.json", "curl -q -O https://example.invalid/data.json", "wget -O src/downloaded.json https://example.invalid/data.json", "wget -o src/wget.log https://example.invalid/data.json", "cd /tmp && cp .omx/state/foo src/foo", ]; for (const command of blockedCommands) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-bash-mutations", agent_id: "thread-conductor-bash-mutations", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); assert.match(String((result.outputJson as { reason?: string } | null)?.reason ?? ""), /Bash (?:.* mutation target|.*write target) .*not workflow state\/ledger\/mailbox\/handoff metadata|Bash redirect target is not a static workflow metadata leaf|target /); } const allowedCommands = [ "touch .omx/state/conductor-ledger.json", "mkdir -p .omx/handoffs/run-1", "cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/conductor-ledger.json", "mv .omx/handoffs/run-1/conductor-ledger.json .omx/handoffs/run-1/ledger.json", "env cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/env-ledger.json", "exec cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/exec-ledger.json", "cp src/source.ts .omx/state/source-copy.ts", "cat <<'EOF' > .omx/state/conductor-heredoc.json\n{}\nEOF", "bash --noprofile --norc -lc \"printf safe\"", "sh -c 'printf safe'", "curl -q https://example.invalid/data.json", ]; for (const command of allowedCommands) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-bash-mutations", agent_id: "thread-conductor-bash-mutations", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal(result.outputJson, null, command); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("trusts exact inherited non-repository PATH executables while keeping explicit resolution mutations fail-closed (#3370)", async () => { if (process.platform === "win32") return; const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-inherited-path-")); const hostBinDir = await mkdtemp(join(tmpdir(), "omx-native-hook-host-bin-")); const emptyPrefixDir = await mkdtemp(join(tmpdir(), "omx-native-hook-empty-prefix-")); const previousPath = process.env.PATH; const previousPathext = process.env.PATHEXT; try { const bashPath = ["/opt/homebrew/bin/bash", "/usr/local/bin/bash", "/bin/bash", "/usr/bin/bash"].find((candidate) => existsSync(candidate)); const shPath = ["/bin/sh", "/usr/bin/sh"].find((candidate) => existsSync(candidate)); const perlPath = ["/opt/homebrew/bin/perl", "/usr/local/bin/perl", "/usr/bin/perl", "/bin/perl"].find((candidate) => existsSync(candidate)); const gitPath = ["/opt/homebrew/bin/git", "/usr/local/bin/git", "/usr/bin/git", "/bin/git"].find((candidate) => existsSync(candidate)); const catPath = ["/bin/cat", "/usr/bin/cat"].find((candidate) => existsSync(candidate)); assert.ok(bashPath, "host bash executable is required"); assert.ok(shPath, "host sh executable is required"); assert.ok(perlPath, "host perl executable is required"); assert.ok(gitPath, "host git executable is required"); assert.ok(catPath, "host cat executable is required"); await symlink(bashPath, join(hostBinDir, "bash")); await symlink(bashPath, join(hostBinDir, "BASH")); await symlink(shPath, join(hostBinDir, "ſh")); await symlink(perlPath, join(hostBinDir, "perl")); await symlink(gitPath, join(hostBinDir, "git")); await symlink(catPath, join(hostBinDir, "cat")); await symlink(bashPath, join(hostBinDir, "omx.exe")); await symlink(bashPath, join(hostBinDir, "gjc.cmd")); const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-inherited-path"; const threadId = "thread-conductor-inherited-path"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); execFileSync("git", ["init", "-q"], { cwd }); await writeFile(join(stateDir, "conductor.log"), "old\n", "utf-8"); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: threadId }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" } }, }, }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); process.env.PATH = `${hostBinDir}:/usr/bin:/bin`; const dispatch = (command: string) => dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, agent_id: threadId, tool_name: "Bash", tool_input: { command }, }, { cwd }, ); const hardenedGitStatus = "GIT_ATTR_NOSYSTEM=1 GIT_CONFIG_COUNT=0 GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_SYSTEM=/dev/null GIT_EDITOR= GIT_EXTERNAL_DIFF= GIT_PAGER= GIT_SEQUENCE_EDITOR= PAGER= git --no-pager --no-optional-locks -c core.fsmonitor=false -c core.untrackedCache=false -c pager.status=false status --short --branch --untracked-files=normal --ignore-submodules=all --no-renames"; for (const command of [ "bash --noprofile --norc -lc \"printf safe\"", "perl -pi -e 's/old/new/' .omx/state/conductor.log", hardenedGitStatus, `PATH=${JSON.stringify(`${emptyPrefixDir}:${process.env.PATH}`)} cat .omx/state/conductor.log`, ]) { assert.equal((await dispatch(command)).outputJson, null, command); } const inheritedPath = process.env.PATH; const metadataPerl = "perl -pi -e 's/old/new/' .omx/state/conductor.log"; const metadataCat = "cat .omx/state/conductor.log"; const inversePathCat = `ALT=${JSON.stringify(inheritedPath)}; declare -n PATH=ALT; ${metadataCat}`; const lateInversePathCat = `declare -n PATH=ALT; ALT=${JSON.stringify(inheritedPath)}; ${metadataCat}`; const functionInversePathCat = `f() { local ALT=${JSON.stringify(inheritedPath)}; local -n PATH=ALT; ${metadataCat}; }; f`; const inversePathextCat = `ALT=.EVIL; declare -n PATHEXT=ALT; ${metadataCat}`; for (const command of [ `PATH=${JSON.stringify(hostBinDir)} bash --noprofile --norc -lc \"printf safe\"`, `PATH=${JSON.stringify(inheritedPath)} bash --noprofile --norc -lc \"printf safe\"`, `PATH=${JSON.stringify(inheritedPath)}; bash --noprofile --norc -lc \"printf safe\"`, `export PATH=${JSON.stringify(inheritedPath)}; bash --noprofile --norc -lc \"printf safe\"`, `printf -v PATH '%s' ${JSON.stringify(inheritedPath)}; bash --noprofile --norc -lc \"printf safe\"`, "PATHEXT=.EVIL bash --noprofile --norc -lc \"printf safe\"", "BASH --noprofile --norc -lc \"printf safe\"", "ſh -c \"printf safe\"", "omx.exe --help", "gjc.cmd --help", `PATH=${JSON.stringify(`${cwd}/missing:${inheritedPath}`)} cat .omx/state/conductor.log`, `f() { PATH=${JSON.stringify(inheritedPath)}; }; f; ${metadataPerl}`, `f() { export PATH; }; f; ${metadataPerl}`, `f() { local PATH=${JSON.stringify(inheritedPath)}; ${metadataPerl}; }; f`, `f() { declare PATH=${JSON.stringify(inheritedPath)}; ${metadataPerl}; }; f`, `f() { local PATHEXT=.EVIL; ${metadataPerl}; }; f`, `f() { local PATH=${JSON.stringify(inheritedPath)}; printf -v PATH '%s' ${JSON.stringify(inheritedPath)}; ${metadataPerl}; }; f`, `f() { local PATH=${JSON.stringify(inheritedPath)}; local -n path_ref=PATH; printf -v path_ref '%s' ${JSON.stringify(inheritedPath)}; ${metadataPerl}; }; f`, `bash --noprofile --norc -lc ${JSON.stringify(inversePathCat)}`, `env bash --noprofile --norc -lc ${JSON.stringify(inversePathCat)}`, `exec bash --noprofile --norc -lc ${JSON.stringify(inversePathCat)}`, `bash --noprofile --norc -lc ${JSON.stringify(lateInversePathCat)}`, `bash --noprofile --norc -lc ${JSON.stringify(functionInversePathCat)}`, `bash --noprofile --norc -lc ${JSON.stringify(inversePathextCat)}`, `ALT=${JSON.stringify(inheritedPath)}; declare -n PATH=ALT; ${metadataCat}`, `declare -n PATH=ALT; ALT=${JSON.stringify(inheritedPath)}; ${metadataCat}`, `f() { local ALT=${JSON.stringify(inheritedPath)}; local -n PATH=ALT; ${metadataCat}; }; f`, `declare -n PATHEXT=ALT; ALT=.EVIL; ${metadataCat}`, `f() { local ALT=.EVIL; local -n PATHEXT=ALT; ${metadataCat}; }; f`, `ALT=${JSON.stringify(inheritedPath)}; declare -n PATH=ALT; ${metadataPerl}`, `declare -n PATH=ALT; ALT=${JSON.stringify(inheritedPath)}; ${metadataPerl}`, `declare -n PATHEXT=ALT; ALT=.EVIL; ${metadataPerl}`, `f() { local ALT=${JSON.stringify(inheritedPath)}; local -n PATH=ALT; ${metadataPerl}; }; f`, `f() { local -n PATH=ALT; local ALT=${JSON.stringify(inheritedPath)}; ${metadataPerl}; }; f`, `f() { local -n PATHEXT=ALT; local ALT=.EVIL; ${metadataPerl}; }; f`, `f() { local -n ALT=PATH; ${metadataPerl}; }; f`, `f() { local ALT=.EVIL; local -n PATHEXT=ALT; ${metadataPerl}; }; f`, `f() { local -n ALT=PATHEXT; ${metadataPerl}; }; f`, `f() { ${metadataPerl}; }; PATH=${JSON.stringify(inheritedPath)} f`, `f() { ${metadataPerl}; }; PATHEXT=.EVIL f`, ]) { const result = await dispatch(command); assert.equal(result.outputJson?.decision, "block", command); } const repoBinDir = join(cwd, "repo-bin"); await mkdir(repoBinDir, { recursive: true }); await symlink(bashPath, join(repoBinDir, "bash")); process.env.PATH = `${repoBinDir}:${inheritedPath}`; const repositoryPathCandidate = await dispatch("bash --noprofile --norc -lc \"printf safe\""); assert.equal(repositoryPathCandidate.outputJson?.decision, "block"); } finally { if (previousPath === undefined) delete process.env.PATH; else process.env.PATH = previousPath; if (previousPathext === undefined) delete process.env.PATHEXT; else process.env.PATHEXT = previousPathext; await rm(emptyPrefixDir, { recursive: true, force: true }); await rm(hostBinDir, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("allows autopilot rework implementation writes while conductor phases stay guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-rework-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-rework-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "rework"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-rework-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root ralph conductor source writes while allowing .omx workflow state writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralph-conductor-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ralph-conductor-write", }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const blocked = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(blocked.outputJson?.decision, "block"); assert.match(String(blocked.outputJson?.reason ?? ""), /ralph phase: executing/); const allowed = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", tool_name: "Write", tool_input: { file_path: ".omx/state/sessions/sess-ralph-conductor-write/ralph-state.json" }, }, { cwd }, ); assert.equal(allowed.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks non-shell direct writes in Main-root conductor states", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-bash-mutations-")); const previousPath = process.env.PATH; const unsafeRuntimeEnvNames = [ "NODE_OPTIONS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ] as const; const previousRuntimeEnv = Object.fromEntries(unsafeRuntimeEnvNames.map((name) => [name, process.env[name]])); try { process.env.PATH = `${dirname(process.execPath)}:/usr/bin:/bin`; for (const name of unsafeRuntimeEnvNames) delete process.env[name]; const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-bash-mutations"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeFile(join(cwd, "a"), "finite metadata source\n", "utf-8"); await writeLiveNativeMappedSessionState( cwd, stateDir, sessionId, "native-conductor-bash-mutations", "thread-conductor-bash-mutations", ); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const blockedCommands = [ "node -e \"require('fs').appendFileSync('src/runtime.ts','x')\"", "python3 -c \"open('src/runtime.ts','a').write('x')\"", "curl -q -fsSL https://example.test/runtime.ts -o src/runtime.ts", "curl -q -fsSL -O https://example.test/runtime.ts --output-dir src", "curl -q -fsSL -LO https://example.test/runtime.ts --output-dir src", "curl -q -fsSL --remote-name --output-dir=src https://example.test/runtime.ts", "wget -O src/runtime.ts https://example.test/runtime.ts", "curl -q --output-dir src -O https://example.test/runtime.ts", "curl -q --create-dirs --output-dir src -o .omx/state/out https://example.test/runtime.ts", "wget -P src https://example.test/runtime.ts", "wget --directory-prefix=src https://example.test/runtime.ts", "git rm src/runtime.ts", "git clean -fd src", ]; for (const command of blockedCommands) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-bash-mutations", agent_id: "thread-conductor-bash-mutations", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); assert.match(String((result.outputJson as { reason?: string } | null)?.reason ?? ""), /Bash (?:node|python) write target .*not workflow state\/ledger\/mailbox\/handoff metadata|Bash (?:curl|wget) (?:output|mutation) target .*not workflow state\/ledger\/mailbox\/handoff metadata|Bash git worktree mutation is not workflow state\/ledger\/mailbox\/handoff metadata|target /); } for (const command of [ "python3 -c \"print('ok')\"", "python3 - <<'PY'\nimport shutil\nshutil.copyfile('a', '.omx/state/foo')\nPY", ]) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-bash-mutations", agent_id: "thread-conductor-bash-mutations", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); } const allowedCommands = [ "node -e \"console.log('ok')\"", "python3 -I -c \"print('ok')\"", "python3 -I - <<'PY'\nimport shutil\nshutil.copyfile('a', '.omx/state/foo')\nPY", ]; for (const command of allowedCommands) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-bash-mutations", agent_id: "thread-conductor-bash-mutations", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal(result.outputJson, null, command); } } finally { if (typeof previousPath === "string") process.env.PATH = previousPath; else delete process.env.PATH; for (const name of unsafeRuntimeEnvNames) { if (previousRuntimeEnv[name] === undefined) delete process.env[name]; else process.env[name] = previousRuntimeEnv[name]; } await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root team conductor writes from root team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-team-conductor-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-root-team-conductor-write"; const teamName = "root-team-conductor-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeLiveNativeMappedSessionState( cwd, stateDir, sessionId, "native-root-team-conductor-write", "thread-root-team-conductor-write", ); await writeSessionSkillActiveState(stateDir, sessionId, "team", "team-exec"); await writeJson(join(stateDir, "team-state.json"), { active: true, mode: "team", current_phase: "starting", team_name: teamName, session_id: sessionId, thread_id: "thread-root-team-conductor-write", }); await writeJson(join(stateDir, "team", teamName, "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-root-team-conductor-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /team phase: team-exec/); for (const [toolName, toolInput] of [ ["mcp__omx_state__state_write", { mode: "team", active: false, current_phase: "complete", session_id: sessionId, workingDirectory: cwd, }], ["Bash", { command: `omx state write --input '${JSON.stringify({ mode: "team", active: false, current_phase: "complete", session_id: sessionId, workingDirectory: cwd })}' --json`, }], ] as const) { const deactivation = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-root-team-conductor-write", tool_name: toolName, tool_input: toolInput, }, { cwd }); assert.equal(deactivation.outputJson?.decision, "block", toolName); assert.match(String(deactivation.outputJson?.reason ?? ""), /preserve the canonical active Conductor guard|remain bound to the active Conductor session/); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows autopilot rework implementation writes while conductor phases stay guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-rework-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-rework-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "rework"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-rework-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root ralph conductor source writes while allowing .omx workflow state writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralph-conductor-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-ralph-conductor-write", }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const blocked = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(blocked.outputJson?.decision, "block"); assert.match(String(blocked.outputJson?.reason ?? ""), /ralph phase: executing/); const nativeMappedCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-native-write-")); try { const nativeMappedStateDir = join(nativeMappedCwd, ".omx", "state"); const canonicalSessionId = "omx-canonical-ralph-conductor-write"; const nativeSessionId = "codex-native-ralph-conductor-write"; await writeNativeMappedSessionState( nativeMappedCwd, nativeMappedStateDir, canonicalSessionId, nativeSessionId, "thread-native-ralph-conductor-write", ); await writeSessionSkillActiveState(nativeMappedStateDir, canonicalSessionId, "ralph", "executing"); await writeJson(join(nativeMappedStateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: canonicalSessionId, }); const nativeMappedBlocked = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd: nativeMappedCwd, session_id: nativeSessionId, thread_id: "thread-native-ralph-conductor-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd: nativeMappedCwd }, ); assert.equal(nativeMappedBlocked.outputJson?.decision, "block"); assert.match(String(nativeMappedBlocked.outputJson?.reason ?? ""), /ralph phase: executing/); } finally { await rm(nativeMappedCwd, { recursive: true, force: true }); } const allowed = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-ralph-conductor-write", tool_name: "Write", tool_input: { file_path: ".omx/state/sessions/sess-ralph-conductor-write/ralph-state.json" }, }, { cwd }, ); assert.equal(allowed.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-bash-mutations-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-bash-mutations"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeFile(join(cwd, "README.md"), "read-only source\n", "utf-8"); await mkdir(join(stateDir, "inbox"), { recursive: true }); await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(stateDir, "payload"), "metadata payload\n", "utf-8"); await writeFile(join(cwd, "src", "runtime.ts"), "export {};\n", "utf-8"); await writeFile(join(cwd, "src", "source.ts"), "export {};\n", "utf-8"); await symlink(join(cwd, "src", "runtime.ts"), join(stateDir, "inbox", "payload")); await symlink(join(cwd, "src", "runtime.ts"), join(stateDir, "curl-glob-1.log")); await symlink(join(cwd, "src", "dangling-target.ts"), join(stateDir, "inbox", "dangling")); await writeFile(join(stateDir, "conductor-ledger.json"), "{}\n", "utf-8"); await writeFile(join(stateDir, "line-one.json"), "{}\n", "utf-8"); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-conductor-bash-mutations" }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-conductor-bash-mutations", threads: { "thread-conductor-bash-mutations": { thread_id: "thread-conductor-bash-mutations", kind: "leader" } }, }, }, }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const blockedCommands = [ "mv src/old.ts src/new.ts", "cp package.json src/package-copy.json", "touch src/generated.ts", "mkdir -p src/generated", "rm -f src/generated.ts", "chmod 600 src/runtime.ts", "sudo -n cp README.md src/readme-copy.md", "env cp package.json src/package-copy.json", "exec cp package.json src/package-copy.json", "env FOO=1 mv src/a.ts src/b.ts", "git reset --hard > .omx/state/reset.log", "touch .omx/state/inbox/dangling", "wget --no-config --no-hsts -P .omx/state/inbox https://example.test/dangling", "npm install > .omx/state/install.log", "printf ok; cp package.json src/package-copy.json", "printf ok && mv src/a.ts src/b.ts", "printf ok\ncp package.json src/package-copy.json", "printf ok > .omx/state/log\nmv src/a src/b", "cp package.json --target-directory src/generated", "cp .omx/state/payload .omx/state/inbox", "ln src/source.ts -t .omx/handoffs/run-1", "curl -q --output-dir .omx/state/inbox -O https://example.test/payload", "wget --no-config --no-hsts -P .omx/state/inbox https://example.test/payload", "curl -q --output-dir .omx/state/inbox -O https://example.test/payload ftp.example.test/payload", "wget --no-config --no-hsts -P .omx/state/inbox https://example.test/payload ftp.example.test/payload", "curl -q -o .omx/state/curl-glob-#1.log 'https://example.test/file[1-2]'", "mv src/a.ts --target-directory=src/generated", "mv src/a.ts --target-directory=.omx/state", "install package.json -t src/generated", "install -d .omx/state src/generated", "ln package.json -t src/generated", "cp package.json --target-directory", "cp package.json --target-directory=", "mv src/a.ts --target-directory", "install --target-directory= .omx/state/conductor-ledger.json", "ln .omx/state/conductor-ledger.json -t --", "if true; then mv src/a.ts src/b.ts; fi", "(cp package.json src/package-copy.json)", "bash -lc \"mv src/old.ts src/new.ts\"", "sh -c 'cp package.json src/package-copy.json'", "echo $(cp package.json src/package-copy.json)", "echo `mv src/old.ts src/new.ts`", "bash -lc \"sed -i 's/old/new/' src/runtime.ts\"", "bash -lc \"perl -pi -e 's/old/new/' src/runtime.ts\"", "sed -Ei 's/old/new/' src/runtime.ts", "do_src_write() { cp package.json src/package-copy.json; }; do_src_write", "do_src_write() ( mv src/old.ts src/new.ts ); time do_src_write", "cat <(cp package.json src/package-copy.json)", "cat >(mv src/old.ts src/new.ts)", "cat > .omx/state/conductor.log </); } const allowedCommands = [ "touch .omx/state/conductor-ledger.json", "cp .omx/state/payload .omx/handoffs/run-1/payload", "mkdir -p .omx/handoffs/run-1", "cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/conductor-ledger.json", "mv .omx/handoffs/run-1/conductor-ledger.json .omx/handoffs/run-1/ledger.json", "env cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/env-ledger.json", "exec cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/exec-ledger.json", "cat <<'EOF' > .omx/state/conductor-heredoc.json\n{}\nEOF", "printf safe > .omx/state/conductor.log", "printf one > .omx/state/one.log\nprintf two > .omx/handoffs/run-1/two.log", "touch .omx/state/line-one.json\ncp .omx/state/line-one.json .omx/handoffs/run-1/line-two.json", "bash --noprofile --norc -lc \"printf safe\"", "sh -c 'printf safe'", "cp .omx/state/conductor-ledger.json --target-directory .omx/handoffs/run-1", "mv .omx/state/conductor-ledger.json --target-directory=.omx/handoffs/run-1", "install .omx/state/conductor-ledger.json -t .omx/handoffs/run-1", "ln .omx/state/conductor-ledger.json -t .omx/handoffs/run-1", "if true; then cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/if-ledger.json; fi", "(cp .omx/state/conductor-ledger.json .omx/handoffs/run-1/subshell-ledger.json)", "sed -n '1,20p' src/runtime.ts", "perl -ne 'print' src/runtime.ts", "sed -i 's/old/new/' .omx/state/conductor-ledger.json", "perl -pi -e 's/old/new/' .omx/state/conductor-ledger.json", "sed -Ei 's/old/new/' .omx/state/conductor-ledger.json", "cp src/source.ts .omx/state/source-copy.ts", "install src/source.ts -t .omx/state", "mkdir -p .omx/state .omx/handoffs/run-1", "rsync README.md .omx/state/readme.md", "xargs env printf safe { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-sed-perl-metadata-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-conductor-sed-perl-metadata"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: "thread-conductor-sed-perl-metadata", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "thread-conductor-sed-perl-metadata", threads: { "thread-conductor-sed-perl-metadata": { thread_id: "thread-conductor-sed-perl-metadata", kind: "leader" } } } } }); await writeSessionSkillActiveState(stateDir, sessionId, "ralph", "executing"); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: sessionId, }); const allowedCommands = [ "sed -i 's/old/new/' .omx/state/conductor.log", "perl -pi -e 's/old/new/' .omx/state/conductor.log", "bash --noprofile --norc -lc \"sed -i 's/old/new/' .omx/state/conductor.log\"", "bash --noprofile --norc -lc \"perl -pi -e 's/old/new/' .omx/state/conductor.log\"", ]; for (const command of allowedCommands) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-sed-perl-metadata", agent_id: "thread-conductor-sed-perl-metadata", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal(result.outputJson, null, command); } const blockedCommands = [ "sed -i 's/old/new/' src/runtime.ts", "perl -pi -e 's/old/new/' src/runtime.ts", "bash -lc \"sed -i 's/old/new/' src/runtime.ts\"", "bash -lc \"perl -pi -e 's/old/new/' src/runtime.ts\"", ]; for (const command of blockedCommands) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-conductor-sed-perl-metadata", agent_id: "thread-conductor-sed-perl-metadata", tool_name: "Bash", tool_input: { command }, }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); assert.match(String((result.outputJson as { reason?: string } | null)?.reason ?? ""), /Bash .* target .*not workflow state\/ledger\/mailbox\/handoff metadata|target /); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows autopilot rework implementation writes while conductor phases stay guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-rework-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-rework-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "rework"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-rework-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state without team_name when no session is known", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-no-session-no-name-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "team-state.json"), { active: true, mode: "team", current_phase: "starting", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state without team_name for a foreign session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-foreign-no-name-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, mode: "team", current_phase: "starting", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from another thread's stale root team state when no scoped team state exists", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-team-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "stale-root-thread-team", session_id: "sess-current", thread_id: "thread-other", }); await writeJson(join(stateDir, "team", "stale-root-thread-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state with matching session but missing thread ownership", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-missing-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "root-missing-thread-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team", "root-missing-thread-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state when canonical phase is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-missing-phase-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await mkdir(join(stateDir, "team", "root-missing-phase-team"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "root-missing-phase-team", session_id: "sess-current", thread_id: "thread-current", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from session-scoped team state owned by another thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-scoped-team-other-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: true, current_phase: "starting", team_name: "scoped-other-thread-team", session_id: "sess-current", thread_id: "thread-other", }); await writeJson(join(stateDir, "team", "scoped-other-thread-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop from session-scoped team state owned by the current session and thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-scoped-team-current-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: true, current_phase: "starting", team_name: "scoped-current-team", session_id: "sess-current", thread_id: "thread-current", }); await writeJson(join(stateDir, "team", "scoped-current-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (scoped-current-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from another session's stale root team state when no scoped team state exists", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-team-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "stale-root-team", session_id: "sess-other", }); await writeJson(join(stateDir, "team", "stale-root-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from orphaned team mode state after cleanup removed canonical team artifacts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-orphaned-team-state-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "cleaned-team", session_id: "sess-current", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("prefers the current session team state over a stale root team fallback during Stop", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-current-session-team-preferred-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: true, current_phase: "starting", team_name: "current-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team", "current-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 1, transitions: [], updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "stale-root-team", session_id: "sess-other", }); await writeJson(join(stateDir, "team", "stale-root-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not fall back to active root team state when the current scoped team state is inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-inactive-scoped-team-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: false, current_phase: "complete", team_name: "scoped-finished-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "root-fallback-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team", "root-fallback-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("#3118 native role contract", () => { const unknownRoleReason = "Native typed-subagent dispatch denied: supplied agent_type/agent_role is unknown or not installed."; async function withIsolatedNativeRoleState( prefix: string, run: (cwd: string, stateDir: string) => Promise, ): Promise { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-3118-${prefix}-`)); const previousOmxRoot = process.env.OMX_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const previousStateRoot = process.env.OMX_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_TEAM_STATE_ROOT; delete process.env.OMX_STATE_ROOT; return await run(cwd, getBaseStateDir(cwd)); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; if (previousTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; if (previousStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = previousStateRoot; await rm(cwd, { recursive: true, force: true }); } } it("denies an unknown agent_type on collaboration.spawn_agent before dispatch (#3118)", async () => { await withIsolatedNativeRoleState("collaboration-unknown-role", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "collaboration.spawn_agent", tool_input: { agent_type: "not-an-installed-role" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.reason, unknownRoleReason); const additionalContext = String( (result.outputJson?.hookSpecificOutput as { additionalContext?: string } | undefined)?.additionalContext ?? "", ); assert.match(additionalContext, /When the surface reports role_routing_unavailable and adapted Ralplan authority is requested/i); assert.match(additionalContext, /omx ralplan preflight --json/); assert.match(additionalContext, /unsupported_documented_leader_proof/); assert.match(additionalContext, /Ordinary work remains under its own workflow gates/i); assert.doesNotMatch(additionalContext, /before Ralplan planning, state, HUD, runtime, or delegation work/i); }); }); it("denies unknown roles on exact-name spawn_agent and task before dispatch (#3118)", async () => { await withIsolatedNativeRoleState("exact-name-unknown-role", async (cwd) => { for (const [toolName, toolInput] of [ ["spawn_agent", { agent_type: "not-an-installed-role" }], ["task", { agent_role: "not-an-installed-role" }], ] as const) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: toolName, tool_input: toolInput, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block", toolName); assert.equal(result.outputJson?.reason, unknownRoleReason, toolName); } }); }); it("does not deny an installed architect on collaboration.spawn_agent (#3118)", async () => { await withIsolatedNativeRoleState("collaboration-installed-role", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "collaboration.spawn_agent", tool_input: { agent_role: "architect" }, }, { cwd }, ); assert.equal(result.outputJson, null); }); }); }); // --------------------------------------------------------------------------- // Triage layer integration tests // --------------------------------------------------------------------------- describe("codex native hook triage integration", () => { const priorCodexHome = process.env.CODEX_HOME; beforeEach(() => { resetTriageConfigCache(); }); afterEach(() => { if (typeof priorCodexHome === "string") process.env.CODEX_HOME = priorCodexHome; else delete process.env.CODEX_HOME; resetTriageConfigCache(); }); // ── Group 1: Keyword bypass (triage must NOT run) ──────────────────────── it("does not inject triage advisory for $ralplan keyword prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-keyword-ralplan-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-kw-ralplan-1", thread_id: "thread-triage-kw-1", turn_id: "turn-triage-kw-1", prompt: "$ralplan implement issue #1307", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-kw-ralplan-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate workflow state for native subagent prompts even when canonical id is the child session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-subagent-keyword-")); const boxedRoot = await mkdtemp(join(tmpdir(), "omx-native-subagent-keyword-boxed-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = boxedRoot; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const boxedStateDir = getBaseStateDir(cwd); await mkdir(boxedStateDir, { recursive: true }); await writeJson(join(boxedStateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "omx-parent-session": { session_id: "omx-parent-session", leader_thread_id: "parent-native-thread", updated_at: "2026-05-21T19:04:40.000Z", threads: { "parent-native-thread": { thread_id: "parent-native-thread", kind: "leader", first_seen_at: "2026-05-21T19:04:40.000Z", last_seen_at: "2026-05-21T19:04:40.000Z", turn_count: 1, }, "child-native-session": { thread_id: "child-native-session", kind: "subagent", first_seen_at: "2026-05-21T19:04:41.000Z", last_seen_at: "2026-05-21T19:04:41.000Z", turn_count: 1, mode: "review", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "child-native-session", thread_id: "child-native-session", turn_id: "turn-subagent-review", prompt: [ "Read-only review only. Do not edit files. Do not inspect/mutate OMX state/hooks.", "Context: The user asked for $autopilot, and this subagent must only review the patch.", ].join("\n\n"), }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.equal(additionalContext, ""); assert.equal( existsSync(join(boxedStateDir, "sessions", "child-native-session", "skill-active-state.json")), false, ); assert.equal( existsSync(join(boxedStateDir, "sessions", "child-native-session", "autopilot-state.json")), false, ); assert.equal( existsSync(join(cwd, ".omx", "state", "subagent-tracking.json")), false, "subagent tracking must not leak into the source worktree when OMX_ROOT is boxed", ); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalTeamStateRoot; await rm(cwd, { recursive: true, force: true }); await rm(boxedRoot, { recursive: true, force: true }); } }); it("does not inject triage advisory for autopilot keyword prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-keyword-autopilot-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-kw-autopilot-1", thread_id: "thread-triage-kw-ap-1", turn_id: "turn-triage-kw-ap-1", prompt: "$autopilot build this", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-kw-autopilot-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps marked workflow-like answers inert without treating them as a new triage request", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-marked-answer-inert-")); const codexHome = await mkdtemp(join(tmpdir(), "omx-triage-marked-answer-home-")); const previousCodexHome = process.env.CODEX_HOME; try { await writeJson(join(codexHome, ".omx-config.json"), { promptRouting: { triage: { enabled: true } }, }); process.env.CODEX_HOME = codexHome; resetTriageConfigCache(); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "triage-marked-answer-inert", thread_id: "thread-triage-marked-answer-inert", prompt: "[omx question answered] explain this function?", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.equal(result.skillState, null); assert.equal(additionalContext, ""); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "triage-marked-answer-inert", "autopilot-state.json")), false, ); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "triage-marked-answer-inert", "prompt-routing-state.json")), false, ); const promptsResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "triage-prompts-inert", thread_id: "thread-triage-prompts-inert", prompt: "/prompts:architect explain this function?", }, { cwd }, ); assert.equal(promptsResult.skillState, null); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "triage-prompts-inert", "prompt-routing-state.json")), false, ); } finally { if (previousCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = previousCodexHome; resetTriageConfigCache(); await rm(cwd, { recursive: true, force: true }); await rm(codexHome, { recursive: true, force: true }); } }); it("makes fresh autopilot preflight denial observable in state, HUD context, and prompt guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeSessionStart(cwd, "sess-autopilot-observable"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-observable", thread_id: "thread-autopilot-observable", turn_id: "turn-autopilot-observable", prompt: "$autopilot implement issue #2430", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); assert.deepEqual(result.skillState?.active_skills, []); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /denied workflow keyword "\$autopilot" -> autopilot/); assert.match(additionalContext, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(additionalContext, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-observable"); const statePath = join(sessionDir, "autopilot-state.json"); const modeState = JSON.parse(await readFile(statePath, "utf-8")) as { active: boolean; current_phase: string; error?: string; }; assert.equal(modeState.active, false); assert.equal(modeState.current_phase, "failed"); assert.equal(modeState.error, "documented_host_consensus_receipt_unavailable"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); const hudState = await readAllState(cwd); assert.equal(hudState.autopilot, null); assert.doesNotMatch(renderHud(hudState, "focused"), /autopilot:/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps fresh autopilot preflight denial free of Team handoff guidance when Team mode is disabled", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-no-team-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled", }); await writeSessionStart(cwd, "sess-autopilot-observable-no-team"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-observable-no-team", thread_id: "thread-autopilot-observable-no-team", turn_id: "turn-autopilot-observable-no-team", prompt: "$autopilot implement issue #2430", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(additionalContext, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal|\$team/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-observable-no-team"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores disabled $team before outside-tmux Team blocking so later workflows can activate", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-disabled-team-primary-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled", }); await writeSessionStart(cwd, "sess-disabled-team-primary"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-disabled-team-primary", thread_id: "thread-disabled-team-primary", turn_id: "turn-disabled-team-primary", prompt: "$team $ralph fix this", }, { cwd }, ); assert.equal(result.skillState?.skill, "ralph"); assert.equal(result.skillState?.transition_error, undefined); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "sess-disabled-team-primary", "ralph-state.json")), true, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /detected workflow keyword "\$ralph" -> ralph/); assert.doesNotMatch(additionalContext, /Codex App\/native outside-tmux sessions cannot activate/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("makes bare fresh autopilot preflight denial observable in state and prompt guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-bare-observable-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeSessionStart(cwd, "sess-autopilot-bare-observable"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-bare-observable", thread_id: "thread-autopilot-bare-observable", turn_id: "turn-autopilot-bare-observable", prompt: "run autopilot", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, false); assert.equal(result.skillState?.phase, "failed"); assert.equal(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); assert.deepEqual(result.skillState?.active_skills, []); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /denied workflow keyword "autopilot" -> autopilot/); assert.match(additionalContext, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(additionalContext, /Autopilot protocol:|deep-interview -> ralplan -> ultragoal/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-bare-observable"); const statePath = join(sessionDir, "autopilot-state.json"); const modeState = JSON.parse(await readFile(statePath, "utf-8")) as { active: boolean; current_phase: string; error?: string; }; assert.equal(modeState.active, false); assert.equal(modeState.current_phase, "failed"); assert.equal(modeState.error, "documented_host_consensus_receipt_unavailable"); assert.equal(existsSync(join(sessionDir, "deep-interview-state.json")), false); assert.equal(existsSync(join(sessionDir, "ultragoal-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 2: HEAVY injection ───────────────────────────────────────────── it("injects HEAVY advisory and writes prompt-routing-state for a multi-step goal prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-heavy-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-heavy-1", thread_id: "thread-triage-heavy-1", turn_id: "turn-triage-heavy-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /multi-step goal with no workflow keyword/); assert.match(additionalContext, /Prefer the existing autopilot-style workflow/); // skill-active-state.json must NOT be written (triage is advisory only) assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false); // prompt-routing-state.json must be written with lane=HEAVY const stateFile = join(cwd, ".omx", "state", "sessions", "triage-heavy-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { version?: number; last_triage?: { lane?: string; destination?: string }; suppress_followup?: boolean; }; assert.equal(state.version, 1); assert.equal(state.last_triage?.lane, "HEAVY"); assert.equal(state.last_triage?.destination, "autopilot"); assert.equal(state.suppress_followup, true); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 3: LIGHT/explore ──────────────────────────────────────────────── it("injects LIGHT/explore advisory and writes state for a question-shaped prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-explore-1", thread_id: "thread-triage-explore-1", turn_id: "turn-triage-explore-1", prompt: "explain this function", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /read-only\/question-shaped/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-explore-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; suppress_followup?: boolean; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.suppress_followup, true); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 4: LIGHT/executor ─────────────────────────────────────────────── it("injects LIGHT/executor advisory and writes state for a narrow edit-shaped prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-executor-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-executor-1", thread_id: "thread-triage-executor-1", turn_id: "turn-triage-executor-1", prompt: "fix typo in src/foo.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /narrow edit-shaped/); assert.match(additionalContext, /Prefer the executor role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-executor-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "executor"); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 5: LIGHT/designer ─────────────────────────────────────────────── it("injects LIGHT/designer advisory and writes state for a visual/style prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-designer-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-designer-1", thread_id: "thread-triage-designer-1", turn_id: "turn-triage-designer-1", prompt: "make the button blue", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /visual\/style request/); assert.match(additionalContext, /Prefer the designer role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-designer-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "designer"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects LIGHT/researcher advisory and writes state for an official-doc lookup prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-1", thread_id: "thread-triage-researcher-1", turn_id: "turn-triage-researcher-1", prompt: "Find the official docs and version compatibility notes for this SDK", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /external documentation\/reference research request/); assert.match(additionalContext, /Prefer the researcher role surface/); assert.doesNotMatch(additionalContext, /skill: researcher activated/); assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; suppress_followup?: boolean; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); assert.equal(state.suppress_followup, true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes Korean external lookup phrasing to researcher without treating it as workflow activation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-researcher-ko-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-ko-1", thread_id: "thread-triage-researcher-ko-1", turn_id: "turn-triage-researcher-ko-1", prompt: "OpenAI Responses API 공식 문서 찾아줘", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /Prefer the researcher role surface/); assert.equal(result.skillState, null); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-ko-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes official-doc question prompts to researcher instead of explore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-question-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-question-researcher-1", thread_id: "thread-triage-question-researcher-1", turn_id: "turn-triage-question-researcher-1", prompt: "where can I find official docs for OpenAI Responses API?", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-question-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes endpoint-shaped official-doc lookups to researcher instead of local explore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-endpoint-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-endpoint-researcher-1", thread_id: "thread-triage-endpoint-researcher-1", turn_id: "turn-triage-endpoint-researcher-1", prompt: "find official docs for api/v1/responses", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-endpoint-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes dotted technology official-doc lookups to researcher instead of local explore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-dotted-tech-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-dotted-tech-researcher-1", thread_id: "thread-triage-dotted-tech-researcher-1", turn_id: "turn-triage-dotted-tech-researcher-1", prompt: "find official docs for Node.js", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-dotted-tech-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes URL-shaped official-doc lookups with repo paths to researcher instead of local routes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-url-path-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-url-path-researcher-1", thread_id: "thread-triage-url-path-researcher-1", turn_id: "turn-triage-url-path-researcher-1", prompt: "find official docs for github.com/org/repo/src/foo.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the executor role surface/); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-url-path-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps implementation-shaped official-doc prompts on HEAVY instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-researcher-implementation-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-implementation-1", thread_id: "thread-triage-researcher-implementation-1", turn_id: "turn-triage-researcher-implementation-1", prompt: "implement auth using official docs for the SDK", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-implementation-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "HEAVY"); assert.equal(state.last_triage?.destination, "autopilot"); assert.equal(state.last_triage?.reason, "implementation_research_goal"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps planning-shaped official-doc prompts on HEAVY instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-researcher-planning-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-planning-1", thread_id: "thread-triage-researcher-planning-1", turn_id: "turn-triage-researcher-planning-1", prompt: "research and plan auth migration using official docs for the SDK", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-planning-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "HEAVY"); assert.equal(state.last_triage?.destination, "autopilot"); assert.equal(state.last_triage?.reason, "implementation_research_goal"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps local source lookup prompts off researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-local-source-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-local-source-1", thread_id: "thread-triage-local-source-1", turn_id: "turn-triage-local-source-1", prompt: "search source for parseConfig in src/config.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the executor role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-local-source-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "executor"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps anchored local API usage prompts on executor instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-local-api-executor-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-local-api-1", thread_id: "thread-triage-local-api-1", turn_id: "turn-triage-local-api-1", prompt: "find API usage in src/foo.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the executor role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-local-api-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "executor"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps project-scoped local API usage prompts on explore instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-project-api-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-project-api-1", thread_id: "thread-triage-project-api-1", turn_id: "turn-triage-project-api-1", prompt: "find API usage in this project", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-project-api-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.last_triage?.reason, "local_reference_lookup"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps repository changelog lookup prompts on explore despite generic docs terms", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-repo-changelog-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-repo-changelog-1", thread_id: "thread-triage-repo-changelog-1", turn_id: "turn-triage-repo-changelog-1", prompt: "find changelog in this repository", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-repo-changelog-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.last_triage?.reason, "local_reference_lookup"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes anchored read-only questions through explore before executor", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-anchored-question-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-anchored-question-1", thread_id: "thread-triage-anchored-question-1", turn_id: "turn-triage-anchored-question-1", prompt: "what does src/foo.ts do?", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the executor role surface/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-anchored-question-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.last_triage?.reason, "question_or_explanation"); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 6: PASS (no triage injection, no state) ──────────────────────── it("produces no triage advisory and no state for trivial greeting prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-pass-hello-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-pass-hello-1", thread_id: "thread-triage-pass-1", turn_id: "turn-triage-pass-1", prompt: "hello", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-pass-hello-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("produces no triage advisory and no state for ambiguous short prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-pass-short-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-pass-short-1", thread_id: "thread-triage-pass-short-1", turn_id: "turn-triage-pass-short-1", prompt: "fix the thing", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-pass-short-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 7: Turn-2 suppression (same session across two invocations) ──── it("suppresses HEAVY triage re-injection on a short follow-up in the same session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-suppress-heavy-")); const sessionId = "triage-suppress-heavy-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: HEAVY fires const turn1 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-heavy-1", turn_id: "turn-suppress-heavy-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const ctx1 = String( (turn1.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(ctx1, /multi-step goal with no workflow keyword/); // Turn 2: short follow-up — triage suppressed const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-heavy-1", turn_id: "turn-suppress-heavy-2", prompt: "yes, settings page", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(ctx2, /multi-step goal/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses LIGHT/explore triage re-injection on a short follow-up in the same session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-suppress-explore-")); const sessionId = "triage-suppress-explore-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: LIGHT/explore fires await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-explore-1", turn_id: "turn-suppress-explore-1", prompt: "explain this function", }, { cwd }, ); // Turn 2: short follow-up — no duplicate LIGHT injection const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-explore-1", turn_id: "turn-suppress-explore-2", prompt: "the auth helper", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(ctx2, /read-only\/question-shaped/); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 8: First-turn PASS does NOT block later triage ───────────────── it("still applies triage on turn 2 when turn 1 was a PASS with no state written", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-pass-then-light-")); const sessionId = "triage-pass-then-light-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: PASS — no state written await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-pass-then-light-1", turn_id: "turn-pass-then-light-1", prompt: "hello", }, { cwd }, ); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "prompt-routing-state.json")), false, ); // Turn 2: LIGHT/executor should fire normally const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-pass-then-light-1", turn_id: "turn-pass-then-light-2", prompt: "fix typo in src/foo.ts", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(ctx2, /narrow edit-shaped/); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 9: Opt-out forces PASS ───────────────────────────────────────── it("produces no triage advisory when prompt contains 'just chat' opt-out", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-optout-chat-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-optout-chat-1", thread_id: "thread-optout-chat-1", turn_id: "turn-optout-chat-1", prompt: "add dark mode toggle to the settings page, but just chat about it", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-optout-chat-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("produces no triage advisory when prompt contains 'no workflow' opt-out", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-optout-noworkflow-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-optout-noworkflow-1", thread_id: "thread-optout-noworkflow-1", turn_id: "turn-optout-noworkflow-1", prompt: "make the button blue, no workflow", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-optout-noworkflow-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 10: Keyword on follow-up turn wins cleanly ───────────────────── it("keyword on turn 2 suppresses triage and writes no triage state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-kw-followup-")); const sessionId = "triage-kw-followup-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: neutral prompt — triage may or may not fire, doesn't matter await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-kw-followup-1", turn_id: "turn-kw-followup-1", prompt: "hello", }, { cwd }, ); // Turn 2: keyword prompt — keyword fast-path runs, triage does NOT add extra advisory const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-kw-followup-1", turn_id: "turn-kw-followup-2", prompt: "$ralph continue", }, { cwd }, ); assert.equal(turn2.skillState?.skill, "ralph"); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(ctx2, /multi-step goal with no workflow keyword/); assert.doesNotMatch(ctx2, /read-only\/question-shaped/); assert.doesNotMatch(ctx2, /narrow edit-shaped/); assert.doesNotMatch(ctx2, /visual\/style request/); // No triage state written on the keyword turn const triageState = join(cwd, ".omx", "state", "sessions", sessionId, "prompt-routing-state.json"); // The state from turn 1 (if any) must not have been created either (hello = PASS) assert.equal(existsSync(triageState), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 11: Config-disabled path ─────────────────────────────────────── it("produces no triage advisory and no state when triage is disabled in config", async () => { const tmpHome = await mkdtemp(join(tmpdir(), "omx-triage-config-disabled-home-")); const cwd = await mkdtemp(join(tmpdir(), "omx-triage-config-disabled-cwd-")); try { // Write a .omx-config.json in the fake CODEX_HOME that disables triage await writeJson(join(tmpHome, ".omx-config.json"), { promptRouting: { triage: { enabled: false } }, }); process.env.CODEX_HOME = tmpHome; resetTriageConfigCache(); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-disabled-1", thread_id: "thread-triage-disabled-1", turn_id: "turn-triage-disabled-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-disabled-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(tmpHome, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("keeps triage default-enabled when config omits promptRouting.triage.enabled", async () => { const tmpHome = await mkdtemp(join(tmpdir(), "omx-triage-config-omitted-home-")); const cwd = await mkdtemp(join(tmpdir(), "omx-triage-config-omitted-cwd-")); const previousCodexHome = process.env.CODEX_HOME; try { await writeJson(join(tmpHome, ".omx-config.json"), { promptRouting: {}, }); process.env.CODEX_HOME = tmpHome; resetTriageConfigCache(); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-defaulted-1", thread_id: "thread-triage-defaulted-1", turn_id: "turn-triage-defaulted-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-defaulted-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); } finally { if (typeof previousCodexHome === "string") process.env.CODEX_HOME = previousCodexHome; else delete process.env.CODEX_HOME; resetTriageConfigCache(); await rm(tmpHome, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress a short anchored follow-up that is a new request", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-short-new-request-")); const sessionId = "triage-short-new-request-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-short-new-request-1", turn_id: "turn-short-new-request-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-short-new-request-1", turn_id: "turn-short-new-request-2", prompt: "fix typo in src/foo.ts", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(ctx2, /narrow edit-shaped/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("skips triage state persistence for malformed explicit session ids without writing root state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-invalid-session-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "bad/session", thread_id: "thread-triage-invalid-session-1", turn_id: "turn-triage-invalid-session-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(existsSync(join(cwd, ".omx", "state", "prompt-routing-state.json")), false); const log = await readFile( join(cwd, ".omx", "logs", `omx-${new Date().toISOString().slice(0, 10)}.jsonl`), "utf-8", ); assert.match(log, /prompt_session_provenance_rejected/); assert.doesNotMatch(log, /bad\/session/); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("native Stop autopilot deep-interview wait", () => { it("does not force continued execution while autopilot is waiting on a deep-interview omx question", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-question-wait-"), ); try { const sessionId = "sess-autopilot-wait"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, }); await writeJson(join(sessionDir, "autopilot-state.json"), { mode: "autopilot", active: true, current_phase: "waiting-for-user", run_outcome: "blocked_on_user", lifecycle_outcome: "askuserQuestion", session_id: sessionId, state: { deep_interview_question: { status: "waiting_for_user", source: "omx-question", obligation_id: "obligation-stop-1", previous_phase: "deep-interview", }, }, }); await writeJson(join(sessionDir, "deep-interview-state.json"), { mode: "deep-interview", active: false, current_phase: "intent-first", lifecycle_outcome: "askuserQuestion", run_outcome: "blocked_on_user", session_id: sessionId, question_enforcement: { obligation_id: "obligation-stop-1", source: "omx-question", status: "pending", lifecycle_outcome: "askuserQuestion", requested_at: "2026-04-19T00:00:00.000Z", }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "deep-interview", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", session_id: sessionId, thread_id: "thread-autopilot-wait", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe('native UserPromptSubmit payload provenance', () => { it('prefers an explicit payload session over a stale selected pointer and rejects a foreign tracked child without mutation', async () => { const cwd = await mkdtemp(join(tmpdir(), 'omx-native-prompt-provenance-')); try { const stateDir = join(cwd, '.omx', 'state'); await writeSessionStart(cwd, 'selected-root', { nativeSessionId: 'selected-native', pid: process.pid }); await writeJson(join(stateDir, 'sessions', 'selected-root', 'sentinel.json'), { unchanged: true }); await writeJson(join(stateDir, 'sessions', 'selected-root', 'skill-active-state.json'), { version: 1, active: true, skill: 'ralplan', phase: 'planning', session_id: 'selected-root', owner_codex_session_id: 'selected-native', }); const payloadFirst = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'payload-primary', thread_id: 'payload-primary-thread', turn_id: 'payload-primary-turn', prompt: '$ralplan implement payload-first scope', }, { cwd }); assert.equal(payloadFirst.skillState?.session_id, 'payload-primary'); assert.equal(existsSync(join(stateDir, 'sessions', 'payload-primary', 'skill-active-state.json')), true); assert.equal(existsSync(join(stateDir, 'sessions', 'selected-root', 'ralplan-state.json')), false); const alias = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'selected-native', thread_id: 'selected-alias-thread', turn_id: 'selected-alias-turn', prompt: '$ralplan activate through selected alias', }, { cwd }); assert.equal(alias.skillState?.session_id, 'selected-root'); const fallback = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, thread_id: 'selected-fallback-thread', turn_id: 'selected-fallback-turn', prompt: '$ralplan continue', }, { cwd }); assert.equal(fallback.skillState?.session_id, 'selected-root'); const malformedTargetDir = join(stateDir, 'sessions', 'malformed-target'); await mkdir(malformedTargetDir, { recursive: true }); const malformedTargetPath = join(malformedTargetDir, 'ralph-state.json'); await writeFile(malformedTargetPath, '{ malformed'); const malformedTarget = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'malformed-target', thread_id: 'malformed-target-thread', turn_id: 'malformed-target-turn', prompt: '$ralph must not overwrite malformed ownership state', }, { cwd }); assert.equal(malformedTarget.skillState, null); assert.equal(await readFile(malformedTargetPath, 'utf8'), '{ malformed'); assert.equal(existsSync(join(malformedTargetDir, 'skill-active-state.json')), false); const sentinelBefore = await readFile(join(stateDir, 'sessions', 'selected-root', 'sentinel.json'), 'utf-8'); await writeJson(join(stateDir, 'subagent-tracking.json'), { schemaVersion: 1, sessions: { 'foreign-root': { session_id: 'foreign-root', updated_at: '2026-07-14T00:00:00.000Z', threads: { 'foreign-child-thread': { thread_id: 'foreign-child-thread', kind: 'subagent', first_seen_at: '2026-07-14T00:00:00.000Z', last_seen_at: '2026-07-14T00:00:00.000Z', turn_count: 1, }, }, }, }, }); const foreignChild = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'foreign-child', thread_id: 'foreign-child-thread', turn_id: 'foreign-child-turn', prompt: '$ralplan must not activate', }, { cwd }); assert.equal(foreignChild.skillState, null); assert.equal(existsSync(join(stateDir, 'sessions', 'foreign-child', 'skill-active-state.json')), false); assert.equal(await readFile(join(stateDir, 'sessions', 'selected-root', 'sentinel.json'), 'utf-8'), sentinelBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies canonical adapted role intent before hostile pointer states for raw and compiled hooks", async () => { const expected = { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "unsupported_documented_leader_proof: Codex hooks do not expose a documented, non-user-mintable root identity required for adapted Ralplan.", }, }; const payload = { hook_event_name: "PreToolUse", tool_name: "Bash", tool_use_id: "hostile-pointer-role-intent", tool_input: { command: 'omx ralplan role-intent write --role architect --parent-thread "$CODEX_THREAD_ID" --json' }, }; const cases: Array<[string, (stateDir: string, pointerPath: string, cwd: string) => Promise]> = [ ["malformed", async (_stateDir, pointerPath) => { await writeFile(pointerPath, "{", "utf8"); }], ["oversized", async (_stateDir, pointerPath) => { await writeFile(pointerPath, "x".repeat(2 * 1024 * 1024), "utf8"); }], ["unreadable", async (_stateDir, pointerPath) => { await mkdir(pointerPath); }], ["symlink", async (_stateDir, pointerPath, cwd) => { const targetPath = join(cwd, "forged-session.json"); await writeFile(targetPath, '{"session_id":"forged"}', "utf8"); await symlink(targetPath, pointerPath); }], ["replaced", async (_stateDir, pointerPath) => { await writeFile(pointerPath, '{"session_id":"first"}', "utf8"); await rm(pointerPath); await writeFile(pointerPath, '{"session_id":"replacement"}', "utf8"); }], ]; for (const [name, setup] of cases) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-role-intent-${name}-`)); try { const stateDir = join(cwd, ".omx", "state"); const pointerPath = join(stateDir, "session.json"); await mkdir(stateDir, { recursive: true }); await setup(stateDir, pointerPath, cwd); const raw = await dispatchCodexNativeHook({ ...payload, cwd }, { cwd }); assert.deepEqual(raw.outputJson, expected, `${name} raw`); assert.deepEqual((await readdir(stateDir)).sort(), ["session.json"], `${name} raw writes`); const compiled = parseSingleJsonStdout(runNativeHookCli({ ...payload, cwd }, { cwd })); assert.deepEqual(compiled, expected, `${name} compiled`); assert.deepEqual((await readdir(stateDir)).sort(), ["session.json"], `${name} compiled writes`); } finally { await rm(cwd, { recursive: true, force: true }); } } }); }); describe("native Team notice ledger reconciliation", () => { it("invalidates removed Teams before queued wake context reaches the model", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-team-notice-ledger-")); const stateRoot = join(cwd, ".omx", "state"); try { const session = await writeSessionStart(cwd, "sess-team-notice-ledger"); let wakePrompt = ""; for (const teamName of ["notice-live", "notice-removed"]) { const teamDir = join(stateRoot, "team", teamName); await mkdir(teamDir, { recursive: true }); await writeFile(join(teamDir, "config.json"), JSON.stringify({ team_name: teamName })); const registration = await registerTeamNotice({ stateRoot, targetId: "leader-shared", teamName, noticeClass: "mailbox", generation: "1", source: { kind: "test" }, }); wakePrompt ||= registration.prompt ?? ""; } await rm(join(stateRoot, "team", "notice-removed"), { recursive: true, force: true }); const result = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, session_id: session.session_id, prompt: `${wakePrompt} [OMX_TMUX_INJECT]`, }, { cwd }); const context = String((result.outputJson?.hookSpecificOutput as { additionalContext?: unknown } | undefined)?.additionalContext ?? ""); assert.match(context, /notice-live \(mailbox\)/); assert.doesNotMatch(context, /notice-removed/); const replay = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, session_id: session.session_id, prompt: `${wakePrompt} [OMX_TMUX_INJECT]`, }, { cwd }); const replayContext = String((replay.outputJson?.hookSpecificOutput as { additionalContext?: unknown } | undefined)?.additionalContext ?? ""); assert.match(replayContext, /notice-live \(mailbox\)/); assert.doesNotMatch(replayContext, /notice-removed/); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("Stop transcript-backed recovery for a stale-dead selected pointer (issue #3427)", { concurrency: false }, () => { async function writeStaleDeadStopFixture( cwd: string, sessionId: string, options: { transcript?: boolean; sessionDir?: boolean; pointer?: Record } = {}, ): Promise<{ stateDir: string; transcriptPath: string; pointerBefore: string }> { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, pid: 2_147_483_647, ...options.pointer, }); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); if (options.transcript !== false) { await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd, timestamp: "2026-08-03T00:00:00.000Z" }, })}\n`, ); } if (options.sessionDir !== false) { await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); } return { stateDir, transcriptPath, pointerBefore }; } async function writeSessionScopedModeState(cwd: string, sessionId: string, mode: string): Promise { const stateDir = join(cwd, ".omx", "state"); await writeJson(join(stateDir, "sessions", sessionId, `${mode}-state.json`), { active: true, mode, current_phase: "executing", session_id: sessionId, workingDirectory: cwd, }); } it("authorizes an exact stale-dead Stop through the payload transcript and evaluates session-scoped state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-transcript-recovery-")); try { const sessionId = "live-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); await writeSessionScopedModeState(cwd, sessionId, "autopilot"); const first = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, turn_id: "3427-stop-turn", }, { cwd }); assert.equal(first.outputJson?.decision, "block"); assert.equal(first.outputJson?.stopReason, "autopilot_executing"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); // The recovery is read-only, so a repeated Stop is idempotent. const second = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, turn_id: "3427-stop-turn-2", }, { cwd }); assert.equal(second.outputJson?.stopReason, "autopilot_executing"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("authorizes the exact stale-dead Stop as a clean no-op when no session-scoped state is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-transcript-clean-stop-")); try { const sessionId = "clean-live-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a foreign transcript cwd", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-foreign-cwd-")); try { const sessionId = "foreign-cwd-session-3427"; const foreignCwd = join(tmpdir(), "omx-3427-unrelated-repo"); await mkdir(foreignCwd, { recursive: true }); const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd: foreignCwd }, })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the transcript session_meta id does not match the payload session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-mismatched-id-")); try { const sessionId = "mismatch-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: "some-other-session", session_id: sessionId, cwd }, })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the transcript filename is not bound to the session id", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-filename-binding-")); try { const sessionId = "filename-bound-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const transcriptPath = join(cwd, "rollout-unrelated.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd }, })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a relative transcript path", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-relative-transcript-")); try { const sessionId = "relative-transcript-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: `rollout-${sessionId}.jsonl`, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for conflicting session_id/sessionId aliases", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-conflicting-aliases-")); try { const sessionId = "alias-conflict-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, sessionId: "other-alias-session-3427", transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for non-string transcript aliases", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-nonstring-transcript-")); try { const sessionId = "nonstring-transcript-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: 42, transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when transcript_path and transcriptPath disagree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-transcript-alias-conflict-")); try { const sessionId = "transcript-alias-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const otherPath = join(cwd, `rollout-${sessionId}-other.jsonl`); await writeFile(otherPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd }, })}\n`); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, transcriptPath: otherPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for owner identity claims", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-owner-claim-")); try { const sessionId = "owner-claim-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, owner_codex_session_id: sessionId, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for subagent thread-spawn provenance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-subagent-provenance-")); try { const sessionId = "subagent-provenance-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, source: { subagent: { thread_spawn: { parent_thread_id: "some-parent", depth: 1 } } }, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a payload with agent_id subagent provenance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-agent-id-provenance-")); try { const sessionId = "agent-id-provenance-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, agent_id: "child-agent-thread", }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a typed agent-role subagent payload", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-agent-role-provenance-")); try { const sessionId = "agent-role-provenance-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, agent_type: "executor", }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the session-scoped state directory is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-missing-session-dir-")); try { const sessionId = "missing-session-dir-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a symlink transcript", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-symlink-transcript-")); try { const sessionId = "symlink-transcript-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { transcript: false }); const realTranscript = join(cwd, `real-${sessionId}.jsonl`); await writeFile(realTranscript, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd }, })}\n`); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await symlink(realTranscript, transcriptPath); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the transcript first record is not session_meta", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-non-meta-transcript-")); try { const sessionId = "non-meta-transcript-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { transcript: false }); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await writeFile( transcriptPath, `${JSON.stringify({ type: "event_msg", payload: { type: "user_message", message: "hi" } })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for an identity-indeterminate pointer even with a valid transcript", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-indeterminate-pointer-")); try { const sessionId = "indeterminate-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { pointer: { identity_schema_version: 1 }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("never rewrites the singleton selected pointer during transcript recovery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-pointer-immutable-")); try { const sessionId = "pointer-immutable-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); await writeSessionScopedModeState(cwd, sessionId, "autopilot"); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson?.decision, "block"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "sessions", "stale-dead", "session.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("covers the built distribution entrypoint for the transcript-backed recovery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-dist-recovery-")); try { const sessionId = "dist-recovery-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); await writeSessionScopedModeState(cwd, sessionId, "autopilot"); const output = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd })); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_executing"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed in the built distribution entrypoint for a conflicting alias", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-dist-failclosed-")); try { const sessionId = "dist-failclosed-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const output = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: sessionId, sessionId: "dist-other-session-3427", transcript_path: transcriptPath, }, { cwd })); assert.deepEqual(output, {}); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); });