feat(terminal): wire shared-container key into profile-scoped resolver and MEDIA delivery

Follow-up on @fangliquanflq's opt-in (#84775): after the profile-scoping fix
(#94560) the container cache key is resolved in _resolve_container_task_id,
so the shared key must unify profiles there too — 'shared:<key>' for every
session of every opted-in profile AND for CLI/no-session runs. Delivery adds
the shared sandbox layout as the first translation candidate. Empty key
keeps strict per-profile isolation; SSH ignores the key entirely.
This commit is contained in:
Teknium
2026-08-25 03:04:06 -07:00
parent f5200a4c10
commit 82b32f32ef
3 changed files with 74 additions and 0 deletions
+4
View File
@@ -1552,6 +1552,10 @@ def _docker_sandbox_dir_candidates(session_key: str = "") -> List[str]:
from tools.environments.base import sanitize_task_id_for_path
except Exception:
return ["default"]
# Explicit trusted-profiles opt-in: one shared container identity.
shared = os.getenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "").strip()
if shared:
candidates.append(sanitize_task_id_for_path(f"shared:{shared}"))
try:
from hermes_cli.profiles import get_active_profile_name
@@ -253,3 +253,59 @@ def test_ssh_backend_keeps_session_scoping(monkeypatch):
assert terminal_tool._resolve_container_task_id(None) == "session:sess-A"
finally:
clear_session_vars(tokens)
# --- Trusted-profiles shared container opt-in (#84671) ------------------------
def test_shared_key_unifies_profiles(monkeypatch):
from gateway.session_context import clear_session_vars, set_session_vars
_persistent_docker(monkeypatch)
monkeypatch.setenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "team/workspace")
tokens = set_session_vars(session_key="s1", profile="work")
try:
a = terminal_tool._resolve_container_task_id(None)
finally:
clear_session_vars(tokens)
tokens = set_session_vars(session_key="s2", profile="research")
try:
b = terminal_tool._resolve_container_task_id(None)
finally:
clear_session_vars(tokens)
assert a == b == "shared:team/workspace"
def test_shared_key_applies_to_cli_no_session(monkeypatch):
# CLI (no session key) must land in the same shared container as gateway
# sessions, or the opt-in splits the container it exists to unify.
_persistent_docker(monkeypatch)
monkeypatch.setenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "team/workspace")
monkeypatch.delenv("HERMES_SESSION_KEY", raising=False)
assert terminal_tool._resolve_container_task_id(None) == "shared:team/workspace"
def test_empty_shared_key_keeps_profile_scoping(monkeypatch):
from gateway.session_context import clear_session_vars, set_session_vars
_persistent_docker(monkeypatch)
monkeypatch.setenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "")
tokens = set_session_vars(session_key="s1", profile="work")
try:
assert terminal_tool._resolve_container_task_id(None) == "profile:work"
finally:
clear_session_vars(tokens)
def test_shared_key_ignored_outside_persistent_docker(monkeypatch):
# The opt-in is a persistent-Docker concept only: SSH keeps session
# scoping even when the key is set.
from gateway.session_context import clear_session_vars, set_session_vars
monkeypatch.setenv("TERMINAL_ENV", "ssh")
monkeypatch.setenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "team/workspace")
tokens = set_session_vars(session_key="sess-A", profile="work")
try:
assert terminal_tool._resolve_container_task_id(None) == "session:sess-A"
finally:
clear_session_vars(tokens)
+14
View File
@@ -1513,11 +1513,25 @@ def _resolve_container_task_id(task_id: Optional[str]) -> str:
# the default profile share the SAME container — CLI's historical key
# IS the default profile's container.
if _docker_persistent_profile_scoped():
# Explicit opt-in: trusted profiles configuring the same
# terminal.docker_shared_container_key share ONE container/cache
# slot (and sandbox dir) regardless of profile name (#84671).
shared = os.getenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "").strip()
if shared:
return f"shared:{shared}"
profile = _current_session_profile() or "default"
if profile == "default":
return "default"
return f"profile:{profile}"
return f"session:{session_key}"
# CLI/no-session path: honour the shared-container opt-in here too, or a
# CLI run of a keyed profile would land in "default" while its gateway
# sessions land in "shared:<key>" — splitting the very container the
# setting exists to unify.
if _docker_persistent_profile_scoped():
shared = os.getenv("TERMINAL_DOCKER_SHARED_CONTAINER_KEY", "").strip()
if shared:
return f"shared:{shared}"
return "default"