Skip to content

provider-acp: no consumption path for agent-wake notifications — ACP agents with settled background work can't be resumed (OMP async-job delivery) #2461

Description

@bradhallett

1. Summary

When an ACP agent settles background work after the client-owned prompt has completed — OMP's async-job auto-delivery is the concrete case — the only v1-legal way to surface that work is a private, capability-gated notification asking the client to prompt again. OMP is preparing exactly that (option (b) in can1357/oh-my-pi#9157: initialize _meta capability + _-prefixed wake notification + parked delivery, already endorsed by two other ACP consumers). bb has no consuming half:

  • handleAgentNotification drops every notification that is not session/update — a wake has no entry point.
  • No path synthesizes a client turn on an agent's behalf: client/turn/requested has source producers only for spawn/tell.
  • activeBackgroundAgentCount counts only Claude-Code backgroundTask rows, so bb cannot even show that an ACP agent has work in flight.

This tracks the bb side: capability handshake, notification routing, synthetic resume, and background-task materialization. It is the delivery half of #2122 (which tracks rendering of unprompted session/update frames): on OMP 18.x there are no frames to render — verified live in production below. Work shipped to git while the thread sat idle forever.

2. Versions and environment

3. Steps to reproduce

  1. Start a thread on acp-omp and give the agent work that ends the turn with background agents still running (OMP's "ending the turn for auto-delivery" — its core resume mechanism).
  2. Wait minutes for the agents to settle.
  3. OMP resumes the orchestrator detached inside its own process; the follow-up turn runs to completion (verification gate, commit, final summary).
  4. The bb thread never reacts: no turn, no message, no error; idle forever.

4. Expected vs actual

Observed live (all UTC, 2026-08-26):

00:50:37    turn/completed — orchestrator ends turn, 2 background agents in flight
01:01–01:04 agents finalize inside OMP
01:05:32–53 OMP's detached resume completes: verification gate passes,
            commit 75e0691 lands 01:05:34 (14 files, +793/−149),
            close-out summary written to the OMP session transcript only
bb events:  zero turn events; one acp/fs/write at 01:05:32 surfaced as a
            hidden provider/unhandled row; no session/update of any kind

Expected: the thread reports the follow-up (summary + commit), or at minimum a resumable state. Actual: complete silence — the work exists only in git and ~/.omp/agent/sessions/, and the user has to learn it shipped from elsewhere.

5. Evidence

Each side of the gap, at d3f7a90:

  • Routing: handleAgentNotification in packages/provider-bridge-acp/src/bridge/bridge.ts (L2504–L2511) returns immediately for any method !== "session/update" — everything else an agent sends is dropped without logging.
  • Turn initiation: packages/domain/src/thread-events.ts types client/turn/requested with source: z.enum(["spawn", "tell"]) (L119) — no producer can express an agent-initiated request, even though initiator already includes "agent" (invariant comment at L121) and continuationOfRequestId exists unused (L117–118). The event surface is half-ready.
  • Visibility: activeBackgroundAgentCount is SQL over backgroundTask rows that ACP providers never write — bb cannot show "N agents running" for OMP.
  • Send path: the provider-retry plumbing (continueCandidate, plugins/provider-retry/src/service.ts L505) already sends agent-only continuations with a legible decline (input-not-accepted when the session can't accept) — the same shape a wake-answer needs.
  • Agent side: ACP: async-job auto-delivery turns reach clients unbracketed; allowAgentInitiatedTurns never enabled can1357/oh-my-pi#9157 option (b) — agentCapabilities._meta["oh-my-pi"].agentWake / clientCapabilities._meta["bb.dev"].agentWake handshake, PromptResponse._meta.pendingAsyncJobs, an _-prefixed wake notification, and delivery parked on the existing hidden-message queue until the client prompts.

6. What I ruled out

7. Suggested priority and effort

Same population as #2122 — every acp-omp thread whose turn ends with background work — now with guaranteed rather than heuristic loss. P1; medium effort, additive and fully gated so nothing changes for agents that don't advertise:

  1. Advertise clientCapabilities._meta["bb.dev"] = { agentWake: 1 } at initialize; treat agentCapabilities._meta["oh-my-pi"].agentWake as the agent-side opt-in.
  2. Route _-prefixed notifications through handleAgentNotification instead of dropping them.
  3. On wake: synthetic resume on the existing send path, emitting client/turn/requested with initiator: "agent" and continuationOfRequestId (extend the source enum with a "wake" member or equivalent); decline legibly (input-not-accepted) if the session can't accept.
  4. Materialize backgroundTask rows for ACP agents with work in flight so activeBackgroundAgentCount and the UI count them.
  5. Keep the hardening from the Bracket agent-initiated ACP turns so unprompted work renders #2220 review: a wake racing a user prompt can hit omp's Agent is already processing rejection — queue-and-retry with backoff rather than failing the synthetic turn.

Sequencing: lands after (or with) the OMP-side capability.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ompOh My Piprovider-acpBuilt-in plugin: provider-acpprovidersCross-provider bridges, models, login

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions