mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
9fdc266f64
* fix(subagents): wake the parent when a follow-up finishes a yielded child A sub-agent that calls sessions_yield on its own behalf parks its run and correctly withholds the parent's announce. But a later follow-up to that same child session registered a sibling registry row instead of continuing the paused one, so the requester defaulted to the child's own main session and the original parent — itself idle behind sessions_yield — was never woken. The paused row also stayed an unsettled descendant, deferring the parent's settle batch forever with nothing recorded explaining the silence. Follow-up dispatch now adopts the paused row through the existing post-steer replacement seam, inheriting the requester identity and carrying the settle-wake credential forward with its frozen batch membership remapped to the new run id. A follow-up that names its own requester keeps registering separately, since an explicit requester is a delivery opt-in that adoption would silently drop. Also stops frozen-result refill from targeting paused rows: a yield clears the result on purpose, so refilling from the session would attribute a later turn's text to the paused run. Closes #120157 * fix(subagents): select the paused owner past a requester-bound sibling Adoption looked up the newest run for the child session and adopted it only when that row was itself paused. A requester-bound follow-up deliberately stays a sibling, but it registers at a higher generation and becomes that newest row, so any later default follow-up saw an unpaused newest row, declined adoption, and registered yet another sibling. The original requester stayed parked behind a paused row that can never announce -- the same silent stall this fix exists to remove, reached through a valid mixed-delivery sequence. The latest-run query now takes an optional predicate applied before the generation comparison, so a caller that owns a specific row class selects the newest row of that class. Adoption asks for the newest `sessions_yield` row directly instead of inferring it from generation order. Docs now state that continuation applies to default delivery, since a follow-up carrying its own requester runs as a sibling by design. * test(qa): prove post-yield follow-up delivery through the gateway boundary The unit and gateway-method tests for paused-run adoption assert on registry rows, which proves the bookkeeping but not that an operator ever sees the result. This adds the boundary proof: a real gateway child, the QA mock channel, and the mock provider driving a subagent that pauses itself and finishes only on a later follow-up. A fixture plugin owns both legs. Its `before_dispatch` hook spawns the child with `completionDelivery: "current-requester"`, so the announce has the operator turn as its audience. An HTTP route then dispatches the follow-up to that same paused session using default delivery -- the path adoption is meant to catch. A requester-bound follow-up would opt into its own audience and run as a sibling instead, so the two legs must differ here. The mock provider gains a child that yields on its own behalf. Both of its turns match on the current prompt rather than the shared transcript, so the yielded kickoff cannot make the follow-up turn yield a second time. The scenario asserts both sides of the invariant: no outbound traffic while the child is paused, and exactly one announce carrying the follow-up marker once it ends. Reverting the adoption call site fails this test in the way that matters: the child still produces its marker and the run still ends with stopReason=stop, but nothing reaches the requester and the wait times out. The result is computed and then silently dropped -- which is the failure this repair exists to remove. * fix(ci): match QA Lab fixture plugin entries as a group in knip The all-exports pass listed one fixture entry by name, so every new QA Lab fixture plugin lands as an unused file and turns check-dependencies red until someone remembers this file. Nothing imports these entries by design: the Gateway E2E loads them through plugin config paths. * docs(subagents): scope yield continuation to plugin runtime follow-ups Adoption is gated on plugin_subagent task tracking, which only createGatewaySubagentRuntime().run sets, so api.runtime.subagent.run is the sole route into it. Writing that as one example implied other follow-up paths to a paused session continue the run too; they are not tracked as sub-agent runs and announce nobody. * fix(subagents): reject undurable paused-run adoption Fail plugin follow-up admission closed when the paused-run ownership swap cannot be persisted, while retaining the existing restart-recovery return-false contract. Trim duplicate tests and keep boundary coverage for requester routing, wake-batch remapping, repeated yield, and persistence rollback. Co-authored-by: zhou.huanfeng <woundfongv3@163.com> * docs(subagents): clarify yielded-run steering Co-authored-by: zhou.huanfeng <woundfongv3@163.com> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>