fix(lib): prevent set -u unbound variable error for final_cmd in kind detection and harden test fixtures
This commit is contained in:
@@ -271,11 +271,11 @@ print('\t'.join(env_flags) + '\n' + ' '.join(binary_tokens))
|
|||||||
kind="claude"
|
kind="claude"
|
||||||
elif echo "$name" | grep -qi "hermes"; then
|
elif echo "$name" | grep -qi "hermes"; then
|
||||||
kind="hermes"
|
kind="hermes"
|
||||||
elif echo "$final_cmd" | grep -qi "agy"; then
|
elif echo "${final_cmd:-}" | grep -qi "agy"; then
|
||||||
kind="agy"
|
kind="agy"
|
||||||
elif echo "$final_cmd" | grep -qi "claude"; then
|
elif echo "${final_cmd:-}" | grep -qi "claude"; then
|
||||||
kind="claude"
|
kind="claude"
|
||||||
elif echo "$final_cmd" | grep -qi "hermes"; then
|
elif echo "${final_cmd:-}" | grep -qi "hermes"; then
|
||||||
kind="hermes"
|
kind="hermes"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+26
-7
@@ -87,12 +87,12 @@ if not os.path.exists(state_file):
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Lock the state file exclusively to prevent concurrent race conditions
|
# Lock the state file exclusively to prevent concurrent race conditions
|
||||||
lock_f = open(state_file + ".lock", "w")
|
lock_f = open(state_file + ".lock", "a")
|
||||||
fcntl.flock(lock_f, fcntl.LOCK_EX)
|
fcntl.flock(lock_f, fcntl.LOCK_EX)
|
||||||
|
|
||||||
state = {"workspaces": [], "agents": {}, "calls": []}
|
state = {"workspaces": [], "agents": {}, "calls": []}
|
||||||
if os.path.exists(state_file):
|
if os.path.exists(state_file):
|
||||||
for _retry in range(10):
|
for _retry in range(50):
|
||||||
try:
|
try:
|
||||||
with open(state_file, 'r') as f:
|
with open(state_file, 'r') as f:
|
||||||
content = f.read().strip()
|
content = f.read().strip()
|
||||||
@@ -100,16 +100,35 @@ if os.path.exists(state_file):
|
|||||||
state = json.loads(content)
|
state = json.loads(content)
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
import time
|
pass
|
||||||
time.sleep(0.05)
|
time.sleep(0.02)
|
||||||
|
|
||||||
# Record the command call
|
# Record the command call
|
||||||
state["calls"].append(sys.argv[1:])
|
state["calls"].append(sys.argv[1:])
|
||||||
|
|
||||||
def save_state():
|
def save_state():
|
||||||
|
disk_state = {"workspaces": [], "agents": {}, "calls": []}
|
||||||
|
if os.path.exists(state_file):
|
||||||
|
for _retry in range(50):
|
||||||
|
try:
|
||||||
|
with open(state_file, 'r') as f:
|
||||||
|
content = f.read().strip()
|
||||||
|
if content:
|
||||||
|
disk_state = json.loads(content)
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(0.02)
|
||||||
|
disk_state["agents"] = state.get("agents", {})
|
||||||
|
if "workspaces" in state:
|
||||||
|
disk_state["workspaces"] = state["workspaces"]
|
||||||
|
disk_calls = disk_state.setdefault("calls", [])
|
||||||
|
if sys.argv[1:] and (not disk_calls or disk_calls[-1] != sys.argv[1:]):
|
||||||
|
disk_calls.append(sys.argv[1:])
|
||||||
|
|
||||||
tmp_state = state_file + f".tmp.{os.getpid()}"
|
tmp_state = state_file + f".tmp.{os.getpid()}"
|
||||||
with open(tmp_state, 'w') as f:
|
with open(tmp_state, 'w') as f:
|
||||||
json.dump(state, f, indent=2)
|
json.dump(disk_state, f, indent=2)
|
||||||
f.flush()
|
f.flush()
|
||||||
os.fsync(f.fileno())
|
os.fsync(f.fileno())
|
||||||
os.replace(tmp_state, state_file)
|
os.replace(tmp_state, state_file)
|
||||||
@@ -242,7 +261,7 @@ elif cmd1 == "agent":
|
|||||||
"cwd": cwd or "TMP_PATH_PLACEHOLDER",
|
"cwd": cwd or "TMP_PATH_PLACEHOLDER",
|
||||||
"workspace_id": ws or "w1",
|
"workspace_id": ws or "w1",
|
||||||
"pid": 9999,
|
"pid": 9999,
|
||||||
"pane_id": f"w1:p{len(agents)+1}",
|
"pane_id": f"w1:p_{name}",
|
||||||
"command": " ".join(agent_cmd),
|
"command": " ".join(agent_cmd),
|
||||||
"buffer": buffer_content
|
"buffer": buffer_content
|
||||||
}
|
}
|
||||||
@@ -376,7 +395,7 @@ elif cmd1 == "agent":
|
|||||||
agents = state.get("agents", {})
|
agents = state.get("agents", {})
|
||||||
if name in agents:
|
if name in agents:
|
||||||
agents[name]["sent_text"] = agents[name].get("sent_text", "") + text
|
agents[name]["sent_text"] = agents[name].get("sent_text", "") + text
|
||||||
if text == "C-m":
|
if text in ("C-m", "Enter"):
|
||||||
agents[name]["buffer"] = agents[name].get("buffer", "") + "\\nesc to interrupt"
|
agents[name]["buffer"] = agents[name].get("buffer", "") + "\\nesc to interrupt"
|
||||||
else:
|
else:
|
||||||
agents[name]["buffer"] = agents[name].get("buffer", "") + "\\n" + text
|
agents[name]["buffer"] = agents[name].get("buffer", "") + "\\n" + text
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import fcntl
|
||||||
import pytest
|
import pytest
|
||||||
import shutil
|
import shutil
|
||||||
import yaml
|
import yaml
|
||||||
@@ -117,19 +118,23 @@ def test_integration_stop_purge_combination(mam_sandbox, mock_herdr, mock_agents
|
|||||||
assert jsonl_file.exists()
|
assert jsonl_file.exists()
|
||||||
|
|
||||||
# Update mock herdr's state to match the running agent so the stop kill-chain works gracefully
|
# Update mock herdr's state to match the running agent so the stop kill-chain works gracefully
|
||||||
with open(mock_herdr, 'r') as f:
|
lock_path = str(mock_herdr) + ".lock"
|
||||||
state = json.load(f)
|
with open(lock_path, 'a') as lock_f:
|
||||||
state["agents"][session_name] = {
|
fcntl.flock(lock_f, fcntl.LOCK_EX)
|
||||||
"status": "running",
|
with open(mock_herdr, 'r') as f:
|
||||||
"agent": "claude",
|
state = json.load(f)
|
||||||
"cwd": str(tmp_path),
|
state["agents"][session_name] = {
|
||||||
"pid": 12345,
|
"status": "running",
|
||||||
"pane_id": "w1:p1",
|
"agent": "claude",
|
||||||
"command": "claude",
|
"cwd": str(tmp_path),
|
||||||
"buffer": "Anthropic Claude Ready"
|
"pid": 12345,
|
||||||
}
|
"pane_id": f"w1:p_{session_name}",
|
||||||
with open(mock_herdr, 'w') as f:
|
"command": "claude",
|
||||||
json.dump(state, f, indent=2)
|
"buffer": "Anthropic Claude Ready"
|
||||||
|
}
|
||||||
|
with open(mock_herdr, 'w') as f:
|
||||||
|
json.dump(state, f, indent=2)
|
||||||
|
fcntl.flock(lock_f, fcntl.LOCK_UN)
|
||||||
|
|
||||||
# 2. Calling stop_session without --yes fails for --purge-conversation
|
# 2. Calling stop_session without --yes fails for --purge-conversation
|
||||||
stop_script = tmp_path / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh"
|
stop_script = tmp_path / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh"
|
||||||
|
|||||||
@@ -295,18 +295,21 @@ d['herdr_sessions'] = [
|
|||||||
'name': '{worker_name}',
|
'name': '{worker_name}',
|
||||||
'status': 'running',
|
'status': 'running',
|
||||||
'role': 'worker',
|
'role': 'worker',
|
||||||
|
'claude_session_id_own': '11111111-1111-4111-a111-111111111111',
|
||||||
'pane': {{'cwd': 'WS_PLACEHOLDER'}}
|
'pane': {{'cwd': 'WS_PLACEHOLDER'}}
|
||||||
}},
|
}},
|
||||||
{{
|
{{
|
||||||
'name': '{reviewer_name}',
|
'name': '{reviewer_name}',
|
||||||
'status': 'running',
|
'status': 'running',
|
||||||
'role': 'reviewer',
|
'role': 'reviewer',
|
||||||
|
'claude_session_id_own': '22222222-2222-4222-a222-222222222222',
|
||||||
'pane': {{'cwd': 'WS_PLACEHOLDER'}}
|
'pane': {{'cwd': 'WS_PLACEHOLDER'}}
|
||||||
}},
|
}},
|
||||||
{{
|
{{
|
||||||
'name': '{planner_name}',
|
'name': '{planner_name}',
|
||||||
'status': 'running',
|
'status': 'running',
|
||||||
'role': 'planner',
|
'role': 'planner',
|
||||||
|
'claude_session_id_own': '33333333-3333-4333-a333-333333333333',
|
||||||
'pane': {{'cwd': 'WS_PLACEHOLDER'}}
|
'pane': {{'cwd': 'WS_PLACEHOLDER'}}
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user