#!/usr/bin/env bash
# _tmpjail-shim.sh — shared body for the /tmp-jail PATH shims in this directory.
# Each agent shim (claude, codex, cursor-agent) is a symlink to this file.
#
# Why a PATH shim and not a shell alias: PATH is inherited across every exec
# boundary, so this fires for ANY launch method -- interactive shell, a
# non-interactive `bash -c`, a launcher script, cron, or another agent's
# subprocess. Aliases only exist in the one interactive shell that defined
# them, which is why they never covered scripted agent sessions. This dir
# (~/.claude/bin) is first in PATH, so it wins over the real binaries in
# ~/.npm-global/bin and ~/.local/bin. Agent self-updates repoint those real
# symlinks but never touch this shim, and `real` is re-resolved each run, so
# the jail survives updates.
#
# OD_PATH_SHIM_MARKER — identifies this file to every shim resolver, so a second copy
# of it on PATH is rejected as a candidate instead of being invoked as the "real"
# binary (wrapper invoking itself = unbounded fork loop).
set -e
name="${0##*/}"
SHIM_DIR="$(cd "$(dirname "$(readlink -f -- "${BASH_SOURCE[0]}")")" && pwd)"

GUARD_LIB="$SHIM_DIR/../lib/shim-guard.sh"
if [[ ! -r "$GUARD_LIB" ]]; then
  echo "$name: tmpjail shim cannot read $GUARD_LIB — refusing" >&2
  exit 78
fi
# shellcheck source=../lib/shim-guard.sh
source "$GUARD_LIB"
shim_guard_enter "$name"

real="$(shim_resolve_real "$name" "$SHIM_DIR")" || real=""
if [[ -z "$real" ]]; then
  echo "$name: command not found (tmpjail shim found no real binary outside $SHIM_DIR)" >&2
  exit 127
fi

if [[ "$SHIM_REENTRY" == 1 ]]; then
  exec "$real" "$@"
fi

# Skip the npm Node launcher when its native Codex payload is available.
if [[ "$name" == "codex" && "$real" == "$HOME/.npm-global/bin/codex" ]]; then
  _codex_package_root="$HOME/.npm-global/lib/node_modules/@openai/codex"
  _codex_platform_package=""
  _codex_target=""
  case "${OSTYPE:-}:${HOSTTYPE:-}" in
    linux*:x86_64)
      _codex_platform_package="codex-linux-x64"
      _codex_target="x86_64-unknown-linux-musl"
      ;;
    linux*:aarch64 | linux*:arm64)
      _codex_platform_package="codex-linux-arm64"
      _codex_target="aarch64-unknown-linux-musl"
      ;;
    darwin*:x86_64)
      _codex_platform_package="codex-darwin-x64"
      _codex_target="x86_64-apple-darwin"
      ;;
    darwin*:aarch64 | darwin*:arm64)
      _codex_platform_package="codex-darwin-arm64"
      _codex_target="aarch64-apple-darwin"
      ;;
  esac
  _codex_native="$_codex_package_root/node_modules/@openai/$_codex_platform_package/vendor/$_codex_target/bin/codex"
  if [[ -n "$_codex_target" && ! -x "$_codex_native" ]]; then
    _codex_native="$_codex_package_root/vendor/$_codex_target/bin/codex"
  fi
  if [[ -n "$_codex_target" && -x "$_codex_native" ]]; then
    real="$_codex_native"
    CODEX_MANAGED_PACKAGE_ROOT="$_codex_package_root"
    CODEX_MANAGED_BY_NPM=1
    unset CODEX_MANAGED_BY_BUN CODEX_MANAGED_BY_PNPM
    export CODEX_MANAGED_PACKAGE_ROOT CODEX_MANAGED_BY_NPM
  fi
fi

if [[ "$name" == "codex" ]]; then
  export CODEX_SQLITE_HOME="${CODEX_SQLITE_HOME:-$HOME/.codex-shared-state}"
fi

# Durable session ledger. The entry reaches disk BEFORE the exec below, so a session
# whose terminal dies is still discoverable afterwards. AGENT_LEDGER_ID is exported into
# the whole tree; readers resolve the live PID by scanning /proc/*/environ for it, which
# is immune to PID reuse. Contract: overdeck docs/agent-session-ledger.md.
#
# Interactive launches are additionally wrapped in dtach so terminal death detaches
# instead of killing the session. dtach does no terminal emulation -- native scrollback,
# mouse selection and every control key pass straight through (tmux/screen take over the
# outer terminal's alternate screen and would change how the terminal feels).
# Kill switch: AGENT_LEDGER_MUX=0.
#
# A human's own session is routed to _human-session (tmux in human.slice) instead, so it must
# not also claim a dtach socket nothing will ever create. Classify first, then record.
HUMAN_SESSION_RUNTIMES="${HUMAN_SESSION_RUNTIMES:-claude}"
AGENT_LEDGER_HUMAN=0
if [[ -z "${TMPJAIL_ACTIVE:-}" && -z "${HUMAN_SESSION_ACTIVE:-}" && " $HUMAN_SESSION_RUNTIMES " == *" $name "* ]] &&
  [[ -r "$SHIM_DIR/../lib/session-class.sh" ]]; then
  {
    # shellcheck source=../lib/session-class.sh
    . "$SHIM_DIR/../lib/session-class.sh"
    session_is_human >/dev/null && AGENT_LEDGER_HUMAN=1
  } || :
fi

if [[ -r "$HOME/.claude/lib/agent-session-ledger.sh" ]]; then
  {
    # shellcheck source=/dev/null
    . "$HOME/.claude/lib/agent-session-ledger.sh"
    if [[ "$AGENT_LEDGER_HUMAN" != 1 ]] &&
      [[ -z "${TMPJAIL_ACTIVE:-}" && -z "${DTACH:-}" && "${AGENT_LEDGER_MUX:-1}" != "0" ]] &&
      [[ -t 0 && -t 1 ]] && command -v dtach >/dev/null 2>&1; then
      AGENT_LEDGER_MUX_WANT=1
    fi
    # agent_ledger_birth runs in a command substitution, so the socket it derives cannot
    # come back through a variable -- rebuild it here from the id it printed.
    _ledger_id="$(AGENT_LEDGER_MUX_WANT="${AGENT_LEDGER_MUX_WANT:-0}" agent_ledger_birth "$name")"
    if [[ -n "$_ledger_id" ]]; then
      export AGENT_LEDGER_ID="$_ledger_id"
      if [[ ${AGENT_LEDGER_MUX_WANT:-0} == 1 ]]; then
        AGENT_LEDGER_MUX_SOCKET="$(agent_ledger_dir)/sock/$_ledger_id"
      fi
    fi
  } || :
fi

# Already inside a jail (nested agent launch): don't wrap again -- nested bwrap
# can fail, and the child inherits the parent's /tmp overlay anyway (still off
# the real /tmp, which is the whole point).
if [[ -n "${TMPJAIL_ACTIVE:-}" ]]; then
  exec "$real" "$@"
fi

# A human typing this at a terminal owns the session; an agent spawning it does not. The
# human's goes to human.slice (no ceiling, survives its terminal), the agent's keeps today's
# agent.slice confinement. HUMAN_SESSION_ACTIVE marks the inside of a protected session, so a
# runtime launched from within one is confined as the agent it is.
if [[ "${AGENT_LEDGER_HUMAN:-0}" == 1 ]]; then
  exec "$SHIM_DIR/_human-session" "$real" "$@"
fi

# dtach outermost: confine.sh still runs inside it, so the agent tree lands in its own
# confine-agent-*.scope exactly as before. -E removes the detach key (the user never
# detaches by hand), -z lets Ctrl-Z through to the agent, -r winch redraws on reattach.
if [[ -n "${AGENT_LEDGER_MUX_SOCKET:-}" ]]; then
  export DTACH=1
  exec dtach -c "$AGENT_LEDGER_MUX_SOCKET" -E -z -r winch \
    "$SHIM_DIR/_agent-build-scope" tmpjail "$real" "$@"
fi

exec "$SHIM_DIR/_agent-build-scope" tmpjail "$real" "$@"
