#!/usr/bin/env bash
# Ledger birth record for agent sessions. Sourced by the PATH shim that launches
# claude / codex / cursor-agent, and run before the shim execs the real binary,
# so the entry exists on disk before the session can die.
#
# Contract for readers: docs/agent-session-ledger.md (schemaVersion 1).
#
# No subprocess may be spawned that a PATH shim could intercept (there is a `git`
# shim in front of the real git), and nothing here may abort the launch.

agent_ledger_dir() {
  printf '%s' "${AGENT_SESSIONS_DIR:-$HOME/.local/state/agent-sessions}"
}

_agent_ledger_json() {
  local s=$1 c i
  s=${s//\\/\\\\}
  s=${s//\"/\\\"}
  s=${s//$'\t'/\\t}
  s=${s//$'\n'/\\n}
  s=${s//$'\r'/\\r}
  # A control byte anywhere in a path or ref name makes the whole entry unparseable,
  # which silently drops the session from recovery.
  for i in {1..31}; do
    case $i in 9 | 10 | 13) continue ;; esac
    printf -v c '\\x%02x' "$i"
    printf -v c "$c"
    s=${s//"$c"/$(printf '\\u%04x' "$i")}
  done
  printf '"%s"' "$s"
}

_agent_ledger_json_or_null() {
  if [[ -z ${1:-} ]]; then printf 'null'; else _agent_ledger_json "$1"; fi
}

# Walks up from $1 to the first directory holding .git, then reads HEAD without
# invoking git. Sets _alg_repo_root, _alg_branch, _alg_worktree.
_agent_ledger_git() {
  _alg_repo_root=""
  _alg_branch=""
  _alg_worktree="false"
  local dir=$1 gitpath head_file line
  while [[ -n $dir && $dir != "/" ]]; do
    gitpath="$dir/.git"
    if [[ -d $gitpath ]]; then
      _alg_repo_root=$dir
      head_file="$gitpath/HEAD"
      break
    fi
    if [[ -f $gitpath ]]; then
      # `gitdir: <path>` — a linked worktree or a submodule.
      _alg_repo_root=$dir
      _alg_worktree="true"
      read -r _ line <"$gitpath" 2>/dev/null || line=""
      [[ $line == /* ]] || line="$dir/$line"
      head_file="$line/HEAD"
      break
    fi
    dir=${dir%/*}
  done
  [[ -n ${head_file:-} && -r ${head_file:-} ]] || return 0
  read -r line <"$head_file" 2>/dev/null || return 0
  if [[ $line == "ref: refs/heads/"* ]]; then
    _alg_branch=${line#ref: refs/heads/}
  elif [[ -n $line ]]; then
    _alg_branch="detached:${line:0:12}"
  fi
}

# agent_ledger_birth <runtime> — prints the ledger id, or nothing on failure.
agent_ledger_birth() {
  local runtime=$1
  local dir sessions_dir id stamp rand cwd tty entry tmp

  dir=$(agent_ledger_dir)
  sessions_dir="$dir/sessions"
  # -m 700 on creation: a later chmod leaves the socket dir world-traversable in between,
  # and a reachable socket is a live agent terminal.
  mkdir -p -m 700 "$sessions_dir" "$dir/sock" 2>/dev/null || return 1
  chmod 700 "$dir" 2>/dev/null || true

  # bash's %()T formats in the caller's zone; every other writer of this ledger emits UTC,
  # and a mixed-zone startedAt makes a session's age negative. TZ must reach the builtin
  # through the environment, so a subshell prefix is the only form that takes effect.
  stamp=$(TZ=UTC printf '%(%Y%m%dT%H%M%S)T' -1)
  rand=$RANDOM$RANDOM
  id="${runtime}-${stamp}Z-${rand: -6}"

  cwd=$PWD
  _agent_ledger_git "$cwd"

  tty=""
  [[ -t 0 ]] && tty=$(readlink "/proc/$$/fd/0" 2>/dev/null) || true
  [[ $tty == /dev/* ]] || tty=""

  local boot_id=""
  read -r boot_id </proc/sys/kernel/random/boot_id 2>/dev/null || boot_id=""

  # Caller sets AGENT_LEDGER_MUX_WANT=1 when it intends to wrap the launch; the socket
  # path is derived here so the entry and the wrapper can never disagree about it.
  local mux_kind='null' mux_socket='null'
  AGENT_LEDGER_MUX_SOCKET=""
  if [[ ${AGENT_LEDGER_MUX_WANT:-0} == 1 ]]; then
    AGENT_LEDGER_MUX_SOCKET="$dir/sock/$id"
    mux_kind='"dtach"'
    mux_socket=$(_agent_ledger_json "$AGENT_LEDGER_MUX_SOCKET")
  fi

  local project=""
  [[ -n $_alg_repo_root ]] && project=${_alg_repo_root##*/}

  entry=$(
    cat <<JSON
{
  "schemaVersion": 1,
  "ledgerId": $(_agent_ledger_json "$id"),
  "runtime": $(_agent_ledger_json "$runtime"),
  "startedAt": "$(TZ=UTC printf '%(%Y-%m-%dT%H:%M:%S)TZ' -1)",
  "host": $(_agent_ledger_json "${HOSTNAME:-unknown}"),
  "bootId": $(_agent_ledger_json_or_null "$boot_id"),
  "cwd": $(_agent_ledger_json "$cwd"),
  "repoRoot": $(_agent_ledger_json_or_null "$_alg_repo_root"),
  "project": $(_agent_ledger_json_or_null "$project"),
  "branch": $(_agent_ledger_json_or_null "$_alg_branch"),
  "worktree": $_alg_worktree,
  "tty": $(_agent_ledger_json_or_null "$tty"),
  "launcherPid": $$,
  "parentLedgerId": $(_agent_ledger_json_or_null "${AGENT_LEDGER_ID:-}"),
  "mux": { "kind": $mux_kind, "socket": $mux_socket, "target": $(_agent_ledger_json "$id") },
  "pid": null,
  "pidStartTicks": null,
  "sessionId": null,
  "transcriptPath": null,
  "lastHeartbeatAt": null,
  "finishedAt": null,
  "finishReason": null
}
JSON
  ) || return 1

  tmp="$sessions_dir/.$id.$$.tmp"
  printf '%s\n' "$entry" >"$tmp" 2>/dev/null || return 1
  mv -f "$tmp" "$sessions_dir/$id.json" 2>/dev/null || { rm -f "$tmp"; return 1; }

  printf '%s' "$id"
}
