diff --git a/pipeline_service/secret_vault.py b/pipeline_service/secret_vault.py index c627152..26faf77 100644 --- a/pipeline_service/secret_vault.py +++ b/pipeline_service/secret_vault.py @@ -775,6 +775,10 @@ async def sanitize_tool_output(sor, text: str, *, org_id: str = "", # ══════════════════════ 执行边界:占位符 → 环境变量 ══════════════════════ +#: env 引用形态($PIPELINE_SEC_NAME / ${PIPELINE_SEC_NAME})——prompt 承诺的写法 +_ENV_REF_RE = re.compile(r"\$\{?" + ENV_PREFIX + r"([A-Z][A-Z0-9_]*)\}?") + + async def prepare_command(sor, command: str, *, org_id: str = "", user_id: str = "") -> Tuple[str, Dict[str, str], List[str]]: """执行边界唯一入口:命令里的占位符 → `$PIPELINE_SEC_NAME` + 环境变量字典。 @@ -794,6 +798,19 @@ async def prepare_command(sor, command: str, *, org_id: str = "", logger.warning("prepare_command load failed: %s", str(e)[:120]) secrets = {} rewritten, hits, unknown = detokenize(command, secrets, mode="env") + # env 引用形态补齐(2026-09-17 缺口修复,e2e 实锤):SECRET_PROMPT_BLOCK 承诺 + # 「命令里写 $PIPELINE_SEC_X 平台执行瞬间注入真值」,但此前只认占位符形态—— + # agent 照 prompt 写 env 引用时 hits 为空 → env 不注入 → 变量展开为空 + # (实测 stdout `SEC_READY_` 空值)。两种写法从此等价: + # 已存在名称 → 进 hits(env 注入);不存在名称 → 进 unknown(拒绝执行, + # 与陷阱 C「绝不静默空值」同原则——空展开会静默改变命令语义)。 + for m in _ENV_REF_RE.finditer(rewritten): + n = m.group(1) + if n in secrets: + if n not in hits: + hits.append(n) + elif n not in unknown: + unknown.append(n) env = resolve_env(secrets, hits) if hits: try: