pub fn expand_env_vars(s: &str) -> StringExpand description
BP-9 (§1.8 “env substitution in values”, D6 row “Env/command
substitution in config values”): expand substitution references in s
against the process environment and the filesystem. Applied at
HarnessConfig::to_config_profile to the string-valued [core]
fields that plausibly vary per deployment — base_url, system_prompt,
append_system_prompt, additional_dirs, extra_headers values, and
extra_body string values (judgment call, §1.8’s “in values” wording
names no exhaustive field list; api_key_env/api_key_cmd/
api_key_command are deliberately EXCLUDED — the first is already an
env var NAME not a value, the other two are commands the shell/exec
layer resolves when it runs them, see the call site’s comment).
Three forms are recognized, matching the catalog’s D6 semantics
(${VAR}, {file:…}, !command) with the third deliberately REFUSED:
${VAR}— the process environment. An unset variable is left LITERAL (${VAR}stays in the output) rather than silently substituted with an empty string, so a config author sees immediately that something didn’t resolve instead of silently getting a blankbase_url.${VAR:-default}— the shell’s own “usedefaultwhenVARis unset OR empty” operator (cc§7’s.mcp.jsonform). Because the default makes the author’s intent explicit, THIS form never leaves a literal behind.{file:/path/to/secret}— the file’s contents with trailing newlines trimmed (oc§6’s form). An unreadable path is left LITERAL, the same fail-visible posture as an unset${VAR}.
!command (pi§6’s form) is NOT expanded here and never will be: a
config VALUE that silently executes a command turns every layer that can
set that value into arbitrary code execution. The one sanctioned door is
the explicitly-named credential helper (core.api_key_cmd /
core.api_key_command), which is [project-forbidden] and runs only in
the credential-resolution path. command_substitution_refusals reports
a !-prefixed value as a resolve-time warning naming that reason.