fix(opencode): the Linux check fixes for opencode 2.x (Issue #60) #62
No files matched your search
@@ -239,7 +239,7 @@ graceful_stop() {
|
||||
if [ "$exitkey" = "Ctrl+C" ] || [ "$exitkey" = "C-c" ]; then
|
||||
herdr send-keys -t "$SESSION_NAME" C-c 2>/dev/null || true
|
||||
else
|
||||
send_keys_safe "$SESSION_NAME" "$exitkey" "stop$$" || echo "graceful: safe delivery failed (rc=$?) — falling back to kill chain"
|
||||
send_keys_safe "$SESSION_NAME" "$exitkey" "stop$$" || echo "graceful: delivery of '$exitkey' unconfirmed (rc=$?) — waiting for the session to exit"
|
||||
fi
|
||||
_wait_session_gone "$SESSION_NAME" 5 || true
|
||||
if ! herdr has-session -t "$SESSION_NAME" 2>/dev/null; then
|
||||
|
||||
@@ -7,14 +7,18 @@
|
||||
the sidecars on close; a plain mode=ro open creates missing ones).
|
||||
6. `debug paths` (which writes a line to opencode's log) runs once per
|
||||
(binary, HOME, XDG_DATA_HOME) in a process.
|
||||
2. A graceful stop whose `/exit` worked says so: the agent exits instead of
|
||||
turning `working`, so the delivery is unconfirmed, and no kill step ran.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -213,3 +217,63 @@ def test_a_failed_debug_paths_is_not_remembered(tmp_path, monkeypatch):
|
||||
default = str(tmp_path / "home" / ".local" / "share" / "opencode" / "opencode.db")
|
||||
assert [adapter._resolve_db(ctx) for _ in range(2)] == [default] * 2
|
||||
assert log.read_text().splitlines() == ["debug paths"] * 2
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. the stop's words match what happened
|
||||
# ---------------------------------------------------------------------------
|
||||
SESSION = "i60-stop-opencode"
|
||||
|
||||
# A herdr in front of the conftest mock that, like a real opencode 2.x pane
|
||||
# (Linux check 3-3), ends the agent on `/exit`: the prompt's wait for
|
||||
# `working` then fails because the agent is gone.
|
||||
EXITING_HERDR = """#!/usr/bin/env python3
|
||||
import json, os, sys
|
||||
args = sys.argv[1:]
|
||||
if "prompt" in args and "/exit" in args:
|
||||
with open(@STATE@) as f:
|
||||
state = json.load(f)
|
||||
state["agents"].pop(@NAME@, None)
|
||||
state.setdefault("calls", []).append("herdr " + " ".join(args))
|
||||
with open(@STATE@, "w") as f:
|
||||
json.dump(state, f)
|
||||
sys.stderr.write(json.dumps({"error": {"code": "agent_exited", "message": "agent exited"}}) + "\\n")
|
||||
sys.exit(1)
|
||||
os.execv(@MOCK@, [@MOCK@] + args)
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exits", [True, False])
|
||||
def test_the_graceful_stop_reports_what_happened(mam_sandbox, mock_herdr, mock_agents, monkeypatch, exits):
|
||||
state = json.loads(Path(mock_herdr).read_text())
|
||||
state["agents"][SESSION] = {"status": "running", "agent": "opencode", "agent_status": "idle",
|
||||
"cwd": str(mam_sandbox), "pid": 12345, "pane_id": "w1:p1",
|
||||
"command": "opencode", "buffer": "Ask anything"}
|
||||
Path(mock_herdr).write_text(json.dumps(state, indent=2))
|
||||
lib = mam_sandbox / ".agents" / "skills" / "lib.sh"
|
||||
row = ("d['herdr_sessions'] = [{'name': '%s', 'agent': 'opencode', 'status': 'running', "
|
||||
"'pane': {'cwd': '%s', 'pid': 12345, 'cmd': 'opencode'}}]" % (SESSION, mam_sandbox))
|
||||
res = subprocess.run(["bash", "-c", f"source {lib} && atomic_dump_yaml {mam_sandbox / '.mam' / 'agent-sessions.yaml'}"],
|
||||
input=row, capture_output=True, text=True)
|
||||
assert res.returncode == 0, res.stderr
|
||||
if exits:
|
||||
wrap = mam_sandbox / "exitbin" / "herdr"
|
||||
wrap.parent.mkdir()
|
||||
wrap.write_text(EXITING_HERDR.replace("@STATE@", repr(str(mock_herdr))).replace("@NAME@", repr(SESSION))
|
||||
.replace("@MOCK@", repr(shutil.which("herdr"))))
|
||||
wrap.chmod(0o755)
|
||||
monkeypatch.setenv("PATH", f"{wrap.parent}:{os.environ['PATH']}")
|
||||
stop = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-stop" / "scripts" / "stop_session.sh"
|
||||
res = subprocess.run(["bash", str(stop), "--session", SESSION, "--agent", "opencode"],
|
||||
capture_output=True, text=True, timeout=180)
|
||||
out = res.stdout + res.stderr
|
||||
assert res.returncode == 0, out
|
||||
calls = json.loads(Path(mock_herdr).read_text()).get("calls", [])
|
||||
assert any("prompt" in c and "/exit" in c for c in calls), calls
|
||||
assert "falling back to kill chain" not in out
|
||||
if exits:
|
||||
assert "unconfirmed" in out and "graceful: exited cleanly" in out, out
|
||||
assert "kill-session" not in out and not any("kill-session" in c for c in calls), out
|
||||
else:
|
||||
# The kill step runs, so it is named.
|
||||
assert "still alive → kill-session" in out, out
|
||||
Reference in new issue
Block a user