Implemented the next missing parity slice around session env var subprocess merging.

Wires session_env_vars.py into agent_tools._build_subprocess_env so that
variables set via the session registry now reach spawned children, matching
utils/shell/bashProvider.ts:249. Per-call extra_env still wins so explicit
tool overrides take precedence over session defaults.
This commit is contained in:
Abdelrahman Abdallah
2026-04-20 00:51:11 +02:00
parent 66920a3360
commit 72e6fffb34
3 changed files with 42 additions and 1 deletions
+1 -1
View File
@@ -707,7 +707,7 @@ Done:
- [x] Basic git status snapshot - [x] Basic git status snapshot
- [x] Basic shell/subprocess handling - [x] Basic shell/subprocess handling
- [x] Bundled small portable utilities — `utils/array.ts`, `utils/set.ts`, `utils/objectGroupBy.ts`, `utils/xml.ts`, `utils/uuid.ts` ported in `src/small_utils.py` - [x] Bundled small portable utilities — `utils/array.ts`, `utils/set.ts`, `utils/objectGroupBy.ts`, `utils/xml.ts`, `utils/uuid.ts` ported in `src/small_utils.py`
- [x] Session-scoped env-var registry (`utils/sessionEnvVars.ts`) ported in `src/session_env_vars.py` - [x] Session-scoped env-var registry (`utils/sessionEnvVars.ts`) ported in `src/session_env_vars.py` and merged into spawned subprocess env via `_build_subprocess_env` (mirrors `utils/shell/bashProvider.ts`)
- [x] Display formatters from `utils/format.ts` (`formatFileSize`, `formatSecondsShort`, `formatDuration`, `formatNumber`, `formatTokens`) ported in `src/format_utils.py` - [x] Display formatters from `utils/format.ts` (`formatFileSize`, `formatSecondsShort`, `formatDuration`, `formatNumber`, `formatTokens`) ported in `src/format_utils.py`
Missing major utility categories: Missing major utility categories:
+6
View File
@@ -15,6 +15,7 @@ from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Iterator, Union from typing import TYPE_CHECKING, Any, Callable, Iterator, Union
from .agent_types import AgentPermissions, AgentRuntimeConfig, ToolExecutionResult from .agent_types import AgentPermissions, AgentRuntimeConfig, ToolExecutionResult
from .session_env_vars import get_session_env_vars
if TYPE_CHECKING: if TYPE_CHECKING:
from .account_runtime import AccountRuntime from .account_runtime import AccountRuntime
@@ -3321,6 +3322,11 @@ def _build_subprocess_env(context: ToolExecutionContext) -> dict[str, str]:
for key, value in os.environ.items() for key, value in os.environ.items()
if not _is_sensitive_env_var(key) if not _is_sensitive_env_var(key)
} }
# Mirror utils/shell/bashProvider.ts: session env vars (set via /env)
# apply to spawned children, layered above the parent env but below
# explicit per-call extras.
for key, value in get_session_env_vars().items():
env[key] = value
env.update(context.extra_env) env.update(context.extra_env)
return env return env
+35
View File
@@ -9,6 +9,7 @@ from pathlib import Path
from src.agent_tools import ( from src.agent_tools import (
ToolExecutionError, ToolExecutionError,
ToolPermissionError, ToolPermissionError,
_build_subprocess_env,
_ensure_shell_allowed, _ensure_shell_allowed,
_is_sensitive_env_var, _is_sensitive_env_var,
_resolve_path, _resolve_path,
@@ -16,6 +17,10 @@ from src.agent_tools import (
default_tool_registry, default_tool_registry,
) )
from src.agent_types import AgentPermissions, AgentRuntimeConfig from src.agent_types import AgentPermissions, AgentRuntimeConfig
from src.session_env_vars import (
clear_session_env_vars,
set_session_env_var,
)
def _make_context( def _make_context(
@@ -230,5 +235,35 @@ class TestIsSensitiveEnvVar(unittest.TestCase):
self.assertTrue(_is_sensitive_env_var("db_password")) self.assertTrue(_is_sensitive_env_var("db_password"))
# ---------------------------------------------------------------------------
# _build_subprocess_env session env var merging
# ---------------------------------------------------------------------------
class TestBuildSubprocessEnv(unittest.TestCase):
def setUp(self):
clear_session_env_vars()
def tearDown(self):
clear_session_env_vars()
def test_session_env_var_appears_in_subprocess_env(self):
with tempfile.TemporaryDirectory() as tmp:
ctx = _make_context(tmp)
set_session_env_var("CLAW_SESSION_FOO", "from-session")
env = _build_subprocess_env(ctx)
self.assertEqual(env["CLAW_SESSION_FOO"], "from-session")
def test_extra_env_overrides_session_env(self):
with tempfile.TemporaryDirectory() as tmp:
config = AgentRuntimeConfig(cwd=Path(tmp))
ctx = build_tool_context(
config,
tool_registry=default_tool_registry(),
extra_env={"CLAW_OVERRIDE_ME": "from-extra"},
)
set_session_env_var("CLAW_OVERRIDE_ME", "from-session")
env = _build_subprocess_env(ctx)
self.assertEqual(env["CLAW_OVERRIDE_ME"], "from-extra")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()