diff --git a/PARITY_CHECKLIST.md b/PARITY_CHECKLIST.md index 528e6af..284b437 100644 --- a/PARITY_CHECKLIST.md +++ b/PARITY_CHECKLIST.md @@ -707,7 +707,7 @@ Done: - [x] Basic git status snapshot - [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] 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` Missing major utility categories: diff --git a/src/agent_tools.py b/src/agent_tools.py index ec43107..dc7c25b 100644 --- a/src/agent_tools.py +++ b/src/agent_tools.py @@ -15,6 +15,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, Iterator, Union from .agent_types import AgentPermissions, AgentRuntimeConfig, ToolExecutionResult +from .session_env_vars import get_session_env_vars if TYPE_CHECKING: 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() 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) return env diff --git a/tests/test_agent_tools_security.py b/tests/test_agent_tools_security.py index 5f7ea67..1c3d3ed 100644 --- a/tests/test_agent_tools_security.py +++ b/tests/test_agent_tools_security.py @@ -9,6 +9,7 @@ from pathlib import Path from src.agent_tools import ( ToolExecutionError, ToolPermissionError, + _build_subprocess_env, _ensure_shell_allowed, _is_sensitive_env_var, _resolve_path, @@ -16,6 +17,10 @@ from src.agent_tools import ( default_tool_registry, ) from src.agent_types import AgentPermissions, AgentRuntimeConfig +from src.session_env_vars import ( + clear_session_env_vars, + set_session_env_var, +) def _make_context( @@ -230,5 +235,35 @@ class TestIsSensitiveEnvVar(unittest.TestCase): 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__": unittest.main()