fix(agents): harden shell adapter bridge and address double-check review feedback

- create_session.sh: add explicit case fallback for delegate_agent (R1)
- lib_py.agents: add spawn-spec, resume-spec, exit-key argv CLI subcommands (R2)
- resume_session.sh, stop_session.sh: replace python string interpolation with safe argv subcommands (R2)
- stop_session.sh: dynamically iterate adapter.identity_cache_fields (R3)
- verify_session.py, workspace_uuid.py: remove dead imports (R9/N4)
- tests/test_a4_adapter_contract.py: add CLI bridge subcommand, clean-env PYTHONPATH safety, and fallback contract tests (R12/N1)
- SKILL.md, docs, logs: synchronize IMPROVEMENTS.md, LOG.md, and resolve_session_id wording (R8/R10/R11)
- promote verified peer review reports for cline (e7b9812b) and claude (31730364)
This commit is contained in:
2026-08-17 08:54:57 +09:00
parent 7708d3ade3
commit 5ed39f899b
14 changed files with 615 additions and 31 deletions
+65
View File
@@ -258,3 +258,68 @@ def test_adapter_discover(tmp_path):
with open(f"{cl_sess}/u-cl1.json", 'w') as f:
f.write('{"session_id": "u-cl1", "cwd": "' + ws + '"}')
assert cline.discover(ctx_cl) == ['u-cl1']
def test_cli_bridge_subcommands_and_quote_safety():
import subprocess, sys
from pathlib import Path
env = os.environ.copy()
skills_dir = str(Path(__file__).resolve().parent.parent / ".agents" / "skills")
env["PYTHONPATH"] = f"{skills_dir}:{env.get('PYTHONPATH', '')}"
# 1. spawn-spec
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "spawn-spec", "claude", "/path with spaces/claude", "uuid-test", "0"], capture_output=True, text=True, env=env)
assert res.returncode == 0
assert res.stdout.strip() == "/path with spaces/claude --dangerously-skip-permissions --session-id uuid-test"
# 2. resume-spec with single quotes in workspace path
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "resume-spec", "claude", "/bin/claude", "uuid-test", "/tmp/bob's ws"], capture_output=True, text=True, env=env)
assert res.returncode == 0
assert res.stdout.strip() == "/bin/claude --dangerously-skip-permissions --session-id uuid-test"
# 3. exit-key
for agent, expected_key in [('claude', '/exit'), ('agy', 'Exit'), ('hermes', '/exit'), ('cline', '/exit')]:
res = subprocess.run([sys.executable, "-m", "lib_py.agents", "exit-key", agent], capture_output=True, text=True, env=env)
assert res.returncode == 0
assert res.stdout.strip() == expected_key
def test_delegate_agent_resolution_and_fallback():
import subprocess
expected_map = {
'claude': 'claude-code',
'agy': 'antigravity-cli',
'hermes': 'hermes-agent',
'cline': 'cline-agent',
}
# 1. Adapter property
for agent, expected_key in expected_map.items():
adapter = get_adapter(agent)
assert adapter.delegate_agent_key == expected_key
# 2. Shell fallback resolution when MAM_DELEGATE_AGENT_KEY is unset (R1 fallback)
for agent, expected_key in expected_map.items():
sh_snippet = f'''
AGENT="{agent}"
MAM_DELEGATE_AGENT_KEY=""
delegate_agent="${{MAM_DELEGATE_AGENT_KEY:-}}"
if [ -z "$delegate_agent" ]; then
case "$AGENT" in
claude) delegate_agent="claude-code" ;;
hermes) delegate_agent="hermes-agent" ;;
cline) delegate_agent="cline-agent" ;;
agy) delegate_agent="antigravity-cli" ;;
*) echo "ERROR: cannot resolve delegate agent key for '$AGENT'" >&2; exit 2 ;;
esac
fi
echo "$delegate_agent"
'''
res = subprocess.run(["bash", "-c", sh_snippet], capture_output=True, text=True)
assert res.returncode == 0
assert res.stdout.strip() == expected_key
def test_wait_for_tui_ready_missing_tokens_diagnostic(mam_sandbox):
import subprocess
lib_sh = mam_sandbox / "skills" / "lib.sh"
cmd = f'source "{lib_sh}" && wait_for_tui_ready "dummy-sess" "bogus-agent"'
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True)
assert res.returncode != 0
assert "no ready tokens for agent 'bogus-agent'" in res.stderr