Files
hermes-agent/tests/gateway/test_multiplex_background_task_scope.py
Teknium 74a95a3ddf feat: /btw now answers side questions with conversation context; /background renamed to /bg
/bg (formerly /background, which is retired) keeps the existing semantics:
spawn a fresh, independent agent session in the background.

/btw is now its own command matching the convention other harnesses use:
ask a quick side question ABOUT the current conversation without
interrupting it. A one-shot auxiliary LLM call (main model by default,
overridable via auxiliary.side_question.* in config.yaml) answers from a
read-only transcript snapshot — the live session's history, role
alternation, and prompt cache are untouched, and the current turn keeps
running.

Surfaces wired: CLI (inline mid-run dispatch), gateway (all messengers,
busy-dispatch table + idle dispatch, i18n across all 17 locales), TUI
(prompt.btw RPC + btw.complete event), Discord native slash, relay
command manifest, desktop exec routing, docs (EN + zh-Hans).
2026-08-29 07:25:17 -07:00

50 lines
1.7 KiB
Python

"""Regression: background tasks respect profile secret scope when multiplexing.
Issue #60726: /bg spawns _run_background_task as a fire-and-forget
asyncio task with no profile scope, so _resolve_session_agent_runtime()'s
credential reads raise UnscopedSecretError when multiplex_profiles is on.
The fix wraps the task body in _profile_runtime_scope, mirroring _run_agent.
"""
import asyncio
from pathlib import Path
from unittest import mock
from gateway.config import GatewayConfig
from gateway.run import GatewayRunner
def _make_runner(multiplex: bool) -> GatewayRunner:
runner = GatewayRunner.__new__(GatewayRunner)
runner.config = GatewayConfig(multiplex_profiles=multiplex)
return runner
class TestBackgroundTaskProfileScope:
"""_run_background_task installs _profile_runtime_scope when multiplexing is active."""
def test_wraps_in_profile_scope_when_multiplex_active(self):
runner = _make_runner(multiplex=True)
inner = mock.AsyncMock(return_value=None)
runner._run_background_task_inner = inner
source = mock.MagicMock()
source.profile = "test_profile"
with mock.patch.object(
GatewayRunner,
"_resolve_profile_home_for_source",
return_value=Path("/fake/profile"),
), mock.patch("gateway.run._profile_runtime_scope") as scope:
scope.return_value.__enter__ = mock.MagicMock()
scope.return_value.__exit__ = mock.MagicMock(return_value=False)
asyncio.run(
runner._run_background_task(
prompt="test", source=source, task_id="bg_test"
)
)
scope.assert_called_once_with(Path("/fake/profile"))
inner.assert_awaited_once()