Skip to content

feat(omnio): tool_search core-listing, per-child delegation completion, session delegations endpoint, forced-sync flag - #67

Merged
ppazosp merged 6 commits into
ppp/omnio-subagent-wakefrom
ppp/omnio-tool-search-core-and-child-completion
Aug 11, 2026
Merged

feat(omnio): tool_search core-listing, per-child delegation completion, session delegations endpoint, forced-sync flag#67
ppazosp merged 6 commits into
ppp/omnio-subagent-wakefrom
ppp/omnio-tool-search-core-and-child-completion

Conversation

@ppazosp

@ppazosp ppazosp commented Aug 7, 2026

Copy link
Copy Markdown

Stacked on #63 (ppp/omnio-subagent-wake). Four commits, each independently reviewable:

1 · fix(tool-search) — never defer Omnio's own product tools

The tiered-disclosure change (upstream 0986ac393) puts Omnia's six plugin tools behind tool_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: wanted web_read, used web_extract). New _OMNIO_ALWAYS_LOAD_TOOLS frozenset unioned into _core_tool_names(); _HERMES_CORE_TOOLS untouched (it doubles as the cli/cron/telegram tools: payload). Also open standalone as #68 — whichever lands first, the other rebases clean.

2 · feat(delegation) — per-child completion tick

A batch delegation's children finish at different times, but the progress sampler only reported running until the batch's single completion event. _run_single_child's finally sets a per-child finished flag, carried as the 5th element of the progress token; _progress_report_payload emits status: completed for that child exactly once (_claim_child_completions), and _flush_child_completions at finalize rescues the last child whose flip lands inside the sweep interval.

3 · feat(api-server)GET /api/sessions/{session_id}/delegations

Filters the process-wide async-delegation registry by origin_session_id, exposing 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. This is the snapshot source for omnia NousResearch#4079's conversation-events SSE.

4 · feat(delegation) — honour delegation_sync_only

/v1/runs accepts an optional delegation_sync_only flag, threaded into 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 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_search 57 · test_async_delegation 50 · test_delegate 162 · test_delegate_apiserver_background 14 · test_async_delivery_capability 27 · test_api_server_session_delegations 5 (new) · test_api_server_omnio_turn_event_log 47 · test_session_env 16 · test_snapshot_session_id_leak 4. Ruff clean. (test_base_environment has one pre-existing failure, identical on main.)

Backward compatibility

Backward compatible. All four changes are inert without a counterparty: the always-load set only widens what one surface sees; the finished token 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_only defaults 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

ppazosp and others added 4 commits August 10, 2026 16:03
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
ppazosp force-pushed the ppp/omnio-tool-search-core-and-child-completion branch from b358409 to 77a2d1d Compare August 10, 2026 14:04
@ppazosp ppazosp changed the title fix(omnio): keep product tools out of tool_search deferral and report per-child delegation completion feat(omnio): tool_search core-listing, per-child delegation completion, session delegations endpoint, forced-sync flag Aug 10, 2026
ppazosp and others added 2 commits August 10, 2026 18:34
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
ppazosp merged commit 46a0dd6 into ppp/omnio-subagent-wake Aug 11, 2026
37 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant