Files
hermes-agent/agent/delegation_context.py
T
Teknium 8fb678ce5d refactor(agent/image+safety): route vision probes/inline executors on data tables, dedupe path guards (-25% LOC)
- image_routing: capability probes -> _VISION_PROBES ordered table; magic-byte
  sniffing -> _MAGIC signature table; bool tokens -> dict; shared code-span /
  file-existence helpers for extract_image_refs. Fixture-corpus diff base vs
  new identical.
- inline_tool_executors: 11 hand-written executor bodies -> _tool/_callback_tool
  arg-spec factories (kwargs mapping unchanged; corpus of calls identical).
- file_safety: unify _is_under/_under_any, _resolve_target/_resolve_each,
  _mirror_info; deny-list tables compacted; drop retired
  get_cross_profile_warning stub (+ its tests). Every rule preserved (corpus).
- image_gen_provider: drop _images_cache_dir (test-only wrapper), compact.
- estop/delegation_context/kanban_stop/image_gen_registry: defensive-layer
  collapse and docstring compaction.

Tool schema dump byte-identical to base.
2026-09-02 18:58:42 -07:00

96 lines
4.1 KiB
Python

"""Context-local state for delegate_task child execution.
A Hermes process may itself be a Kanban dispatcher worker with HERMES_KANBAN_* in
os.environ. In-process delegate_task children and cron jobs fired via
``cronjob(action="run")`` are NOT dispatcher-owned, so identity gates must fail
closed for them without mutating the process-global environment.
"""
from __future__ import annotations
import os
from contextlib import contextmanager
from contextvars import ContextVar, Token
from typing import Iterator, Mapping, MutableMapping
_DELEGATED_CHILD_CONTEXT: ContextVar[bool] = ContextVar("hermes_delegated_child_context", default=False)
# Any in-process execution that is NOT the dispatcher-owned worker (cron jobs). Kept separate
# so delegate_task-specific behaviour (subprocess env scrubbing, its error strings) is unchanged.
_NON_DISPATCHER_OWNED_CONTEXT: ContextVar[bool] = ContextVar("hermes_non_dispatcher_owned_context", default=False)
DELEGATED_CHILD_ENV_MARKER = "HERMES_DELEGATED_CHILD_CONTEXT"
KANBAN_ENV_KEYS: tuple[str, ...] = (
"HERMES_KANBAN_TASK", "HERMES_KANBAN_RUN_ID", "HERMES_KANBAN_WORKSPACE", "HERMES_KANBAN_WORKSPACES_ROOT",
"HERMES_KANBAN_CLAIM_LOCK", "HERMES_KANBAN_BOARD", "HERMES_KANBAN_DB",
)
@contextmanager
def delegated_child_context(session_id: str | None = None) -> Iterator[None]:
"""Mark child execution and isolate its task-local session identity. Even a context
entered without an id must restore the parent's session ContextVar (child
construction calls ``set_current_session_id``)."""
token = _DELEGATED_CHILD_CONTEXT.set(True)
try:
from gateway.session_context import scoped_current_session_id # lazy: it calls is_delegated_child_context()
with scoped_current_session_id(session_id):
yield
finally:
_DELEGATED_CHILD_CONTEXT.reset(token)
def is_delegated_child_context() -> bool:
"""Return True while code is running for a delegate_task child."""
return bool(_DELEGATED_CHILD_CONTEXT.get())
def enter_non_dispatcher_owned_context() -> Token[bool]:
"""Token form of :func:`non_dispatcher_owned_context` for long try/finally scopes."""
return _NON_DISPATCHER_OWNED_CONTEXT.set(True)
def exit_non_dispatcher_owned_context(token: Token[bool]) -> None:
"""Restore the flag saved by :func:`enter_non_dispatcher_owned_context`."""
_NON_DISPATCHER_OWNED_CONTEXT.reset(token)
@contextmanager
def non_dispatcher_owned_context() -> Iterator[None]:
"""Mark in-process execution that does NOT own the dispatcher's Kanban task; without it
a cron agent run inside a worker is misread as that worker (kanban toolset force-added,
``kanban_complete`` defaulting to its task). ContextVar-scoped rather than clearing
os.environ, which the worker's claim heartbeat and concurrent readers share."""
token = enter_non_dispatcher_owned_context()
try:
yield
finally:
exit_non_dispatcher_owned_context(token)
def is_dispatcher_owned_worker_context() -> bool:
"""The single predicate every ``HERMES_KANBAN_*`` identity gate should use."""
return not (_DELEGATED_CHILD_CONTEXT.get() or _NON_DISPATCHER_OWNED_CONTEXT.get())
def is_delegated_child_process_context() -> bool:
"""Return True in this process or a subprocess spawned by a child."""
return bool(_DELEGATED_CHILD_CONTEXT.get()) or bool(os.environ.get(DELEGATED_CHILD_ENV_MARKER))
def scrub_kanban_env(env: Mapping[str, str] | MutableMapping[str, str]) -> dict[str, str]:
"""Return *env* with dispatcher-only Kanban variables removed and the lineage marker set."""
cleaned = {k: v for k, v in env.items() if k not in KANBAN_ENV_KEYS}
cleaned[DELEGATED_CHILD_ENV_MARKER] = "1"
return cleaned
def delegated_child_subprocess_env(
env: Mapping[str, str] | MutableMapping[str, str] | None = None,
) -> dict[str, str] | None:
"""Env override only when delegated-child lineage must cross fork: preserves ``env=None``
inherit semantics for non-delegated calls; in a child, a scrubbed env carrying the marker."""
if not is_delegated_child_process_context():
return None if env is None else dict(env)
return scrub_kanban_env(os.environ if env is None else env)