diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-30 17:27:21 +0200 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-30 17:27:21 +0200 |
| commit | b0cfd10b80443d3f67d263ff4db177bb53cc90ed (patch) | |
| tree | 957951350434f38da83457f5467b6c000e892867 /tests/pi-live.sh | |
| parent | 91436536b69c10aa333c2b2978284b57cca3ed64 (diff) | |
reload pi agents on an old extension: versioned extension, control socket, menu item and reload-agents
Diffstat (limited to 'tests/pi-live.sh')
| -rwxr-xr-x | tests/pi-live.sh | 68 |
1 files changed, 66 insertions, 2 deletions
diff --git a/tests/pi-live.sh b/tests/pi-live.sh index 059a92b..4ea4caf 100755 --- a/tests/pi-live.sh +++ b/tests/pi-live.sh @@ -9,7 +9,8 @@ # Covers: identification, the extension's state and session reports, a turn # (working, then done), /reload, an extension's blocked event, /importance, /unread, # screen detection for a pi without the extension, restore (`pi --session`, -# importance included), exit. +# importance included), reloading an agent on an old extension (refused +# while busy or with a draft), exit. set -u PI_BIN=$(cd "$(dirname "$1")" && pwd)/$(basename "$1") ROOT=$(cd "$(dirname "$0")/.." && pwd) @@ -35,7 +36,7 @@ cleanup() { t kill-server 2>/dev/null [ -n "${DAEMON_PID:-}" ] && kill "$DAEMON_PID" 2>/dev/null [ -n "${FAKE_PID:-}" ] && kill "$FAKE_PID" 2>/dev/null - rm -rf "$D" + if [ -n "${PI_LIVE_KEEP:-}" ]; then echo "kept $D"; else rm -rf "$D"; fi } trap cleanup EXIT @@ -72,6 +73,12 @@ export PATH="$D/bin:$PATH" PI_CODING_AGENT_DIR="$D/agent" PI_OFFLINE=1 PI_SKIP_V export DINGGER_TMUX_SOCKET="$R" DINGGER_AGENT_SOCKET="$D/hook.sock" DINGGER_STATE_DIR="$D/state" DINGGER_TMUX_CONFIG="$D/tmux.conf" t -f "$D/tmux.conf" new-session -d -s main -n agent -x 140 -y 40 -c "$D/work" t new-window -d -t main: -n plain -c "$D/work2" +EXT="$D/agent/extensions/dingger-agent-state.ts" +stamp() { grep -o 'extensionVersion = "[0-9a-f]*"' "$EXT" | cut -d'"' -f2; } +CURRENT=$(stamp) +# An installed extension of another version: the daemon updates it as it +# starts (a pi started after that loads the current one). +sed -i '' "s/extensionVersion = \"$CURRENT\"/extensionVersion = \"0000000000000000\"/" "$EXT" "$DINGGER" watch --interval-secs 1 --no-sounds --no-notifications >"$D/daemon.log" 2>&1 & DAEMON_PID=$! t send-keys -t main:agent "command -v pi > $D/which-pi" Enter @@ -84,6 +91,10 @@ NO_MCP=$("$PI_BIN" --help 2>&1 | grep -q -- --no-mcp && echo --no-mcp) st() { t show -pv -t "main:$1" @agent_state 2>/dev/null; } type_line() { t send-keys -t "main:$1" -l "$2"; sleep 0.3; t send-keys -t "main:$1" Enter; } +echo "0. the daemon updates the installed extension" +expect "to its own version" "stamp" "$CURRENT" 30 +[ ${#CURRENT} -eq 16 ] && pass "a 16-digit version" || fail "version [$CURRENT]" + echo "1. the extension's reports" t send-keys -t main:agent "clear; PI_CODING_AGENT_DIR=$D/agent pi $NO_MCP" Enter expect "identified as pi" "t show -pv -t main:agent @agent 2>/dev/null" pi 150 @@ -123,6 +134,59 @@ expect "/unread: done again" "st agent" done 50 expect "by a mark" "sed 's/\x1b\[[0-9;]*m//g' $D/daemon.log | grep -c 'mark unread target=main:agent applied=true'" 1 30 expect "pi says so" "t capture-pane -p -t main:agent | grep -c 'marked unread: dingger shows it as done'" 1 30 +echo "1b. reloading an agent on an old extension" +ctl() { t show -pv -t main:agent @dingger_control 2>/dev/null; } +ask_reload() { python3 -c ' +import socket, sys +s = socket.socket(socket.AF_UNIX); s.settimeout(3); s.connect(sys.argv[1]) +s.sendall(b"{\"method\":\"reload\"}\n"); print(s.makefile().readline().strip())' "$(ctl)"; } +reloads() { sed 's/\x1b\[[0-9;]*m//g' $D/daemon.log | grep -c "reload: $1"; } +expect "the agent reports the current version" "t show -pv -t main:agent @dingger_extension 2>/dev/null" "$CURRENT" +expect "and a control socket" "[ -S \"\$(ctl)\" ] && echo yes" yes +expect "nothing to reload" "\"$DINGGER\" reload-agents" "every pi agent runs the current dingger extension" 1 +# Run an old one: change the installed file, /reload by hand, then install +# the current one again (what a dingger update does). +sed -i '' "s/extensionVersion = \"$CURRENT\"/extensionVersion = \"0000000000000000\"/" "$EXT" +type_line agent "/reload" +expect "after /reload, it reports the old version" "t show -pv -t main:agent @dingger_extension 2>/dev/null" 0000000000000000 80 +expect "its new instance has a socket of its own" "[ -S \"\$(ctl)\" ] && echo yes" yes +PI_CODING_AGENT_DIR="$D/agent" "$DINGGER" install pi >/dev/null +expect "the daemon counts it outdated" "t show -gv @dingger_outdated 2>/dev/null" 1 30 +SOCK1=$(ctl) +# Reloading cannot help while the installed file is not the current one. +cp "$EXT" "$D/current.ts"; echo "// edited" >>"$EXT" +expect "reload-agents refuses: install first" "\"$DINGGER\" reload-agents 2>&1 | grep -c 'run .dingger install pi. first'" 1 1 +python3 -c 'import socket,sys; s=socket.socket(socket.AF_UNIX); s.connect(sys.argv[1]); s.sendall(b"{\"method\":\"agents.reload_outdated\"}\n"); s.recv(64)' "$D/hook.sock" +expect "and so does the daemon, asked directly" "sed 's/\x1b\[[0-9;]*m//g' $D/daemon.log | grep -c 'reload agents: the installed pi extension is not'" 1 30 +sleep 1.5 +expect "without reloading it" "reloads reloading" 0 1 +cp "$D/current.ts" "$EXT" +# The extension's own checks, asked directly. +type_line agent "slow 4 busy turn" +expect "working" "st agent" working 50 +expect "busy: it refuses" "ask_reload" '{"ok":false,"reason":"busy"}' +expect "and the turn ends" "st agent" done 80 +t send-keys -t main:agent -l "half typed" +sleep 0.5 +expect "a draft: it refuses" "ask_reload" '{"ok":false,"reason":"draft"}' +# Through dingger: refused for the draft, retried until it can. +"$DINGGER" reload-agents >"$D/reload.out" +expect "reload-agents: not yet" "head -1 $D/reload.out" "reloaded 0 of 1 agent on an old dingger extension" +expect "it says why it waits" "sed -n 2p $D/reload.out" "the other 1 reload once idle and off screen, with an empty input box" +expect "the daemon was told no: a draft" "sed 's/\x1b\[[0-9;]*m//g' $D/daemon.log | grep -c 'reload: not now target=main:agent reason=draft'" 1 30 +expect "the draft is untouched" "t capture-pane -p -t main:agent | grep -c 'half typed'" 1 +for _ in $(seq 10); do t send-keys -t main:agent BSpace; done +expect "once the input box is empty: reloaded" "t show -pv -t main:agent @dingger_extension 2>/dev/null" "$CURRENT" 150 +expect "by the daemon" "reloads reloading" 1 30 +expect "a new instance (a new socket)" "[ \"\$(ctl)\" != \"$SOCK1\" ] && [ -S \"\$(ctl)\" ] && echo yes" yes +expect "the old socket is gone" "[ -e \"$SOCK1\" ] && echo left || echo gone" gone +expect "nothing left to reload" "t show -gv @dingger_outdated 2>/dev/null" 0 30 +expect "still done (unseen)" "st agent" done +expect "no message reached the model" "grep -c dingger-reload \"\$(t show -pv -t main:agent @agent_session)\"" 0 +type_line agent "slow 1 after" +expect "it still works: working" "st agent" working 50 +expect "and done" "st agent" done 60 + echo "2. screen detection (pi without the extension)" t send-keys -t main:plain "PI_CODING_AGENT_DIR=$D/agent-plain pi $NO_MCP" Enter expect "identified as pi" "t show -pv -t main:plain @agent 2>/dev/null" pi 150 |
