Context
Sub-issue of #10671. When checkSunsetted reports idle_out_candidate: true, recovery is a lightweight A2A nudge into the existing session — not a fresh-spawn. Reuses bridge-daemon.mjs in-place keystroke logic; zero new transport.
The Problem
Today there is no idle-out recovery path. Active-subscription stale-memory state has only two outcomes: heartbeat keeps polling (no action) or Cmd+N spawns a duplicate session (parallel-session footgun).
The Architectural Reality
A2A messaging primitive: add_message({to: identity, body: ...}) → bridge-daemon.mjs dispatches via osascript keystroke into the active chat tab. This is the same delivery path used today for non-sunset wake events.
#10641 lesson: staleness ≠ "agent is idle." It's a candidate signal for in-place nudge ONLY.
The Fix
On idle_out_candidate signal:
- Acquire in-flight idle-out lock (per the lock-primitive sub-issue)
- Send A2A heartbeat message: subject
[heartbeat] idle-out nudge; body conventional content (e.g., "long since your last save — please check inbox + save current progress, then continue or sunset cleanly")
bridge-daemon delivers via existing keystroke path
- Wait for first
add_memory from identity (clears lock)
- If lock expires without memory → mark abandoned (per lock primitive)
Invariants:
- Bounded — N nudges within window, hard cooldown thereafter
- Non-spawning — never opens a new session
- Idempotent — re-firing produces no duplicate effect
- No-destructive-type-into-active-draft —
bridge-daemon focus-steal protection per #10422
Acceptance Criteria
Out of Scope
- The lock primitive itself — covered by sibling sub-issue
- Detector contract — covered by sibling sub-issue
Related
- Parent: #10671
- Adjacent: #10422 (focus-steal protection), #10641 (staleness lesson preserved)
Origin Session ID: cce1fea5-32ff-410c-b820-2e9a27b3cd51
Context
Sub-issue of #10671. When
checkSunsettedreportsidle_out_candidate: true, recovery is a lightweight A2A nudge into the existing session — not a fresh-spawn. Reusesbridge-daemon.mjsin-place keystroke logic; zero new transport.The Problem
Today there is no idle-out recovery path. Active-subscription stale-memory state has only two outcomes: heartbeat keeps polling (no action) or Cmd+N spawns a duplicate session (parallel-session footgun).
The Architectural Reality
A2A messaging primitive:
add_message({to: identity, body: ...})→bridge-daemon.mjsdispatches via osascript keystroke into the active chat tab. This is the same delivery path used today for non-sunset wake events.#10641 lesson: staleness ≠ "agent is idle." It's a candidate signal for in-place nudge ONLY.
The Fix
On
idle_out_candidatesignal:[heartbeat] idle-out nudge; body conventional content (e.g., "long since your last save — please check inbox + save current progress, then continue or sunset cleanly")bridge-daemondelivers via existing keystroke pathadd_memoryfrom identity (clears lock)Invariants:
bridge-daemonfocus-steal protection per #10422Acceptance Criteria
bridge-daemonfocus-steal guardOut of Scope
Related
Origin Session ID: cce1fea5-32ff-410c-b820-2e9a27b3cd51