feat(omnio): tool_search core-listing, per-child delegation completion, session delegations endpoint, forced-sync flag - #67
Merged
ppazosp merged 6 commits intoAug 11, 2026
Conversation
The tiered-disclosure change (upstream 0986ac3) activates the tool_search bridge whenever ANY deferrable tool exists, so Omnia's six plugin tools all end up behind tool_search/tool_describe/tool_call. The agent's operating docs and skills name those tools directly, so a deferred one reads as unavailable and the model substitutes a weaker built-in — observed live: it wanted web_read, found it deferred, and used web_extract instead. Extend only the never-defer set tool_search consults, via a dedicated _OMNIO_ALWAYS_LOAD_TOOLS constant unioned into _core_tool_names(). _HERMES_CORE_TOOLS is left untouched because it doubles as the tools: payload for the hermes-cli/cron/telegram toolsets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwVLayGYn4ZdvF5oE6TYPA
A batch of N children had no per-child completion signal: the progress hook hardcoded status "running", and the only terminal signal was the delegation-level wake, which fires once ALL children finish. _run_single_child now marks its child terminal in its finally block, _batch_progress carries that flag as a fifth token element, and the progress reporter turns it into one POST with status "completed" and that child's subagent_id. A claim set on the record keeps it to exactly one tick per child across sweeps, and _finalize flushes before progress_fn is dropped so the last child — whose flip lands with the batch's own completion — is not missed. All of it stays best-effort: hook failures are logged and dropped, never raised into the delegation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwVLayGYn4ZdvF5oE6TYPA
GET /api/sessions/{session_id}/delegations filters the process-wide
async-delegation registry by origin_session_id, exposing the registry's
live-status fields (children_activity, per-child finished) so an external
UI can rebuild what a session is still waiting on from the process that
owns the children.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcRxDiCinRwLFJGDKvAJa3
/v1/runs accepts an optional delegation_sync_only flag, threaded into the session context as HERMES_DELEGATION_SYNC_ONLY (excluded from the session snapshot at both sites, like every _VAR_MAP name). When set, delegate_task forces synchronous execution unconditionally — defeating the wake-session re-enable rather than merely skipping it — so a headless caller (cron, trigger.dev run) that can never consume a background wake gets its result inside the turn. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NcRxDiCinRwLFJGDKvAJa3
ppazosp
force-pushed
the
ppp/omnio-tool-search-core-and-child-completion
branch
from
August 10, 2026 14:04
b358409 to
77a2d1d
Compare
The origin session id was only captured into the dispatch record on the
async_delivery_supported()=False self-post fallback; on the normal
api_server push path every delegation record carried an empty
origin_session_id, so GET /api/sessions/{id}/delegations could never
match a session's own live delegations and the Omnio conversation
snapshot read authoritative-empty for the whole run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The listing snapshot exposed only the combined batch goal, so every child in a conversation-events snapshot rendered the same title until its first per-child progress tick arrived. Stamp goals[idx] onto each sampled activity entry (token tuples are in dispatch order). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KU5yhnJcZzbi9kksVAKJad
ppazosp
added a commit
that referenced
this pull request
Aug 11, 2026
…K as product turns (#63) * feat(gateway): deliver background delegation wakes via OMNIO_WAKE_HOOK as product turns - Thread the originating Omnio turn_id (POST /v1/runs) through session context -> delegation dispatch -> completion event (origin_turn_id), alongside each child's streamed subagent_id (subagent_ids). - gateway/wake.py: opt-in OMNIO_WAKE_HOOK redirect for non-push wakes, authenticated with OMNIO_INTERNAL_TOKEN; 409/429/5xx/connect retry with the existing ladder; permanent 4xx raises WakeHookPermanentError so the watcher consumes (drops) unwinnable wakes instead of requeueing forever; self-post fallback when the hook is unset or no turn id is bound. - Emit subagent.start synchronously at background dispatch (parent run is still live, so the event streams AND persists); suppress the child thread's duplicate emission. - Opt-in OMNIO_SUBAGENT_PROGRESS_HOOK: a 10s-cadence daemon monitor posts best-effort per-child progress (goal + current tool) for running delegations. All knobs default off: with the envs unset, behavior is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(tools): exclude HERMES_ORIGIN_TURN_ID from the session snapshot This branch adds HERMES_ORIGIN_TURN_ID to gateway.session_context._VAR_MAP, making it a per-turn bridged var, but never adds it to the snapshot exclusion set — so it was captured into the shared session snapshot and re-sourced on later turns, leaking one turn's origin id into the next. Both exclusion sites are updated, not just the one the test asserts on: * _SNAPSHOT_EXCLUDED_ENV_REGEX — the Python-side contract the unit test checks against every name in _VAR_MAP. * _export_dump_excluding_session_vars — the shell snippet that actually unsets the vars before `export -p`. This is the one that prevents the leak at runtime; fixing only the regex would turn the test green while the variable still leaked. * feat(omnio): tool_search core-listing, per-child delegation completion, session delegations endpoint, forced-sync flag (#67) * fix(tool-search): never defer Omnio's own product tools The tiered-disclosure change (upstream 0986ac3) activates the tool_search bridge whenever ANY deferrable tool exists, so Omnia's six plugin tools all end up behind tool_search/tool_describe/tool_call. The agent's operating docs and skills name those tools directly, so a deferred one reads as unavailable and the model substitutes a weaker built-in — observed live: it wanted web_read, found it deferred, and used web_extract instead. Extend only the never-defer set tool_search consults, via a dedicated _OMNIO_ALWAYS_LOAD_TOOLS constant unioned into _core_tool_names(). _HERMES_CORE_TOOLS is left untouched because it doubles as the tools: payload for the hermes-cli/cron/telegram toolsets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwVLayGYn4ZdvF5oE6TYPA * feat(delegation): per-child completion tick for background delegations A batch of N children had no per-child completion signal: the progress hook hardcoded status "running", and the only terminal signal was the delegation-level wake, which fires once ALL children finish. _run_single_child now marks its child terminal in its finally block, _batch_progress carries that flag as a fifth token element, and the progress reporter turns it into one POST with status "completed" and that child's subagent_id. A claim set on the record keeps it to exactly one tick per child across sweeps, and _finalize flushes before progress_fn is dropped so the last child — whose flip lands with the batch's own completion — is not missed. All of it stays best-effort: hook failures are logged and dropped, never raised into the delegation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwVLayGYn4ZdvF5oE6TYPA * feat(api-server): list a session's async delegations over HTTP GET /api/sessions/{session_id}/delegations filters the process-wide async-delegation registry by origin_session_id, exposing the registry's live-status fields (children_activity, per-child finished) so an external UI can rebuild what a session is still waiting on from the process that owns the children. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NcRxDiCinRwLFJGDKvAJa3 * feat(delegation): honour delegation_sync_only for headless runs /v1/runs accepts an optional delegation_sync_only flag, threaded into the session context as HERMES_DELEGATION_SYNC_ONLY (excluded from the session snapshot at both sites, like every _VAR_MAP name). When set, delegate_task forces synchronous execution unconditionally — defeating the wake-session re-enable rather than merely skipping it — so a headless caller (cron, trigger.dev run) that can never consume a background wake gets its result inside the turn. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NcRxDiCinRwLFJGDKvAJa3 * fix: stamp origin_session_id on the healthy background dispatch path The origin session id was only captured into the dispatch record on the async_delivery_supported()=False self-post fallback; on the normal api_server push path every delegation record carried an empty origin_session_id, so GET /api/sessions/{id}/delegations could never match a session's own live delegations and the Omnio conversation snapshot read authoritative-empty for the whole run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(delegation): stamp per-child goal onto children_activity snapshots The listing snapshot exposed only the combined batch goal, so every child in a conversation-events snapshot rendered the same title until its first per-child progress tick arrived. Stamp goals[idx] onto each sampled activity entry (token tuples are in dispatch order). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KU5yhnJcZzbi9kksVAKJad --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
Stacked on #63 (
ppp/omnio-subagent-wake). Four commits, each independently reviewable:1 ·
fix(tool-search)— never defer Omnio's own product toolsThe tiered-disclosure change (upstream
0986ac393) puts Omnia's six plugin tools behindtool_search, but the agent's operating docs and skills name them directly — a deferred tool reads as unavailable and the model substitutes a weaker built-in (observed live: wantedweb_read, usedweb_extract). New_OMNIO_ALWAYS_LOAD_TOOLSfrozenset unioned into_core_tool_names();_HERMES_CORE_TOOLSuntouched (it doubles as the cli/cron/telegramtools:payload). Also open standalone as #68 — whichever lands first, the other rebases clean.2 ·
feat(delegation)— per-child completion tickA batch delegation's children finish at different times, but the progress sampler only reported
runninguntil the batch's single completion event._run_single_child'sfinallysets a per-childfinishedflag, carried as the 5th element of the progress token;_progress_report_payloademitsstatus: completedfor that child exactly once (_claim_child_completions), and_flush_child_completionsat finalize rescues the last child whose flip lands inside the sweep interval.3 ·
feat(api-server)—GET /api/sessions/{session_id}/delegationsFilters the process-wide async-delegation registry by
origin_session_id, exposingchildren_activity(+ per-childfinished) so an external UI can rebuild what a session is still waiting on from the process that owns the children. This is the snapshot source for omnia NousResearch#4079's conversation-events SSE.4 ·
feat(delegation)— honourdelegation_sync_only/v1/runsaccepts an optionaldelegation_sync_onlyflag, threaded into session context asHERMES_DELEGATION_SYNC_ONLY(excluded from the session snapshot at both sites, like every_VAR_MAPname). When set,delegate_taskforces synchronous execution unconditionally — defeating the wake-session re-enable rather than skipping it — so a headless caller (trigger.dev run, cron) that can never consume a background wake gets its result inside the turn. Fixes the proactive-run stall.Tests
Per-file (this repo's multi-dir pytest gives false failures):
test_tool_search57 ·test_async_delegation50 ·test_delegate162 ·test_delegate_apiserver_background14 ·test_async_delivery_capability27 ·test_api_server_session_delegations5 (new) ·test_api_server_omnio_turn_event_log47 ·test_session_env16 ·test_snapshot_session_id_leak4. Ruff clean. (test_base_environmenthas one pre-existing failure, identical onmain.)Backward compatibility
Backward compatible. All four changes are inert without a counterparty: the always-load set only widens what one surface sees; the
finishedtoken element degrades on shorter tuples by design; the delegations endpoint is new (old proxies never call it; new proxies treat its absence as unknown);delegation_sync_onlydefaults false and old callers never send it. Hermes and the proxy ship together per sprite via reprovision — no intra-sandbox mixed pairing exists.🤖 Generated with Claude Code