diff options
Diffstat (limited to 'tests/e2e.sh')
| -rwxr-xr-x | tests/e2e.sh | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/tests/e2e.sh b/tests/e2e.sh index e73dc73..e5b5865 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -32,6 +32,8 @@ PASSES=0 export DINGGER_TMUX_SOCKET="$SOCK" DINGGER_AGENT_SOCKET="$HOOK" DINGGER_STATE_DIR="$DIR/state" # The daemon updates an installed pi extension when it starts: not the user's. export PI_CODING_AGENT_DIR="$DIR/pi-agent" +# What pi agents cost: the daemons read session files here every 0.3 s. +export DINGGER_COST_SCAN_SECS=0.3 t() { tmux -u -L "$SOCK" "$@"; } @@ -260,14 +262,14 @@ if [ -x "$MENUBAR" ]; then expect "A1 done" "opt $A1 @agent_state" done expect "menu title" "mb --print | head -1" "title: ×1 ✓1" expect "menu rows: herdr priority; idle most recent first (C1 since its jump in step 4, B1 since the start)" \ - "mb --print | tail -n +2 | grep -v '^⟳' | cut -f1,2 | tr '\t\n' '|;'" "× blocked|pi;✓ done|Deploy the widget;○ idle|pi;○ idle|Review PR 42;" + "mb --print | tail -n +2 | grep -v '^⟳\|^\$ ' | cut -f1,2 | tr '\t\n' '|;'" "× blocked|pi;✓ done|Deploy the widget;○ idle|pi;○ idle|Review PR 42;" # Wait for working: B1 was idle before, so idle alone would not show the turn. hook "$B1" working expect "B1 working" "opt $B1 @agent_state" working hook "$B1" idle expect "B1 finishes a turn while watched: idle" "opt $B1 @agent_state" idle expect "and is now the most recent idle agent: listed first" \ - "mb --print | tail -n +2 | grep -v '^⟳' | cut -f1,2 | tr '\t\n' '|;'" "× blocked|pi;✓ done|Deploy the widget;○ idle|Review PR 42;○ idle|pi;" + "mb --print | tail -n +2 | grep -v '^⟳\|^\$ ' | cut -f1,2 | tr '\t\n' '|;'" "× blocked|pi;✓ done|Deploy the widget;○ idle|Review PR 42;○ idle|pi;" expect "subtitle: location, harness, waiting time" "mb --print | grep 'Deploy the widget' | cut -f3" "a · wa1 · pi · done now" expect "idle subtitle has no waiting time" "mb --print | grep 'Review PR 42' | cut -f3" "b · wb1 · pi" mb --click "$A1" @@ -516,6 +518,7 @@ in_snapshot() { python3 -c 'import sys; print(sys.argv[2] in open(sys.argv[1]).r expect "snapshot: the agent's session" "in_snapshot '\"session\": \"$W/sessions/a b.jsonl\"'" True 80 expect "snapshot: a running command's exact arguments" "in_snapshot \"'sleep 100000; :' 'two words'\"" True 80 expect "snapshot: the zsh hook's text" "in_snapshot '\"command\": \"slp 200000 | cat\"'" True 80 +expect "snapshot: the other agent's session" "in_snapshot gone.jsonl" True 80 # An agent that exits just before tmux dies (a shutdown stops programs first). kill "$(ps -o tpgid= -p "$(r display -p -t "$GONE" '#{pane_pid}')" | tr -d ' ')" expect "the exited agent is noticed" "r show -pv -t $GONE @agent 2>/dev/null" "" @@ -1099,6 +1102,48 @@ if command -v node >/dev/null; then fi t kill-session -t dr +# --- 25. what agents cost ----------------------------------------------------- +# The daemon reads pi's session files: each reply once, for the session it +# first appeared in (a fork copies its parent's), closed agents included. +echo "25. what agents cost" +SESS="$PI_CODING_AGENT_DIR/sessions/--work--" +mkdir -p "$SESS" +reply() { # id cost [days ago] + python3 -c 'import json,sys,time; print(json.dumps({"type":"message","id":sys.argv[1],"message":{"role":"assistant","timestamp":int((time.time()-86400*float(sys.argv[3]))*1000),"usage":{"cost":{"total":float(sys.argv[2])}}}}))' "$1" "$2" "${3:-0}" +} +header() { echo "{\"type\":\"session\",\"timestamp\":\"$1\"}"; } +{ header 2026-01-01T00:00:00Z; reply p0 100 40; reply p1 1.25; reply p2 0.25; } >"$SESS/parent.jsonl" +# The fork: its parent's entries, then its own. +{ header 2026-01-02T00:00:00Z; tail -n +2 "$SESS/parent.jsonl"; reply f1 0.5; } >"$SESS/fork.jsonl" +expect "today, each reply once (not 40 days ago)" "gopt @dingger_cost_today" 2.00 50 +expect "this week" "gopt @dingger_cost_week" 2.00 +expect "this month" "gopt @dingger_cost_month" 2.00 +session_report() { # pane session-file + python3 - "$HOOK" "$1" "$2" <<'PY' +import json, socket, sys +params = {"pane_id": sys.argv[2], "agent": "pi", "state": "idle", "agent_session_path": sys.argv[3]} +s = socket.socket(socket.AF_UNIX) +s.connect(sys.argv[1]) +s.sendall((json.dumps({"method": "pane.report_agent", "params": params}) + "\n").encode()) +s.close() +PY +} +t new-session -d -s cost -n c1 -c "$DIR" "$DIR/bin/pi" +t new-window -d -t cost: -n c2 -c "$DIR" "$DIR/bin/pi" +C1=$(t display -p -t cost:c1 '#{pane_id}'); C2=$(t display -p -t cost:c2 '#{pane_id}') +session_report "$C1" "$SESS/parent.jsonl"; session_report "$C2" "$SESS/fork.jsonl" +expect "an agent: what its session spent today" "opt $C1 @dingger_cost" 1.50 +expect "a fork: only its own replies" "opt $C2 @dingger_cost" 0.50 +expect "the menu's top line" "mb --print | grep '^\\$ '" '$ $2.00 today · $2.00 this week · $2.00 this month' +expect "an agent's line" "mb --print | grep -F 'cost · c1' | cut -f3" "cost · c1 · pi · \$1.50" +expect "dingger status: per agent" "$DINGGER status | grep -F 'cost · c2' | grep -c ' \$0.50 '" 1 +expect "and in total" "$DINGGER status | tail -1" 'spent: $2.00 today · $2.00 this week · $2.00 this month (UTC)' +reply f2 0.75 >>"$SESS/fork.jsonl" +expect "a new reply: counted" "opt $C2 @dingger_cost" 1.25 50 +expect "in the total too" "gopt @dingger_cost_today" 2.75 +t kill-session -t cost +expect "closed agents still count" "gopt @dingger_cost_today" 2.75 10 + # Every daemon this run started must stop with it. kill "$DAEMON_PID" "$DAEMON2_PID" 2>/dev/null expect "the test daemons stop" "ps -o pid= -p $DAEMON_PID,$DAEMON2_PID | wc -l | tr -d ' '" 0 |
