// OPR.0.4.8.2 — OpenRig YOLO mode (opt-in, DEFAULT OFF). // // A simple deterministic setting that rides the STABLE launch-flag surface only (per the founder's // two-surface rule: launch flags may be deterministic code; config-file policy may NOT). When ON, // every managed seat boots at its harness's maximally-permissive LAUNCH FLAG: // - Claude: --dangerously-skip-permissions (permission bypass) // - Codex: -s danger-full-access (maximally-permissive sandbox; OFF floor = the // explicit -s workspace-write flag, NOT a harness default) // - Pi: --approve (full RESOURCE TRUST — Pi's // --approve/--no-approve govern RESOURCE TRUST, not a permission policy) // When OFF (the default), seats boot with the usability floor, unchanged. The YOLO path writes ZERO // config files — it only selects a launch flag. Opt-in via the OPENRIG_YOLO env setting. (The // zero-permission-config-write property concerns Claude/Codex permission policy; Pi is resource trust.) /** OPR.0.4.8.3 Seam B: a seat's RESOLVED permission-policy posture. When present it is * authoritative for that seat and overrides the OPENRIG_YOLO env read in BOTH directions * (an attached builtin:locked keeps the floor even under global YOLO; an attached custom * full_bypass flag policy lifts the seat without the env switch). Absent = no policy * attached → the env decision stands (0.4.8.2 behavior, unchanged). */ export type ResolvedLaunchPosture = "floor" | "full_bypass" | "auto"; export function yoloEnabled( env: NodeJS.ProcessEnv = process.env, resolvedPosture?: ResolvedLaunchPosture, ): boolean { if (resolvedPosture) return resolvedPosture === "full_bypass"; const v = env.OPENRIG_YOLO; return v === "1" || v === "true"; } // ── The single launch-posture decision per harness — used on EVERY managed launch path (fresh, // resume, fork) so the floor (OFF) and the maximally-permissive posture (ON) are uniform, never // path-dependent. NOTE the ON posture differs by harness: Claude/Codex = permission bypass; Pi = // full RESOURCE TRUST (--approve), which is not a permission policy. ── /** Claude launch posture flag: floor `--permission-mode acceptEdits`, or the full bypass * (global YOLO, or a per-seat resolved full_bypass policy attachment). */ export function claudePostureFlag( env: NodeJS.ProcessEnv = process.env, resolvedPosture?: ResolvedLaunchPosture, permissionMode?: string, ): string { if (permissionMode !== undefined) { if (!/^[A-Za-z][A-Za-z0-9]*$/.test(permissionMode)) throw new Error("Invalid Claude permission mode"); return `--permission-mode ${permissionMode}`; } if (resolvedPosture === "auto") { return "--permission-mode auto"; } return yoloEnabled(env, resolvedPosture) ? "--dangerously-skip-permissions" : "--permission-mode acceptEdits"; } /** Claude Code answers plan-mode turns with Sonnet when its model setting is exactly the alias `haiku` (its own * plan-mode upgrade; a full model id keeps its model). The launch arguments stay as configured; this is the note the * person sees for that pairing, on the launch or restore result and on `rig seat status`. */ export function planModelAliasNotice( runtime: string | null | undefined, choice: { model?: string | null; permissionMode?: string }, seat: string, ): string | undefined { if (runtime !== "claude-code" || choice.permissionMode !== "plan" || choice.model?.trim() !== "haiku") return undefined; return `Claude Code answers plan-mode turns with Sonnet when the model is the alias "haiku". To keep Haiku, set a full Haiku model id: rig seat set-model ${seat} --model --reason "".`; } /** * Codex launch posture segment (leading space included), applied on EVERY managed Codex path * (fresh / resume / native-fork). `profileArg` is the already-formatted ` -p ` string or "". * - YOLO ON → ` -s danger-full-access` (maximally-permissive sandbox), overriding even a named profile. * - OFF + named profile → the profile (it governs its own sandbox). * - OFF + no profile → OpenRig's explicit workspace-only floor ` -s workspace-write`. * Explicit resolved full_bypass selects both sandbox and approval behavior. The legacy * environment-only YOLO path remains sandbox-only; unselected defaults are unchanged. */ export function codexPostureArg( profileArg: string, env: NodeJS.ProcessEnv = process.env, resolvedPosture?: ResolvedLaunchPosture, ): string { if (resolvedPosture === "full_bypass") return " -s danger-full-access -a never"; if (yoloEnabled(env, resolvedPosture)) return " -s danger-full-access"; return profileArg ? profileArg : " -s workspace-write"; } /** Pi RESOURCE TRUST (Pi's --approve/--no-approve govern resource trust, NOT a permission policy): * YOLO forces `approve`; otherwise the configured posture (default `no-approve`). */ export function piTrust( configured: "approve" | "no-approve" | undefined, env: NodeJS.ProcessEnv = process.env, resolvedPosture?: ResolvedLaunchPosture, ): "approve" | "no-approve" { // Pi wording discipline: this is RESOURCE TRUST, not a permission policy — a resolved // full_bypass policy forces full resource trust exactly as global YOLO does. return yoloEnabled(env, resolvedPosture) ? "approve" : configured ?? "no-approve"; } /** * OPR.0.5.3.1 — Claude classic-renderer launch env prefix. * * Claude Code's fullscreen renderer draws to the terminal ALTERNATE screen, which * emits no scrollback — so tmux capture-pane stores nothing and `rig transcript` * goes thin. Launching with CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 forces the * classic renderer and restores native scrollback (and same-pane handover * scroll-preservation). Applied on EVERY managed Claude launch path (fresh / * resume / fork / restore) so behaviour is uniform, never path-dependent — * mirroring the claudePostureFlag pattern. * * Default ON. Config override: set OPENRIG_CLAUDE_DISABLE_ALTERNATE_SCREEN=0 (or * "false") to opt back into the fullscreen renderer (knowingly forgoing scrollback). * * Returns a command PREFIX ("VAR=1 ", trailing space) to prepend to the `claude` * launch command, or "" when disabled (then the command is byte-identical to pre-OPR.0.5.3.1). */ export function claudeClassicRendererEnvPrefix(env: NodeJS.ProcessEnv = process.env): string { const v = env.OPENRIG_CLAUDE_DISABLE_ALTERNATE_SCREEN; return v === "0" || v === "false" ? "" : "CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 "; }