diff --git a/.agents/scripts/pulse-wrapper.sh b/.agents/scripts/pulse-wrapper.sh
index 73260776e..e7f19562c 100755
--- a/.agents/scripts/pulse-wrapper.sh
+++ b/.agents/scripts/pulse-wrapper.sh
@@ -211,7 +211,13 @@ WRAPPER_LOGFILE="${HOME}/.aidevops/logs/pulse-wrapper.log"
SESSION_FLAG="${HOME}/.aidevops/logs/pulse-session.flag"
STOP_FLAG="${HOME}/.aidevops/logs/pulse-session.stop"
OPENCODE_BIN="${OPENCODE_BIN:-$(command -v opencode 2>/dev/null || echo "opencode")}"
-PULSE_DIR="${PULSE_DIR:-${HOME}/Git/aidevops}"
+# PULSE_DIR: working directory for the supervisor pulse session.
+# Defaults to a neutral workspace path so pulse sessions are not associated
+# with any specific managed repo in the host app's session database.
+# Previously defaulted to ~/Git/aidevops, which caused 155+ orphaned sessions
+# to accumulate under that project even when it had pulse:false (GH#5136).
+# Override via env var if a specific directory is needed.
+PULSE_DIR="${PULSE_DIR:-${HOME}/.aidevops/.agent-workspace}"
PULSE_MODEL="${PULSE_MODEL:-}"
HEADLESS_RUNTIME_HELPER="${HEADLESS_RUNTIME_HELPER:-${SCRIPT_DIR}/headless-runtime-helper.sh}"
REPOS_JSON="${REPOS_JSON:-${HOME}/.config/aidevops/repos.json}"
@@ -226,9 +232,10 @@ if [[ ! -x "$HEADLESS_RUNTIME_HELPER" ]]; then
fi
#######################################
-# Ensure log directory exists
+# Ensure log and workspace directories exist
#######################################
mkdir -p "$(dirname "$PIDFILE")"
+mkdir -p "$PULSE_DIR"
#######################################
# Acquire an exclusive instance lock using mkdir atomicity (GH#4513)
@@ -1842,8 +1849,14 @@ gathered by pulse-wrapper.sh BEFORE this session started."
fi
# Run the provider-aware headless wrapper in background.
- # It alternates direct Anthropic/OpenAI models, persists pulse sessions per
- # provider, and avoids opencode/* gateway models for headless runs.
+ # It alternates direct Anthropic/OpenAI models and avoids opencode/*
+ # gateway models for headless runs.
+ # Session reuse is intentionally DISABLED for pulse role (GH#5136,
+ # headless-runtime-helper.sh line 804-809): each cycle clears the
+ # persisted session ID to avoid stale conversational context contaminating
+ # dispatch decisions. This means each cycle creates a fresh session.
+ # PULSE_DIR uses a neutral workspace path (not a managed repo) so these
+ # sessions don't pollute any project's session list.
local -a pulse_cmd=("$HEADLESS_RUNTIME_HELPER" run --role pulse --session-key supervisor-pulse --dir "$PULSE_DIR" --title "Supervisor Pulse" --agent Automate --prompt "$prompt")
if [[ -n "$PULSE_MODEL" ]]; then
pulse_cmd+=(--model "$PULSE_MODEL")
diff --git a/setup.sh b/setup.sh
index 6eced9737..9792f1e24 100755
--- a/setup.sh
+++ b/setup.sh
@@ -892,10 +892,6 @@ main() {
# - Non-interactive: only installs if config explicitly says true
local wrapper_script="$HOME/.aidevops/agents/scripts/pulse-wrapper.sh"
local pulse_label="com.aidevops.aidevops-supervisor-pulse"
- local _aidevops_dir _pulse_repo_dir
- _aidevops_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- _pulse_repo_dir=$(_resolve_main_worktree_dir "$_aidevops_dir")
-
# Read explicit user consent from config.jsonc (not merged defaults).
# Empty = user never configured this; "true"/"false" = explicit choice.
local _pulse_user_config=""
@@ -1009,12 +1005,14 @@ main() {
# XML-escape paths for safe plist embedding (prevents injection
# if $HOME or paths contain &, <, > characters)
- local _xml_wrapper_script _xml_home _xml_opencode_bin _xml_aidevops_dir _xml_path
+ local _xml_wrapper_script _xml_home _xml_opencode_bin _xml_pulse_dir _xml_path
local _headless_xml_env=""
_xml_wrapper_script=$(_xml_escape "$wrapper_script")
_xml_home=$(_xml_escape "$HOME")
_xml_opencode_bin=$(_xml_escape "$opencode_bin")
- _xml_aidevops_dir=$(_xml_escape "$_pulse_repo_dir")
+ # Use neutral workspace path for PULSE_DIR so supervisor sessions
+ # are not associated with any specific managed repo (GH#5136).
+ _xml_pulse_dir=$(_xml_escape "${HOME}/.aidevops/.agent-workspace")
_xml_path=$(_xml_escape "$PATH")
if [[ -n "${AIDEVOPS_HEADLESS_MODELS:-}" ]]; then
local _xml_headless_models
@@ -1061,7 +1059,7 @@ main() {
OPENCODE_BIN
${_xml_opencode_bin}
PULSE_DIR
- ${_xml_aidevops_dir}
+ ${_xml_pulse_dir}
PULSE_STALE_THRESHOLD
1800
${_headless_xml_env}
@@ -1092,8 +1090,9 @@ PLIST
# PATH= here, it overrides the global line and breaks nvm/bun/cargo.
# OPENCODE_BIN removed — resolved from PATH at runtime via command -v.
# See #4099 and #4240 for history.
- local _cron_aidevops_dir _cron_wrapper_script _cron_headless_env=""
- _cron_aidevops_dir=$(_cron_escape "$_pulse_repo_dir")
+ local _cron_pulse_dir _cron_wrapper_script _cron_headless_env=""
+ # Use neutral workspace path for PULSE_DIR (GH#5136)
+ _cron_pulse_dir=$(_cron_escape "${HOME}/.aidevops/.agent-workspace")
_cron_wrapper_script=$(_cron_escape "$wrapper_script")
if [[ -n "${AIDEVOPS_HEADLESS_MODELS:-}" ]]; then
local _cron_headless_models
@@ -1107,7 +1106,7 @@ PLIST
fi
(
crontab -l 2>/dev/null | grep -v 'aidevops: supervisor-pulse'
- echo "*/2 * * * * PULSE_DIR=${_cron_aidevops_dir}${_cron_headless_env} /bin/bash ${_cron_wrapper_script} >> \"\$HOME/.aidevops/logs/pulse-wrapper.log\" 2>&1 # aidevops: supervisor-pulse"
+ echo "*/2 * * * * PULSE_DIR=${_cron_pulse_dir}${_cron_headless_env} /bin/bash ${_cron_wrapper_script} >> \"\$HOME/.aidevops/logs/pulse-wrapper.log\" 2>&1 # aidevops: supervisor-pulse"
) | crontab - || true
if crontab -l 2>/dev/null | grep -qF "aidevops: supervisor-pulse"; then
print_info "Supervisor pulse enabled (cron, every 2 min). Disable: crontab -e and remove the supervisor-pulse line"