fix(oneshot): declare async delivery unsupported so background delegations run synchronously instead of orphaning - #65491
Closed
forgavio-max wants to merge 1 commit into
Conversation
…tions run synchronously A `hermes -z` oneshot process exits right after printing its single response. A delegate_task background=true dispatched during that turn runs on a daemon thread in the SAME process, so it dies mid-flight and its async_delegations row is orphaned at state=running forever - the model's "result will re-enter the conversation" promise can never be kept. Top-level delegations are forced background by the harness, so the model cannot avoid this by leaving background unset. Fix: bind the existing async-delivery capability gate (the one the stateless API server adapter already uses) to False in run_oneshot() via a new set_async_delivery_supported() helper. delegate_task then downgrades background batches to synchronous execution with an explanatory note, and terminal refuses notify_on_complete watchers it could never fire. Reproduced live 4x before the fix (orphaned rows deleg_3725e69f, deleg_4d9013c4, deleg_9dd323d8, deleg_a9651f0a); after the fix the same prompt completes synchronously with zero orphans. Related: NousResearch#53027 describes the same event-loss class for cron sessions and proposes exactly this mechanism; this PR covers the oneshot path. 4 new tests in tests/hermes_cli/test_oneshot_async_delivery.py; delegate suite passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
Superseded by #66617 (merged). We went with @cgarwood82's #63866 as the base since it was submitted earliest and covers one-shot + cron + terminal watchers in one change; your one-shot approach (standalone setter + tests) matched it. Thanks @forgavio-max. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Makes
hermes -z(oneshot) declare itself unable to deliver async results, sodelegate_task background=truebatches automatically downgrade to synchronous execution instead of being orphaned.The bug: a oneshot process exits right after printing its single response. A background delegation dispatched during that turn runs on a daemon thread in the same process, so it dies mid-flight: the
async_delegationsrow is stuck atstate: runningforever, the work is lost, and the tool's own dispatch note ("its full result re-enters the conversation as a new message") is a promise the channel can never keep. Because top-level delegations are forced to background by the harness (_dispatch_delegate_taskintentionally ignores the schema-levelbackgroundparam), the model cannot avoid this by leavingbackgroundunset — the orphan is structural in oneshot.Reproduced live 4× before the fix (orphaned rows
deleg_3725e69f,deleg_4d9013c4,deleg_9dd323d8,deleg_a9651f0aon our install); after the fix, the same prompt completes synchronously — subagents run, the consolidated report returns in the same response, zero orphans.The fix reuses the existing capability gate rather than inventing a new mechanism:
_SESSION_ASYNC_DELIVERY/async_delivery_supported()is exactly how the stateless API-server adapter already refuses this promise (issue #10760). This PR adds a standalone setter for callers that aren't full gateway adapters, and bindsFalseinrun_oneshot().delegate_task's existing fallback then handles everything;terminalalso correctly refusesnotify_on_complete/watch_patternswatchers it could never fire.Related Issue
Related to #53027, which reports the same event-loss class for cron sessions and proposes exactly this mechanism ("Return False from async_delivery_supported() so delegate_tool.py falls back to synchronous execution"). This PR covers the oneshot path, where the loss is deterministic; the same setter could be reused for the cron path if maintainers want to extend it.
Type of Change
Changes Made
gateway/session_context.py— newset_async_delivery_supported(bool)standalone setter;async_delivery_supported()docstring updated.hermes_cli/oneshot.py—run_oneshot()binds the capability toFalsebefore the agent turn starts.tools/delegate_tool.py— generalized the sync-fallback comment/note wording from "stateless HTTP API" to "stateless one-turn channel" (the note now accurately covers both the API-server and oneshot cases).tests/hermes_cli/test_oneshot_async_delivery.py— 4 new tests: setter semantics, reset-to-default, and (via a patched_run_agent) that the oneshot turn actually observesasync_delivery_supported() == False. Delegate suite passes unchanged.