feat: v0.8.1 answer sessions from the inbox: allow/deny, peek & reply
- PermissionRequest hook: the state file records tool, detail and description of a pending permission; Notification keeps it. - devdesk attach --answer (presses 1/Esc only if the same wait is on and the prompt is visible), --peek (last exchange from the transcript), --reply (bracketed paste + Enter, streamed like uploads). - Inbox: Allow/Deny/Open for permission waits; Peek & reply for other waits and finished sessions. Matrix messages include the command. Closes #22 Closes #23 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -675,3 +675,68 @@ fs_write() { # SESSION PATH BASE CONTENT-FILE
|
||||
[ "$status" -ne 0 ]
|
||||
[ "$(cat "$p/docs/notes/new.md")" = hello ]
|
||||
}
|
||||
|
||||
@test "permission requests are recorded and answered only while their prompt shows (#22)" {
|
||||
start_server
|
||||
devdesk new alpha
|
||||
claude_args alpha >/dev/null
|
||||
pane=$(tmux -S "$DEVDESK_SOCKET" display -p -t =alpha:shell '#{pane_id}')
|
||||
hook() { TMUX="$DEVDESK_SOCKET,1,0" TMUX_PANE="$pane" "$REPO/libexec/devdesk-state-hook"; }
|
||||
echo '{"hook_event_name":"PermissionRequest","tool_name":"Bash","tool_input":{"command":"rm -rf /tmp/x","description":"Remove x"}}' | hook
|
||||
echo '{"hook_event_name":"Notification","message":"Claude needs your permission","notification_type":"permission_prompt"}' | hook
|
||||
st="$DEVDESK_STATE_DIR/state/alpha.json"
|
||||
[ "$(jq -r '[.state, .request.tool, .request.detail, .request.description, .message] | @tsv' "$st")" = \
|
||||
"$(printf 'waiting\tBash\trm -rf /tmp/x\tRemove x\tClaude wants to use Bash')" ]
|
||||
since=$(jq -r .since "$st")
|
||||
# The fake Claude window shows no prompt: refused, nothing typed.
|
||||
run devdesk attach --answer alpha "$since" allow
|
||||
[ "$status" -ne 0 ]
|
||||
[[ $output == *"isn't on screen"* ]]
|
||||
# Put a prompt on screen (what Claude draws), then allow.
|
||||
tmux -S "$DEVDESK_SOCKET" respawn-window -k -t =alpha:claude "printf ' Do you want to proceed?\n ❯ 1. Yes\n 2. No\n'; read -r -n1 k; echo \"got:\$k\" > '$TMP/key'; sleep 30"
|
||||
sleep 0.5
|
||||
run devdesk attach --answer alpha 999 allow
|
||||
[ "$status" -ne 0 ]
|
||||
[[ $output == *"already answered"* ]]
|
||||
run devdesk attach --answer alpha "$since" allow
|
||||
[ "$status" -eq 0 ]
|
||||
sleep 0.5
|
||||
[ "$(cat "$TMP/key")" = "got:1" ]
|
||||
[ "$(jq -r .state "$st")" = working ]
|
||||
# Answered: a second click is refused.
|
||||
run devdesk attach --answer alpha "$since" deny
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "peek shows the last exchange; reply pastes into the Claude window (#23)" {
|
||||
start_server
|
||||
devdesk new alpha
|
||||
claude_args alpha >/dev/null
|
||||
export CLAUDE_CONFIG_DIR="$TMP/claude"
|
||||
id=11111111-2222-3333-4444-555555555555
|
||||
mkdir -p "$CLAUDE_CONFIG_DIR/projects/-x"
|
||||
cat >"$CLAUDE_CONFIG_DIR/projects/-x/$id.jsonl" <<'JSONL'
|
||||
{"type":"user","message":{"role":"user","content":"Please upgrade PBS"},"timestamp":"2026-10-10T10:00:00Z"}
|
||||
{"type":"assistant","message":{"role":"assistant","content":[{"type":"tool_use","name":"Bash","input":{}}]},"timestamp":"2026-10-10T10:00:05Z"}
|
||||
{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"ok"}]},"timestamp":"2026-10-10T10:00:06Z"}
|
||||
{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"PBS is on 4.1. Reboot now?"}]},"timestamp":"2026-10-10T10:00:09Z"}
|
||||
JSONL
|
||||
jq --arg id "$id" '.sessions.alpha.claude_session_id = $id' "$DEVDESK_STATE_DIR/registry.json" >"$TMP/r" && mv "$TMP/r" "$DEVDESK_STATE_DIR/registry.json"
|
||||
run devdesk attach --peek alpha
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(jq -r '[.assistant.text, .user.text] | @tsv' <<<"$output")" = "$(printf 'PBS is on 4.1. Reboot now?\tPlease upgrade PBS')" ]
|
||||
# reply: lands in the Claude window as one paste + Enter
|
||||
tmux -S "$DEVDESK_SOCKET" respawn-window -k -t =alpha:claude "IFS= read -r line; printf '%s' \"\$line\" > '$TMP/got'; sleep 30"
|
||||
sleep 0.3
|
||||
printf 'yes, reboot it' >"$TMP/msg"
|
||||
run bash -c "base64 -w0 '$TMP/msg' | '$REPO/bin/devdesk' attach --reply alpha 14"
|
||||
[ "$status" -eq 0 ]
|
||||
sleep 0.5
|
||||
[ "$(cat "$TMP/got")" = "yes, reboot it" ]
|
||||
# not while a permission prompt waits
|
||||
mkdir -p "$DEVDESK_STATE_DIR/state"
|
||||
printf '{"state":"waiting","since":1,"request":{"tool":"Bash"}}\n' >"$DEVDESK_STATE_DIR/state/alpha.json"
|
||||
run bash -c "base64 -w0 '$TMP/msg' | '$REPO/bin/devdesk' attach --reply alpha 14"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ $output == *"answer that first"* ]]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user