Skip to content

preview(omnio): background-subagent stack integration (#63 + #67 + #68) — do not merge - #69

Closed
ppazosp wants to merge 10 commits into
mainfrom
ppp/omnio-subagent-preview
Closed

preview(omnio): background-subagent stack integration (#63 + #67 + #68) — do not merge#69
ppazosp wants to merge 10 commits into
mainfrom
ppp/omnio-subagent-preview

Conversation

@ppazosp

@ppazosp ppazosp commented Aug 10, 2026

Copy link
Copy Markdown

PREVIEW / INTEGRATION BRANCH — do not merge

Bundles the whole background-subagent hermes stack on top of current main, as one branch tip a sprite can be reprovisioned from (OMNIO_E2E_HERMES_BRANCH=ppp/omnio-subagent-preview / DEV_HERMES_BRANCH) to preview the feature end-to-end with omnia PR NousResearch#4079's preview deployment.

Contains, verbatim (no squash, original hashes):

Merge the real PRs in order (#68#63 → rebase #67 --onto main → merge #67), then delete this branch. Sanity suites on the merged tip: tool_search 57 · async_delegation 50 · wake_delivery 17 · session_delegations 5 · delegate_apiserver_background 14 — all green.

Backward compatibility

Same verdicts as the constituent PRs (#63, #67, #68) — all backward compatible; hermes + proxy ship together per sprite via reprovision.

🤖 Generated with Claude Code

ppazosp and others added 10 commits August 7, 2026 16:14
…K 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>
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.
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
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>
…hild-completion' into ppp/omnio-subagent-preview
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 closed this Aug 11, 2026
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