#!/usr/bin/env bash
# _human-session — run a human's interactive agent runtime where nothing on this machine
# can kill it.
#
#   cgroup    human.slice: no memory/pid/cpu ceiling, MemoryMin reservation, oomd-exempt
#   oom       oom_score_adj -900 on the tmux server, inherited by every pane
#   lifetime  a tmux server owned by systemd, so a terminal or gnome-terminal-server
#             crash detaches the session instead of killing it
#
# usage: _human-session <runtime-binary> [arg...]
# Reached only from _tmpjail-shim.sh, which classifies the caller with lib/session-class.sh.
# Nothing here is a security boundary: it is reached ONLY after that classification says
# human, and it grants no capability an interactive shell does not already have.
set -uo pipefail

SELF_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
STATE_DIR="$HOME/.local/state/human-session"
SOCK="$STATE_DIR/tmux.sock"
CONF="$SELF_DIR/../lib/human-session.tmux.conf"
SLICE=human.slice
SERVER_UNIT=human-session-tmux.service
OOM_SCORE=-900

hs_note() { printf '[human-session] %s\n' "$*" >&2; }

(( $# >= 1 )) || { hs_note "usage: _human-session <runtime> [arg...]"; exit 64; }

RUNTIME="$1"
shift

mkdir -p "$STATE_DIR" || { hs_note "cannot create $STATE_DIR"; exit 73; }
chmod 700 "$STATE_DIR" 2>/dev/null

tmux_() { tmux -S "$SOCK" "$@"; }

server_pid() { tmux_ display-message -p '#{pid}' 2>/dev/null; }

server_in_slice() { # 0 when the running server is inside human.slice
  local pid cg
  pid="$(server_pid)"
  [[ "$pid" =~ ^[0-9]+$ ]] || return 1
  cg="$(cat "/proc/$pid/cgroup" 2>/dev/null)" || return 1
  [[ "$cg" == *"/$SLICE/"* ]]
}

start_server() {
  systemctl --user reset-failed "$SERVER_UNIT" 2>/dev/null
  systemd-run --user --quiet --collect \
    --unit="$SERVER_UNIT" \
    --slice="$SLICE" \
    --service-type=forking \
    -p OOMPolicy=continue \
    -p KillMode=process \
    -p TasksMax=infinity \
    -p MemoryMax=infinity \
    -p MemoryHigh=infinity \
    -p MemorySwapMax=infinity \
    -p CPUWeight=1000 \
    -p ManagedOOMPreference=omit \
    -- /usr/bin/tmux -S "$SOCK" -f "$CONF" start-server || return 1

  local i
  for i in $(seq 1 50); do
    server_pid >/dev/null 2>&1 && return 0
    sleep 0.1
  done
  return 1
}

# Only root may lower oom_score_adj; the user manager clamps -900 to its own 100. Children
# of the server inherit whatever is set here, so this runs once per server, not per session.
shield_server_oom() {
  local pid="$1"
  [[ "$(cat "/proc/$pid/oom_score_adj" 2>/dev/null)" == "$OOM_SCORE" ]] && return 0
  command -v deck-sudo >/dev/null 2>&1 || return 1
  deck-sudo sh -c "printf '%s' $OOM_SCORE > /proc/$pid/oom_score_adj" >/dev/null 2>&1 || return 1
  [[ "$(cat "/proc/$pid/oom_score_adj" 2>/dev/null)" == "$OOM_SCORE" ]]
}

# Runs the runtime in human.slice without tmux. Used when tmux is unusable: losing terminal
# durability is bad, losing the cgroup protection as well would be worse.
exec_without_tmux() { # $1=reason, rest=runtime argv
  hs_note "$1 — running in $SLICE without tmux (session will NOT survive terminal death)"
  shift
  export HUMAN_SESSION_ACTIVE=1
  export AGENT_BUILD_SCOPE_ACTIVE=1
  exec systemd-run --user --scope --quiet --collect \
    --slice="$SLICE" \
    --unit="human-session-$$-${RANDOM}" \
    --same-dir \
    -p OOMPolicy=continue \
    -p TasksMax=infinity \
    -p MemoryMax=infinity \
    -p MemorySwapMax=infinity \
    -- "$HOME/.claude/bin/tmpjail" "$RUNTIME" "$@"
}

command -v tmux >/dev/null 2>&1 || exec_without_tmux "tmux not installed" "$@"
[[ -r "$CONF" ]] || exec_without_tmux "tmux config missing at $CONF" "$@"

if ! server_pid >/dev/null 2>&1; then
  start_server || exec_without_tmux "tmux server would not start in $SLICE" "$@"
fi

# A server the user started by hand lives in the terminal's cgroup, so attaching to it would
# silently hand back every property this launcher exists to guarantee.
server_in_slice || exec_without_tmux "existing tmux server on $SOCK is not in $SLICE" "$@"

SERVER_PID="$(server_pid)"
shield_server_oom "$SERVER_PID" \
  || hs_note "could not set oom_score_adj=$OOM_SCORE on the tmux server (needs root via deck-sudo); session keeps every other protection"

# The runtime's environment (account routing from cld, API base URLs, CLAUDE_HOME) must
# reach the pane verbatim; a tmux pane otherwise inherits the SERVER's environment, which
# came from systemd and has none of it.
umask 077
SPAWN="$(mktemp "$STATE_DIR/spawn.XXXXXXXX.sh")" || exec_without_tmux "cannot write spawn file" "$@"
{
  printf '#!/usr/bin/env bash\n'
  export -p
  printf 'export HUMAN_SESSION_ACTIVE=1\n'
  printf 'export AGENT_BUILD_SCOPE_ACTIVE=1\n'
  printf 'cd %q || exit 1\n' "$PWD"
  # Unlinking before exec keeps the environment (which carries credentials) on disk for the
  # few milliseconds the pane needs to read it; the open fd survives the unlink.
  printf 'rm -f %q\n' "$SPAWN"
  printf 'exec %q %q' "$HOME/.claude/bin/tmpjail" "$RUNTIME"
  for a in "$@"; do printf ' %q' "$a"; done
  printf '\n'
} >"$SPAWN"
chmod 700 "$SPAWN"

NAME="$(basename "$PWD")-$(date +%H%M%S)"
NAME="${NAME//[^A-Za-z0-9._-]/_}"

if ! tmux_ new-session -d -s "$NAME" -c "$PWD" -x "$(tput cols 2>/dev/null || echo 200)" \
     -y "$(tput lines 2>/dev/null || echo 50)" "exec bash $SPAWN"; then
  rm -f "$SPAWN"
  exec_without_tmux "tmux could not create a session" "$@"
fi
tmux_ set-option -t "$NAME" @human_cwd "$PWD" 2>/dev/null
tmux_ set-option -t "$NAME" @human_runtime "$(basename "$RUNTIME")" 2>/dev/null
tmux_ set-option -t "$NAME" @human_started "$(date -Is)" 2>/dev/null

# The ledger entry was written before this launcher ran and does not know the session
# survives in tmux. Recording the target is what lets a reader reopen it; a failure here
# must never cost the user their session, hence the swallow.
if [[ -n "${AGENT_LEDGER_ID:-}" ]]; then
  node --input-type=module -e \
    'const [mod, id, name, sock] = process.argv.slice(1);
     const { updateEntry } = await import(mod);
     updateEntry(id, { tmuxSession: name, tmuxSocket: sock });' \
    "$SELF_DIR/../lib/agent-session-reader.mjs" "$AGENT_LEDGER_ID" "$NAME" "$SOCK" \
    >/dev/null 2>&1 || :
fi

detached="$(tmux_ list-sessions -F '#{session_name} #{session_attached}' 2>/dev/null |
  awk -v n="$NAME" '$1 != n && $2 == 0' | wc -l)"
(( detached > 0 )) && hs_note "$detached earlier session(s) still running detached — reattach with: claude-sessions"

exec tmux -S "$SOCK" attach-session -t "$NAME"
