feat(acp): consume Claude's async background-task lifecycle
claude-agent-acp 0.73.0 publishes the lifecycle of Claude's non-agent background work — background shells, workflows, monitors — as three new `session/update` variants, gated on the AIR `asyncTasks` capability. Advertise it (claude only; codex-acp 1.8.0 has no such channel) and read the frames. This is not a nicer rendering of something codeg already had. The transcript watcher can see that a task was launched but explicitly cannot tell a live task from one whose CLI died, it never sees workflow/monitor tasks at all, and nothing could stop a task once started. The channel carries a real terminal edge, an output file path, and `_session/async_task/stop`. The frames are consumed BEFORE `MatchDispatch`, beside `fix_usage_update_nulls`, because that is the only way to see them: `if_notification` matches on the method and then hard-errors on unparsed params, and schema 0.11.7's `SessionUpdate` has no catch-all arm. Left alone they would land on the dropped-update path, which also feeds the empty-turn diagnosis — a turn that only launched background work would be reported as an agent that said nothing. The three variants collapse into one delta type merged in `apply_event`, so there is a single merge rule and the snapshot a client attaches to is the same projection a client that saw every frame holds. Only a `spawned` delta may create a row; rows are retained after they settle, because the adapter revises finished tasks and an evicted one would come back nameless. A live task now defers the idle sweep on its own. That combines with the transcript watcher's accounting by OR, never a sum: the two observe overlapping work through different channels, and reaping an idle connection kills the agent CLI and the background work with it.
X
xintaofei committed
52818ca4e705825432dc34c0217dfb7eb1e7859f
Parent: fece4e8