feat: write session lock from all launcher paths
All launch paths (claude, codex, opencode, yolo variants) now write a session.lock before exec'ing, so `mosaic coord status` can detect running agent sessions. Stale locks from dead sessions are cleaned up automatically on next launch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
58
bin/mosaic
58
bin/mosaic
@@ -247,6 +247,47 @@ _detect_mission_prompt() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Write a session lock if an active mission exists in the current directory.
|
||||||
|
# Called before exec so $$ captures the PID that will become the agent process.
|
||||||
|
_write_launcher_session_lock() {
|
||||||
|
local runtime="$1"
|
||||||
|
local mission_file=".mosaic/orchestrator/mission.json"
|
||||||
|
local lock_file=".mosaic/orchestrator/session.lock"
|
||||||
|
|
||||||
|
# Only write lock if mission exists and is active
|
||||||
|
[[ -f "$mission_file" ]] || return 0
|
||||||
|
command -v jq &>/dev/null || return 0
|
||||||
|
|
||||||
|
local m_status
|
||||||
|
m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
|
||||||
|
[[ "$m_status" == "active" || "$m_status" == "paused" ]] || return 0
|
||||||
|
|
||||||
|
local session_id
|
||||||
|
session_id="${runtime}-$(date +%Y%m%d-%H%M%S)-$$"
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg sid "$session_id" \
|
||||||
|
--arg rt "$runtime" \
|
||||||
|
--arg pid "$$" \
|
||||||
|
--arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
--arg pp "$(pwd)" \
|
||||||
|
--arg mid "" \
|
||||||
|
'{
|
||||||
|
session_id: $sid,
|
||||||
|
runtime: $rt,
|
||||||
|
pid: ($pid | tonumber),
|
||||||
|
started_at: $ts,
|
||||||
|
project_path: $pp,
|
||||||
|
milestone_id: $mid
|
||||||
|
}' > "$lock_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up session lock on exit (covers normal exit + signals).
|
||||||
|
# Registered via trap after _write_launcher_session_lock succeeds.
|
||||||
|
_cleanup_session_lock() {
|
||||||
|
rm -f ".mosaic/orchestrator/session.lock" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
# Launcher functions
|
# Launcher functions
|
||||||
launch_claude() {
|
launch_claude() {
|
||||||
check_mosaic_home
|
check_mosaic_home
|
||||||
@@ -263,6 +304,8 @@ launch_claude() {
|
|||||||
|
|
||||||
# If active mission exists and no user prompt was given, inject initial prompt
|
# If active mission exists and no user prompt was given, inject initial prompt
|
||||||
_detect_mission_prompt
|
_detect_mission_prompt
|
||||||
|
_write_launcher_session_lock "claude"
|
||||||
|
trap _cleanup_session_lock EXIT INT TERM
|
||||||
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
|
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
|
||||||
echo "[mosaic] Launching Claude Code (active mission detected)..."
|
echo "[mosaic] Launching Claude Code (active mission detected)..."
|
||||||
exec claude --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
|
exec claude --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
|
||||||
@@ -283,6 +326,8 @@ launch_opencode() {
|
|||||||
|
|
||||||
# OpenCode reads from ~/.config/opencode/AGENTS.md
|
# OpenCode reads from ~/.config/opencode/AGENTS.md
|
||||||
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
|
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
|
||||||
|
_write_launcher_session_lock "opencode"
|
||||||
|
trap _cleanup_session_lock EXIT INT TERM
|
||||||
echo "[mosaic] Launching OpenCode..."
|
echo "[mosaic] Launching OpenCode..."
|
||||||
exec opencode "$@"
|
exec opencode "$@"
|
||||||
}
|
}
|
||||||
@@ -298,6 +343,8 @@ launch_codex() {
|
|||||||
|
|
||||||
# Codex reads from ~/.codex/instructions.md
|
# Codex reads from ~/.codex/instructions.md
|
||||||
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
|
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
|
||||||
|
_write_launcher_session_lock "codex"
|
||||||
|
trap _cleanup_session_lock EXIT INT TERM
|
||||||
echo "[mosaic] Launching Codex..."
|
echo "[mosaic] Launching Codex..."
|
||||||
exec codex "$@"
|
exec codex "$@"
|
||||||
}
|
}
|
||||||
@@ -325,6 +372,8 @@ launch_yolo() {
|
|||||||
runtime_prompt="$(build_runtime_prompt "claude")"
|
runtime_prompt="$(build_runtime_prompt "claude")"
|
||||||
|
|
||||||
_detect_mission_prompt
|
_detect_mission_prompt
|
||||||
|
_write_launcher_session_lock "claude"
|
||||||
|
trap _cleanup_session_lock EXIT INT TERM
|
||||||
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
|
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
|
||||||
echo "[mosaic] Launching Claude Code in YOLO mode (active mission detected)..."
|
echo "[mosaic] Launching Claude Code in YOLO mode (active mission detected)..."
|
||||||
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
|
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
|
||||||
@@ -342,6 +391,8 @@ launch_yolo() {
|
|||||||
|
|
||||||
# Codex reads instructions.md from ~/.codex and supports a direct dangerous flag.
|
# Codex reads instructions.md from ~/.codex and supports a direct dangerous flag.
|
||||||
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
|
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
|
||||||
|
_write_launcher_session_lock "codex"
|
||||||
|
trap _cleanup_session_lock EXIT INT TERM
|
||||||
echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
|
echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
|
||||||
exec codex --dangerously-bypass-approvals-and-sandbox "$@"
|
exec codex --dangerously-bypass-approvals-and-sandbox "$@"
|
||||||
;;
|
;;
|
||||||
@@ -354,6 +405,8 @@ launch_yolo() {
|
|||||||
|
|
||||||
# OpenCode defaults to allow-all permissions unless user config restricts them.
|
# OpenCode defaults to allow-all permissions unless user config restricts them.
|
||||||
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
|
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
|
||||||
|
_write_launcher_session_lock "opencode"
|
||||||
|
trap _cleanup_session_lock EXIT INT TERM
|
||||||
echo "[mosaic] Launching OpenCode in YOLO mode..."
|
echo "[mosaic] Launching OpenCode in YOLO mode..."
|
||||||
exec opencode "$@"
|
exec opencode "$@"
|
||||||
;;
|
;;
|
||||||
@@ -469,8 +522,9 @@ _check_resumable_session() {
|
|||||||
local pid
|
local pid
|
||||||
pid="$(jq -r '.pid // 0' "$lock_file" 2>/dev/null)"
|
pid="$(jq -r '.pid // 0' "$lock_file" 2>/dev/null)"
|
||||||
if [[ -n "$pid" ]] && [[ "$pid" != "0" ]] && ! kill -0 "$pid" 2>/dev/null; then
|
if [[ -n "$pid" ]] && [[ "$pid" != "0" ]] && ! kill -0 "$pid" 2>/dev/null; then
|
||||||
echo "[mosaic] Previous orchestration session detected (crashed)."
|
# Stale lock from a dead session — clean it up
|
||||||
echo "[mosaic] Run: mosaic coord resume"
|
rm -f "$lock_file"
|
||||||
|
echo "[mosaic] Cleaned up stale session lock (PID $pid no longer running)."
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
elif [[ -f "$mission_file" ]]; then
|
elif [[ -f "$mission_file" ]]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user