fix(herdr): prevent pane misattribution and prioritize idle-pane reuse in new-session
- Restrict generic agent_kind fallback in _resolve_herdr_pane_id to bare-kind searches - Exclude 'agent' key from exact-match loop to enforce R-1 fail-closed contract - Prioritize reusing existing idle pane on new-session to prevent unbounded pane proliferation - Wrap final_cmd with env prefix to preserve env_flags when reusing idle pane
This commit is contained in:
+36
-3
@@ -29,7 +29,7 @@ export WORKSPACE_ROOT
|
||||
# Framework semantic version. Single runtime source of truth; kept in lockstep
|
||||
# with VERSIONS.md and the 8 SKILL.md frontmatters by tests/test_version_consistency.py.
|
||||
# NOTE: unlike other MAM_* variables this one is intentionally NOT env-overridable.
|
||||
MAM_VERSION="4.1.1"
|
||||
MAM_VERSION="4.1.2"
|
||||
export MAM_VERSION
|
||||
AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}"
|
||||
|
||||
@@ -412,7 +412,7 @@ try:
|
||||
if tws:
|
||||
panes = [p for p in panes if p.get('workspace_id') == tws]
|
||||
# ISSUE-2: exact match first, then agent-sessions.yaml lookup fallback
|
||||
for key in ('label', 'name', 'agent'):
|
||||
for key in ('label', 'name'):
|
||||
for p in panes:
|
||||
v = p.get(key)
|
||||
if v and (v == tn or v == tsa):
|
||||
@@ -433,7 +433,13 @@ try:
|
||||
break
|
||||
except Exception as e:
|
||||
sys.stderr.write(f'warning: yaml load failed: {e}\n')
|
||||
if agent_kind:
|
||||
# BUG-1: only trust the generic agent_kind fallback when the caller's
|
||||
# own target string IS the bare kind (e.g. target == 'agy'). Using a
|
||||
# *derived* kind (looked up via the yaml entry matched by tn/tsa) to
|
||||
# then broadly match any pane of this kind is unsound whenever another,
|
||||
# unrelated same-kind session is alive in the workspace - it misattributes
|
||||
# that unrelated pane as this target even though their names differ.
|
||||
if agent_kind and (tn == agent_kind or tsa == agent_kind):
|
||||
matching = [p.get('pane_id') for p in panes if p.get('agent') == agent_kind and p.get('pane_id')]
|
||||
if len(matching) == 1:
|
||||
print(matching[0])
|
||||
@@ -702,6 +708,32 @@ except Exception:
|
||||
ws_id=""
|
||||
target_pane=""
|
||||
if [ -n "$existing_ws" ]; then
|
||||
# BUG-4: prefer an already-idle (agent-less) pane in the existing
|
||||
# workspace over splitting a new one — avoids unbounded pane
|
||||
# proliferation and 2x2 grid-layout churn when a prior agent's pane
|
||||
# was left behind idle.
|
||||
idle_pane=$(_real_herdr pane list 2>/dev/null | TARGET_WS="$existing_ws" python3 -c "
|
||||
import sys, json, os
|
||||
target_ws = os.environ.get('TARGET_WS', '')
|
||||
try:
|
||||
d = json.loads(sys.stdin.read())
|
||||
panes = d.get('result', {}).get('panes', [])
|
||||
for p in panes:
|
||||
if p.get('workspace_id') == target_ws and not p.get('agent') and p.get('pane_id'):
|
||||
print(p.get('pane_id'))
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$idle_pane" ]; then
|
||||
target_pane="$idle_pane"
|
||||
if [ -n "$final_cmd" ]; then
|
||||
idle_env="${env_flags//--env /}"
|
||||
idle_env="${idle_env:+$idle_env }HERDR_WORKSPACE_ID=$existing_ws"
|
||||
final_cmd="env $idle_env $final_cmd"
|
||||
fi
|
||||
else
|
||||
# W2a: Determine split direction policy via pane layout
|
||||
sample_pane=$(_real_herdr pane list 2>/dev/null | TARGET_WS="$existing_ws" python3 -c "
|
||||
import sys, json, os
|
||||
@@ -752,6 +784,7 @@ except Exception:
|
||||
" 2>/dev/null || echo "")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$existing_ws" ] || [ -z "$target_pane" ]; then
|
||||
ws_json=$(_real_herdr workspace create --cwd "${ws:-.}" ${MAM_WS_LABEL:+--label "$MAM_WS_LABEL"} $env_flags --no-focus 2>/dev/null || echo "")
|
||||
|
||||
Reference in New Issue
Block a user