#AI Agent#TypeScript#DevOps

编程 Agent 命令执行回执:可核验 Transcript、流式脱敏与截断证明

终端里出现一行命令不等于它真的按批准方式执行。本文定义命令 argv、cwd、可执行文件、策略判定、退出状态与输出摘要的不可变回执,同时避免秘密随日志扩散。

本文要点

  • 主题聚焦:AI Agent、TypeScript、DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年5月25日·更新于 2026年7月20日

编程 Agent 常在回复里展示 pnpm test,平台却无法回答:它是通过 shell 解释的字符串还是参数数组,实际二进制来自哪里,工作目录是否正确,策略允许了什么,输出是否被截断,最后又是正常退出还是被信号终止。把聊天中的代码块当执行记录,会让未运行的命令和真实执行无法区分。

编程 Agent 的改动验证门 定义补丁需要哪些检查,MCP 审计日志设计 讨论工具调用审计;本文只证明一次本地进程实际如何启动和结束。它不判断开发环境是否与别处一致,也不做环境漂移预检。

冻结 command-receipt.v1

执行器必须在进程创建边界采集事实,而不是让 Agent 事后填写。一次 attempt 的合同包含 receiptId、父任务与步骤、argv、逻辑与解析后 cwd、解析后的 executablePath、可执行文件摘要、解释器或脚本摘要、环境变量名 allowlist、policyDecisionId 与策略摘要、sandbox profile、开始/结束单调时间、PID、退出码或 signal,以及 stdout/stderr 的流摘要。

json
{
  "schema": "command-receipt.v1",
  "argv": ["pnpm", "test", "--", "auth.spec.ts"],
  "cwd": {"requested": "./app", "resolved": "/workspace/app"},
  "executable": {"path": "/usr/local/bin/pnpm", "sha256": "..."},
  "policy": {"decisionId": "pd_91", "digest": "...", "outcome": "ALLOW"},
  "termination": {"exitCode": 1, "signal": null}
}

必须区分参数数组与 shell=true。后者另外记录 shell 可执行文件、原始脚本的受控摘要和 shell 参数,不能把重定向、管道或命令替换拆成虚假的 argv。Linux execve 描述了程序路径、参数和环境的进程边界;平台应在对应 spawn/exec 包装层签发 STARTED,随后追加 TERMINATED。仅有 REQUESTED 证明不了进程曾启动。

cwd 同时记录调用者请求值和目录对象身份,不能先解析字符串再普通 chdir。Linux launcher 应用受约束的 openat2 取得 dirfd,记录 mount ID、device 与 inode,并让子进程对同一 fd 执行 fchdir。可执行文件也不能在“求摘要”和 exec 之间重新按 PATH 打开:先取得受策略约束的 fd,从不可变快照读取并求摘要,再对同一对象使用 fexecve 或 execveat(AT_EMPTY_PATH)。只记录 device/inode 仍挡不住并发改写;应依赖只读内容寻址副本、fs-verity 或等价密封机制。无法提供对象绑定的平台必须标记 UNBOUND_EXECUTABLE,该回执不能支持强门禁。

shebang 脚本要把脚本与解释器视为一个执行元组:分别固定内容与身份,再显式启动固定解释器;shell=true 同样固定 shell 和脚本文本,不能让内核或 PATH 在取证后重新选择解释器。环境默认只保留变量名与策略分类,值不进入回执;确需证明某个非敏感配置时保存批准后的规范化摘要。凭证、Cookie 和低熵 token 不能因为被哈希就自动变安全。

输出必须边读边脱敏

stdout 与 stderr 按独立序号读取,每块带 sequence、原始字节数、脱敏后字节数和前一块链摘要。脱敏器必须在持久化、事件总线和遥测导出之前运行,并保留跨 chunk 的最大模式窗口;否则 sk-abc 被拆成 sk- 与 abc 两块就会绕过规则。Prompt 与 Trace 遥测治理 的默认拒绝原则同样适用,但命令回执还需要证明哪些字节没有被保留。

记录 redactionRuleset、命中类型与替换计数,不记录命中的秘密。为防止低熵值被摘要枚举,原始流不生成公开裸哈希;完整性使用隔离密钥的 HMAC,普通 reviewer 只看到脱敏流的 SHA-256。若规则错误,输出管线 fail closed:暂停持久化并把 attempt 标成 EVIDENCE_BLOCKED,不能退化为原文直传。

截断也要成为可验证事实

高噪声命令可能产生数 GB 输出。达到配额后不能只写 ...。回执保存 stdout/stderr 各自的 observedBytes、retainedBytes、droppedBytes、首尾保留区间、截断原因与完整流 HMAC;后台仍持续 drain 到进程结束,或明确记录因资源保护而停止读取并终止进程。这样“命令失败但错误行被截掉”不会被误报为无输出。

退出状态必须三态区分:正常退出码、signal/超时终止、启动失败。超时后先发终止信号、再强杀的两个时刻都进入事件序列;执行器崩溃且无法确认子进程结局时标记 UNKNOWN_TERMINATION,禁止猜成 0。测试结果如何绑定当前 diff 由验证门负责,本回执只提供可信的命令事实。

不可变不等于无限保存

完成回执以 previousReceiptDigest 串成任务内哈希链,并由执行器身份签名或写入具备保留锁的对象存储。W3C PROV 的 activity/entity 关系可连接策略、进程和输出工件;更新脱敏规则只能签发派生视图,不能覆盖旧回执。删除到期的脱敏正文时保留最小元数据、链摘要、删除证明与期限,避免“不可变”成为永久保存敏感数据的借口。

Reviewer 点击回复中的命令时,应看到回执状态而不是可编辑文本。没有 TERMINATED、策略摘要不匹配、签名无效、输出证据被阻断或截断方式不符合该门禁,都显示为未验证。编程 Agent 沙箱权限设计 决定命令可以触达什么,本回执不能把一次允许推导成后续无限授权。

故障注入、量化门与回滚

上线前注入六类故障:秘密跨两个 chunk;stdout 洪泛触发截断;进程被 SIGKILL;摘要后替换 PATH 目标或改写同一 inode;cwd 软链接在请求与启动间改变;执行器在 STARTED 后崩溃。期望分别得到零秘密落盘、准确 dropped bytes、signal 终止、执行密封副本或启动被阻断、dirfd 对象身份不变和 UNKNOWN_TERMINATION,且链校验仍能定位缺失阶段。

强制门设为:受管命令回执覆盖率 100%,argv/cwd/executable/policy/termination 必填完整率 100%,合成秘密跨块泄漏为 0,10 GiB 压测的字节账差为 0,篡改检出率 100%,签发增加的 p95 启动延迟低于 20 ms。任何关键字段缺失都不得支持“检查已运行”的声明。

发布先对 5% 非敏感命令做 shadow 回执,不影响原执行;连续 7 天满足门槛后扩到 50%,再只对高风险合并门强制。回滚切回上一版采集与脱敏策略并停止新强制判定,但保留已签发回执和版本化 reader;若新 writer 与旧 reader 不兼容,先双写并验证再切换。最终目标不是保存更多终端文本,而是让每句“我运行过”都对应一份可核验、不会泄密且诚实表达缺口的执行事实。

Related

相关推荐