You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After #3870 the set_skill_env call propagates through CompositeExecutor and lands on ShellExecutor, which in build_bash_command (crates/zeph-tools/src/shell/mod.rs:2515) sets the extra env via cmd.envs(extra_env). However, when [tools.sandbox] enabled = true on macOS, the env overrides are dropped by the sandbox rewrite.
In crates/zeph-tools/src/sandbox/macos.rs:341-376, rewrite_command_with_sandbox_exec replaces the entire std::process::Command with a fresh one to prepend sandbox-exec:
*std_cmd = std::process::Command::new(sandbox_exec);// <-- env overrides lost here
std_cmd.arg("-f");
std_cmd.arg(profile_path);
std_cmd.arg("--");
std_cmd.arg(original_program);for arg in original_args { std_cmd.arg(arg);}
Only program and args are carried over. The envs map, env_remove entries, and current_dir are not. Any per-call env override (e.g. GITHUB_TOKEN from skill x-requires-secrets) is silently lost; the spawned sandbox-exec then inherits only the parent agent's env, where the secret is intentionally absent.
End-to-end symptom: with github skill active, x-requires-secrets: github_token declared, ZEPH_SECRET_GITHUB_TOKEN in age vault, and #3870 applied, gh auth status inside the agent reports:
github.com
X Failed to log in to github.com account bug-ops (default)
- Active account: true
- The token in default is invalid.
(default) is gh's marker for "no token from env, fell back to hosts.yml" — confirming GH_TOKEN/GITHUB_TOKEN never reached the subprocess. Direct invocation GH_TOKEN=<vault_value> gh auth status succeeds with (GH_TOKEN) as source, proving the token itself is valid and the loss is purely in the sandbox rewrite path.
Same defect affects:
current_dir — cmd.current_dir(p) set by callers before apply_sandbox is wiped.
Any future per-call env config (e.g. injected RUST_LOG, proxy vars).
Linux Landlock path (linux.rs) likely fine because it does not rebuild the Command — needs verification.
cargo run --features full -- vault set ZEPH_SECRET_GITHUB_TOKEN <gh_token>.
Add x-requires-secrets: github_token to ~/.config/zeph/skills/github/SKILL.md frontmatter; ensure github skill is trusted.
Launch TUI, ask agent to run gh auth status (skill matcher should activate github at high confidence).
Output shows token source (default) and "The token in default is invalid", confirming GITHUB_TOKEN not in subprocess env.
Counter-test (proves token is valid and sandbox is the problem):
Disable sandbox: [tools.sandbox] enabled = false.
Same prompt → gh auth status source is (GH_TOKEN) and gh works.
Expected Behaviour
sandbox-exec is spawned with the same env overrides, env_remove entries, and current_dir that were set on the original Command. The sandbox wrap should be purely a program/args prepend; it MUST NOT discard caller-configured execution context.
Fix Direction
Capture and replay env state across the Command replacement. std::process::Command exposes:
get_envs() -> CommandEnvs<'_> — yields (&OsStr, Option<&OsStr>); Some(v) means override-to-v, None means env_remove.
get_current_dir() -> Option<&Path>.
get_env_clear() -> bool (nightly only — workaround: re-apply env_clear if originally requested via a separate flag).
Test gap: add a unit test that constructs a Command::new("bash").arg("-c").arg("…").envs([("FOO", "bar")]).current_dir("/tmp"), runs it through rewrite_command_with_sandbox_exec, then asserts via get_envs / get_current_dir that both survived the rewrite.
Config: ~/.config/zeph/gonkagate.toml (sandbox network-allow-all, age vault, full features)
Platform: macOS 25.4.0
Logs / Evidence
Tool result captured from live TUI (CI-784 session):
$ gh auth status
[stderr] github.com
[stderr] X Failed to log in to github.com account bug-ops (default)
[stderr] - Active account: true
[stderr] - The token in default is invalid.
Description
After #3870 the
set_skill_envcall propagates throughCompositeExecutorand lands onShellExecutor, which inbuild_bash_command(crates/zeph-tools/src/shell/mod.rs:2515) sets the extra env viacmd.envs(extra_env). However, when[tools.sandbox] enabled = trueon macOS, the env overrides are dropped by the sandbox rewrite.In
crates/zeph-tools/src/sandbox/macos.rs:341-376,rewrite_command_with_sandbox_execreplaces the entirestd::process::Commandwith a fresh one to prependsandbox-exec:Only
programandargsare carried over. Theenvsmap,env_removeentries, andcurrent_dirare not. Any per-call env override (e.g.GITHUB_TOKENfrom skillx-requires-secrets) is silently lost; the spawnedsandbox-execthen inherits only the parent agent's env, where the secret is intentionally absent.End-to-end symptom: with
githubskill active,x-requires-secrets: github_tokendeclared,ZEPH_SECRET_GITHUB_TOKENin age vault, and #3870 applied,gh auth statusinside the agent reports:(default)is gh's marker for "no token from env, fell back to hosts.yml" — confirmingGH_TOKEN/GITHUB_TOKENnever reached the subprocess. Direct invocationGH_TOKEN=<vault_value> gh auth statussucceeds with(GH_TOKEN)as source, proving the token itself is valid and the loss is purely in the sandbox rewrite path.Same defect affects:
current_dir—cmd.current_dir(p)set by callers beforeapply_sandboxis wiped.RUST_LOG, proxy vars).linux.rs) likely fine because it does not rebuild the Command — needs verification.Reproduction (macOS)
[tools.sandbox] enabled = true, profile = "network-allow-all"anddefault_level = "trusted"for[skills.trust].cargo run --features full -- vault set ZEPH_SECRET_GITHUB_TOKEN <gh_token>.x-requires-secrets: github_tokento~/.config/zeph/skills/github/SKILL.mdfrontmatter; ensure github skill istrusted.gh auth status(skill matcher should activategithubat high confidence).(default)and "The token in default is invalid", confirmingGITHUB_TOKENnot in subprocess env.Counter-test (proves token is valid and sandbox is the problem):
[tools.sandbox] enabled = false.gh auth statussource is(GH_TOKEN)and gh works.Expected Behaviour
sandbox-execis spawned with the same env overrides,env_removeentries, andcurrent_dirthat were set on the originalCommand. The sandbox wrap should be purely a program/args prepend; it MUST NOT discard caller-configured execution context.Fix Direction
Capture and replay env state across the Command replacement.
std::process::Commandexposes:get_envs() -> CommandEnvs<'_>— yields(&OsStr, Option<&OsStr>);Some(v)means override-to-v,Nonemeans env_remove.get_current_dir() -> Option<&Path>.get_env_clear() -> bool(nightly only — workaround: re-apply env_clear if originally requested via a separate flag).Patch in
rewrite_command_with_sandbox_exec:Test gap: add a unit test that constructs a
Command::new("bash").arg("-c").arg("…").envs([("FOO", "bar")]).current_dir("/tmp"), runs it throughrewrite_command_with_sandbox_exec, then asserts viaget_envs/get_current_dirthat both survived the rewrite.Environment
1a78bf2bonmain(post-fix(tools): forward set_skill_env and set_effective_trust through CompositeExecutor #3870)~/.config/zeph/gonkagate.toml(sandboxnetwork-allow-all, age vault, full features)Logs / Evidence
Tool result captured from live TUI (CI-784 session):