Skip to content

Latest commit

 

History

History
408 lines (321 loc) · 23.9 KB

File metadata and controls

408 lines (321 loc) · 23.9 KB
doc-id 09-local-protocol-security
title 本地协议与安全
status active
version 1.9.0
last-updated 2026-09-05
source-range 规格书 §2.3、§9.1 末段、§12.3–§12.4、§14–§15、§25.2;v1.1–v1.7.1 既有协议;v1.8 组件数据链路只读 endpoint 与最小化 observation
参考文献/依赖
03-public-api-models
04-vite-plugin
07-dom-css-collection
08-code-prompt
12-testing-acceptance
15-risks-adr
16-ai-agent-execution
17-model-provider-credentials
data-flow-07-model-protocol-config
data-flow-10-security-performance
data-flow-13-beta-implementation

本地协议与安全

Astro 补充(2026-09-05):源码扩展名白名单新增 .astro,但仍须会话 registry、realpath root、大小预算与 token/Origin 校验。astro-host 目标使用现有 annotation authorizer 重新读取、授权和裁剪源码,不信任浏览器提交的片段。中间件同 origin 挂载,不另起端口;Astro base 不改变 /__spotpatch 协议根路径。build/preview/sync 不挂载中间件。详见 Astro 安全设计

本文件是 HTTP 路径、请求头、错误码、浏览器数据边界、清洗规则、文件授权、会话安全和安全验收的唯一事实来源。fileId 的生成和 registry 行为由 Vite 插件规范定义 (见 doc-id:04-vite-plugin),配置值域和默认值由公共 API 文档定义 (见 doc-id:03-public-api-models)。

浏览器数据边界

浏览器不持有绝对路径、provider 凭据或本地执行能力。

  • DOM 属性只保存会话内的短 source ID、行号和列号。
  • 文件绝对路径只存在于 Vite Node 端内存。
  • 源码仅在用户完成选择后按需读取。
  • 默认只返回有限代码片段,不返回完整文件。
  • AI UI 只持有 provider/model profile ID、显示名、脱敏能力状态和公共 Job 数据。
  • 数据链路 UI 只持有严格 report 和当前页面内存 observation;不得持有 raw URL、body、header、cookie、Response、Fiber、函数、绝对路径或原始 stack。
  • 浏览器不得接收 Base URL、真实 provider 模型名、Key 环境变量名、Authorization header、worktree 真实路径、命令 cwd 或完整进程环境。

禁止把项目 root、绝对路径、编辑器命令、Agent check 命令和 Secret Resolver 注入浏览器。页面脚本、DOM 内容或浏览器请求参数都不能扩大服务端配置的文件、模型和执行权限。

源码片段的业务边界和预算见源码片段与 Prompt 规范 (见 doc-id:08-code-prompt)。

数据清洗与展示安全

永久删除或脱敏

  • valuedefaultValue
  • password
  • authorization
  • cookieset-cookie
  • token、secret、api-key 常见变体
  • 内联 base64
  • blob URL
  • 超长 SVG path
  • React 私有 expando 和事件对象

密码输入框无论用户是否关闭 redact 都必须脱敏。

展示安全

DOM 预览必须通过 textContent 写入 <pre>。禁止将采集到的 HTML 重新写入 innerHTML,否则业务页面内容可能在工具 UI 中形成 XSS。

采集范围由 DOM/CSS 采集规范定义,采集器只消费本节清洗规则,不得维护另一份名单 (见 doc-id:07-dom-css-collection)。

本地 HTTP 协议

协议常量

基础路径、请求头和 endpoint 只在此处定义:

export const SPOTPATCH_API_BASE = "/__spotpatch/v1" as const;
export const SPOTPATCH_TOKEN_HEADER = "X-SpotPatch-Token" as const;

export const SPOTPATCH_ENDPOINTS = {
  sourceContext: `${SPOTPATCH_API_BASE}/source-context`,
  openEditor: `${SPOTPATCH_API_BASE}/open-editor`,
  dataFlowComponentReport: `${SPOTPATCH_API_BASE}/data-flow/component-report`,
  dataFlowPageReport: `${SPOTPATCH_API_BASE}/data-flow/page-report`,
  agentCapability: `${SPOTPATCH_API_BASE}/agent/capability`,
  agentWorkspaceHealth: `${SPOTPATCH_API_BASE}/agent/workspace-health`,
  agentJobs: `${SPOTPATCH_API_BASE}/agent/jobs`,
} as const;

export type AgentJobAction =
  | "events"
  | "result"
  | "cancel"
  | "apply"
  | "revert";

export function getAgentJobEndpoint(
  jobId: string,
  action: AgentJobAction,
): string {
  return `${SPOTPATCH_ENDPOINTS.agentJobs}/${encodeURIComponent(jobId)}/${action}`;
}

客户端与服务端必须从 shared/protocol 引用这些常量,不得复制字符串。

通用请求规则

  • endpoint 请求统一使用带 JSON body 的 POST;Job 事件 endpoint 的响应为 application/x-ndjson
  • 使用 X-SpotPatch-Token 请求头。
  • 普通请求体最大 32 KB;只有创建 Agent Job 的请求因包含有界多目标 DOM/CSS 上下文而允许最大 256 KB。该例外不适用于 capability、事件、结果、编辑器或源码接口,也不改变字段级 Schema 上限。
  • 响应设置 Cache-Control: no-store
  • 校验 Origin;默认只允许当前 Vite origin 和 loopback。
  • 事件和结果读取也必须发送空 JSON 对象。不能改为无 body 的浏览器 GET,因为同源 GET 不保证携带 Origin,会破坏强制 Origin 校验。
  • 所有返回都使用统一 envelope。
  • 写操作禁止浏览器自动重试;服务端使用 Job ID 和 action 保证本地协议副作用幂等。provider 工具副作用使用 turn + toolCallId 作为执行作用域,不能把 provider 原始 ID 当成整个 Job 的全局唯一键。
type ApiSuccess<T> = {
  ok: true;
  data: T;
};

type ApiFailure = {
  ok: false;
  error: {
    code: ErrorCode;
    message: string;
  };
};

错误信息不能返回绝对路径、调用栈和内部异常对象;debug 信息只写本地终端。

NDJSON 流是统一 envelope 的唯一例外。事件联合只在此处定义:

interface AgentJobEventBase {
  readonly schemaVersion: 2;
  readonly sequence: number;
  readonly jobId: string;
  readonly status: AgentJobStatus;
  readonly timestamp: string;
}

export type AgentJobEvent =
  | (AgentJobEventBase & {
      readonly type: "snapshot";
      readonly data: { readonly snapshot: AgentJobSnapshot };
    })
  | (AgentJobEventBase & {
      readonly type: "phase";
      readonly data: { readonly message: string };
    })
  | (AgentJobEventBase & {
      readonly type: "tool";
      readonly data: {
        readonly turn: number;
        readonly toolCallId: string;
        readonly toolName: string;
        readonly state: "started" | "succeeded" | "failed";
        readonly relativePath?: string;
        readonly checkLabel?: string;
      };
    })
  | (AgentJobEventBase & {
      readonly type: "check";
      readonly data: { readonly result: AgentCheckResult };
    })
  | (AgentJobEventBase & {
      readonly type: "result-ready";
      readonly data: { readonly hasResult: true };
    })
  | (AgentJobEventBase & {
      readonly type: "error";
      readonly data: {
        readonly code: ErrorCode;
        readonly message: string;
      };
    });

每行必须通过联合 Schema,并使用 Job 内严格递增、不可复用的 sequenceturn 是 SpotPatch 从 1 开始生成的模型轮次,不由浏览器或 provider 提供;Runtime 使用 turn + toolCallId 关联同一次工具活动,避免中转站跨轮复用原始 ID 时覆盖历史状态。同一轮内相同 ID 的冲突调用必须拒绝。事件不包含原始 provider chunk、Prompt、完整文件、patch、命令、环境或异常;完整 Diff 只通过认证后的 result endpoint 获取。客户端只处理完整换行记录;流中断后通过 Job 快照和 result endpoint 收敛状态,不猜测最后操作是否成功。

读取源码上下文

方法与 endpoint:POST SPOTPATCH_ENDPOINTS.sourceContext

请求示例:

{
  "fileId": "Q7k3pA9vL2s",
  "line": 36,
  "column": 5,
  "maxLines": 80
}

响应示例:

{
  "ok": true,
  "data": {
    "relativePath": "src/components/UserProfile.tsx",
    "language": "tsx",
    "startLine": 12,
    "endLine": 58,
    "excerpt": "...",
    "boundary": "component"
  }
}

示例中的 maxLines 使用当前公共默认预算;规范值只由 ContextBudget.maxCodeLines 定义 (见 doc-id:03-public-api-models)。

打开编辑器

方法与 endpoint:POST SPOTPATCH_ENDPOINTS.openEditor

请求只接受 fileIdlinecolumn。不能接受:

  • 绝对路径
  • 任意 editor 名称
  • shell command
  • CLI arguments

编辑器偏好来自已解析的服务端配置,只接受公共枚举 (见 doc-id:03-public-api-models)。auto 先从 Vite 进程的受控集成环境识别 Cursor/VS Code,无法识别时才进入后备探测;显式 vscode/cursor 只映射到固定命令。参数、工作区路由和探测优先级只由 Vite 插件规范定义 (见 doc-id:04-vite-plugin),浏览器无权选择或扩大该配置。

服务端必须等待 editor adapter 的短暂启动确认;在确认窗口内出现命令不存在、非零退出或启动失败时返回统一脱敏错误,不能提前返回 200。成功响应为 { editor: "auto" | "vscode" | "cursor" },其中 editor 是适配器实际采用的受控偏好,不得机械回显配置值。该响应表示请求已通过即时启动检查,不承诺操作系统一定把窗口置前。Runtime 必须校验响应枚举,不接受服务端或页面注入的任意命令字符串。

Apply 后可以保留会话内 fileId 与行列用于只读导航,但必须释放已失效的 DOM 引用;文件仍需在每次请求时重新通过 registry 和 root allowlist 授权。

组件数据链路报告

以下 endpoint 只在服务端可信配置显式启用 dataFlow 时可用;关闭时返回 DATA_FLOW_DISABLED,不初始化 analyzer,也不泄露依赖:

操作 方法与 endpoint 请求摘要 成功语义
组件报告 POST SPOTPATCH_ENDPOINTS.dataFlowComponentReport schemaVersion: 1 + exact componentSourceId/sourceVersion,或已授权 fileId/line/column 返回严格、已清洗、不可变的静态组件依赖报告
页面报告 POST SPOTPATCH_ENDPOINTS.dataFlowPageReport schemaVersion: 1 + 有界 targets 对目标报告去重聚合,不接收浏览器 observation 原值

exact identity 必须由当前 Source Registry 解析且 sourceVersion 完全一致;缺失或过期返回 DATA_FLOW_SOURCE_STALE,不得搜索相似组件。坐标模式仍只接受 Registry fileId,并在 realpath 后验证项目根。请求不得包含 root、绝对/相对 path、glob、include、module list、adapter、预算、URL 或请求/响应值。

数据链路请求体受 DEFAULT_DATA_FLOW_LIMITS.protocolRequestMaxBytes 约束,报告受独立最大字节数和客户端结构校验约束。Runtime observation 不上传 Node;它只保存在浏览器有界 ring buffer,并通过 opaque callsite/sourceVersion 在本地合并。URL 只保留 origin、pathname、query key,SpotPatch 自身 API 流量在 recorder 入口排除。详细模型和保留周期见 (见 doc-id:data-flow-07-model-protocol-config)、(见 doc-id:data-flow-10-security-performance)。

Agent 能力与 Job

以下 endpoint 只在可信配置显式启用 AI 时注册;Agent 未启用时统一返回 AI_DISABLED,不能泄露 provider 是否配置:

操作 方法与 endpoint 请求摘要 成功语义
能力探测 POST SPOTPATCH_ENDPOINTS.agentCapability providerProfileIdmodelProfileId 返回显示名、脱敏 capability 状态和建议动作
工作区健康 POST SPOTPATCH_ENDPOINTS.agentWorkspaceHealth 空 JSON 对象 返回 readyconsent-requiredblocked,以及去重后的变更计数和稳定错误码
创建 Job POST SPOTPATCH_ENDPOINTS.agentJobs annotationproviderProfileIdmodelProfileIdapplyMode、值必须为 trueproviderDataConsent、可选且只能为 truetrustedFastModeConsentworkingTreeMode 202,返回初始 AgentJobSnapshot
读取事件 POST getAgentJobEndpoint(id, "events") 空 JSON 对象 返回当前会话内有序 NDJSON 事件
读取结果 POST getAgentJobEndpoint(id, "result") 空 JSON 对象 返回公共 Job 快照及可用的 AgentJobResult
取消 POST getAgentJobEndpoint(id, "cancel") 空 JSON 对象 进入 cancelling 或返回已终态快照
应用 POST getAgentJobEndpoint(id, "apply") 空 JSON 对象 重新校验基线并全有或全无应用
撤销 POST getAgentJobEndpoint(id, "revert") 空 JSON 对象 哈希匹配后全有或全无撤销

创建 Job 时客户端不能提供 URL、Key、环境变量名、真实模型名、root、路径、命令、limits、检查集合或 system prompt。服务端必须用 profile ID 从已解析配置重建全部可信值,并重新校验 SpotAnnotation、会话、来源和模型 allowlist。applyMode 只能从服务端公开的策略内选择:服务端为 trusted-auto 时允许本次请求选择 review | trusted-auto,其他策略只接受与服务端完全一致的模式;客户端不能借此扩大权限。workingTreeMode 只接受 require-clean | include-local-changes,缺失时安全回退为前者;后者只表达本次用户已同意纳入当前本地修改,不授权放宽文件、工具、冲突或规模门禁。trustedFastModeConsent: true 只有在本次请求选择 trusted-auto 且服务端已授权时才有效,模式、策略与同意字段不一致必须拒绝创建 Job。

工作区健康 endpoint 每次都从 Vite root 读取真实 Git 状态,不接受浏览器传入的路径或状态。staged、unstaged、untracked 和 conflicted 是可解释计数,同一文件同时 staged/unstaged 时 total 只计一次。未初始化仓库、进行中的 merge/rebase/cherry-pick/revert、未解决冲突、不支持的未跟踪项、未跟踪数量/体积超限分别返回稳定错误码;blocked 不得通过 include-local-changes 绕过。快照上限只由公共模型定义 (见 doc-id:03-public-api-models)。健康快照不是锁,创建 Job、Apply 和 Revert 必须再次复验 (见 doc-id:16-ai-agent-execution)。

多目标创建请求必须满足以下附加门禁:

  • 只接受 SpotAnnotation.schemaVersion: 3localetargets 和每个目标的 instruction 必须满足公共模型。每项说明 trim 后非空且不超过单项目标上限,说明总字符数不超过整组上限;协议拒绝缺项、未知字段、旧版全局 note 和超限数组,服务端再拒绝超过当前 maxTargets 配置的数组。版本、语言值域和全部上限只在公共模型定义 (见 doc-id:03-public-api-models)。
  • 服务端按原顺序逐个授权目标。任一目标的 marker、fileId、显示路径或 React source 不合法时整次请求失败,不创建部分 Job。
  • 有完整 marker 的目标按 fileId + line + column 去重;没有 marker 的完全相同目标也拒绝,防止重复上下文放大请求与费用。Runtime 去重不是服务端安全校验的替代。
  • 浏览器提交的 CodeContext 是不可信提示。Runtime 创建 Agent 请求时应省略它;即使恶意客户端提交,服务端也只校验路径一致性并从当前 Source Registry 重新读取每个目标的代码片段,Agent 只接收服务端结果。
  • 授权时保留每个目标对应的说明,但浏览器提供的源码片段仍被替换为服务端当前读取结果;说明不授予额外文件、路径、命令或网络权限,也不能覆盖系统安全策略。
  • 授权后的目标集作为一个不可变 Job 输入;不得合并不同目标说明、为“先成功的目标”创建子 Job,也不得在后续事件中追加或改写目标。

providerDataConsent: true 是客户端对本次创建请求的显式声明;缺失、false 或非布尔值均拒绝。服务端只把它记为当前 Vite 会话内对应 provider profile 的同意状态,不写盘、不跨会话继承,也不能用一次同意替代另一个 provider profile 的同意。能力探测不接收该字段且不得发送项目内容。

trustedFastModeConsent 缺失表示本次请求没有可信极速授权,只允许字面量 true,不得接受 false、字符串或持久化偏好。在服务端公开 trusted-auto 能力后,Runtime 默认保持 review;用户主动选择可信极速模式时,用一个明确说明后果的会话级复选框同时取得 provider 数据传输、纳入当前有界本地修改、跳过项目检查和直接应用的同意,并为每个 trusted-auto Job 重放该显式字段。切回 review、刷新页面、开发服务重启或切换 provider 后必须重新取得同意。该字段不放宽项目 root、保护文件、符号链接、文本/规模限制、任意 Shell、Git 操作状态、原子 patch 或基线哈希冲突。

Job ID 使用至少 128 bit 随机熵,只在当前 Vite 会话有效。Job endpoint 必须验证 token、Origin、Job 所属会话与允许的状态转换;未知 Job 对未授权请求统一表现为不可用,避免枚举。Job 事件、结果大小和保留时间受服务端上限控制,Vite 退出后全部失效。

Agent 的状态、结果和 limits 使用公共模型 (见 doc-id:03-public-api-models),工具与 Apply/Revert 行为见 Agent 执行规范 (见 doc-id:16-ai-agent-execution),外部 provider 协议和 capability 语义见模型提供商规范 (见 doc-id:17-model-provider-credentials)。

错误码

export const ERROR_CODES = {
  INVALID_REQUEST: "INVALID_REQUEST",
  INVALID_TOKEN: "INVALID_TOKEN",
  ORIGIN_NOT_ALLOWED: "ORIGIN_NOT_ALLOWED",
  SOURCE_NOT_FOUND: "SOURCE_NOT_FOUND",
  SOURCE_OUTSIDE_ROOT: "SOURCE_OUTSIDE_ROOT",
  SOURCE_TOO_LARGE: "SOURCE_TOO_LARGE",
  EDITOR_OPEN_FAILED: "EDITOR_OPEN_FAILED",
  DATA_FLOW_DISABLED: "DATA_FLOW_DISABLED",
  DATA_FLOW_SOURCE_STALE: "DATA_FLOW_SOURCE_STALE",
  DATA_FLOW_ANALYSIS_CANCELLED: "DATA_FLOW_ANALYSIS_CANCELLED",
  AI_DISABLED: "AI_DISABLED",
  PROVIDER_NOT_CONFIGURED: "PROVIDER_NOT_CONFIGURED",
  PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED",
  PROVIDER_PROTOCOL_UNSUPPORTED: "PROVIDER_PROTOCOL_UNSUPPORTED",
  MODEL_NOT_ALLOWED: "MODEL_NOT_ALLOWED",
  MODEL_TOOL_CALL_UNSUPPORTED: "MODEL_TOOL_CALL_UNSUPPORTED",
  PROVIDER_RATE_LIMITED: "PROVIDER_RATE_LIMITED",
  AGENT_BUSY: "AGENT_BUSY",
  AGENT_LIMIT_EXCEEDED: "AGENT_LIMIT_EXCEEDED",
  AGENT_CANCELLED: "AGENT_CANCELLED",
  WORKTREE_DIRTY: "WORKTREE_DIRTY",
  WORKTREE_NOT_REPOSITORY: "WORKTREE_NOT_REPOSITORY",
  WORKTREE_OPERATION_IN_PROGRESS: "WORKTREE_OPERATION_IN_PROGRESS",
  WORKTREE_CONFLICTED: "WORKTREE_CONFLICTED",
  WORKTREE_LOCAL_CHANGES_TOO_LARGE: "WORKTREE_LOCAL_CHANGES_TOO_LARGE",
  WORKTREE_UNTRACKED_UNSUPPORTED: "WORKTREE_UNTRACKED_UNSUPPORTED",
  WORKTREE_LOCAL_CHANGES_UNSUPPORTED: "WORKTREE_LOCAL_CHANGES_UNSUPPORTED",
  TOOL_DENIED: "TOOL_DENIED",
  TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
  TOOL_ARGUMENTS_INVALID: "TOOL_ARGUMENTS_INVALID",
  TOOL_CALL_ID_CONFLICT: "TOOL_CALL_ID_CONFLICT",
  TOOL_PATH_DENIED: "TOOL_PATH_DENIED",
  PATCH_REJECTED: "PATCH_REJECTED",
  VALIDATION_FAILED: "VALIDATION_FAILED",
  APPLY_CONFLICT: "APPLY_CONFLICT",
  INTERNAL_ERROR: "INTERNAL_ERROR",
} as const;

TOOL_INPUT_INVALID 只保留为旧客户端兼容码,新执行路径不得再用它混合不同根因。工具 JSON 不是对象、无法解析、缺字段、含未知字段、类型错误或超过字段约束时使用 TOOL_ARGUMENTS_INVALID;其中已解析对象的字段契约错误可以作为零副作用、一次性可重试工具结果返回,畸形 JSON 仍终止 Job。同一模型轮次内出现相同 toolCallId 且不能归一为同一流式调用时返回 TOOL_CALL_ID_CONFLICT。跨轮次复用 provider 原始 ID 是允许的,幂等和活动关联必须额外包含 SpotPatch turnread_file 对保护、项目外、缺失、目录、符号链接、二进制或非 UTF-8 路径统一返回脱敏的 TOOL_PATH_DENIED 可恢复结果,不区分具体拒绝原因、不回显请求路径,也不读取或修改文件;模型只能改用 list_files / search_text 已返回的允许路径。任何写工具的路径拒绝仍终止 Job。patch 策略拒绝和 provider 流协议错误继续使用各自既有错误码,不得退化为上述通用诊断。

文件安全

Root 约束

启动时先得到真实 root:

const realRoot = await fs.realpath(resolvedConfig.root);

每次读取前重新解析真实文件路径并校验:

export async function assertInsideRoot(
  root: string,
  candidate: string,
): Promise<string> {
  const [realRoot, realCandidate] = await Promise.all([
    fs.realpath(root),
    fs.realpath(candidate),
  ]);

  const relative = path.relative(realRoot, realCandidate);
  const outside = relative.startsWith(`..${path.sep}`)
    || relative === ".."
    || path.isAbsolute(relative);

  if (outside) {
    throw new SpotPatchError("SOURCE_OUTSIDE_ROOT");
  }

  return realCandidate;
}

这样同时阻止 ../ 和符号链接逃逸。

Source Context 文件约束

  • 只允许 registry 已登记文件。
  • 只允许 .tsx.jsx
  • 默认拒绝大于 1 MB 的源文件。
  • maxLines 由服务端上限裁剪,不能信任客户端值。
  • 文件变化或删除返回结构化错误,不回退到任意路径搜索。

Agent 文件约束

Agent 文件工具只在隔离 worktree root 内操作普通文本文件,并在每次读写前后校验规范化路径、realpath、文件类型和大小。以下路径或类型在 v1.1 中无条件拒绝,配置、模型和用户请求都不能放宽:

  • .git/**.env.env.*
  • **/*.pem**/*.key**/id_rsa* 及其他已识别的私钥/凭据文件。
  • node_modules/**dist/**coverage/** 和工具临时目录。
  • package manager lockfile、Git 子模块、符号链接、socket、device、FIFO 和二进制文件。
  • root 外路径、绝对路径、含 ../NUL 的路径、大小写或分隔符规范化后落入保护范围的路径。

Agent 允许范围不是 Source Registry 的扩大版:服务端从 worktree root 开始执行独立授权,结果只返回相对 POSIX 路径。任一变更触及保护路径、文件模式提权、规模上限或路径不确定性时,整批变更 fail-closed,不保留“其余看起来安全”的部分。

会话安全

  • token 至少 128 bit 随机熵。
  • 每次 Vite 启动重新生成。
  • token 不写入磁盘和日志。
  • 客户端仅放在请求 header。
  • allowLan: false 时拒绝非 loopback Host/Origin。
  • Agent endpoint 即使在 allowLan: true 时也继续要求 token、精确 Origin 和首次 provider 数据传输同意;不得因为处于局域网而降低门禁。
  • provider Key 只在 Node Secret Resolver 中解析;会话 token 与 provider Key 不得互换、记录或传给检查子进程。

浏览器使用 fileId 而不是路径的决策见 ADR-005 (见 doc-id:15-risks-adr)。

安全验收

  • 生产残留 = 0。
  • 目录穿越与符号链接逃逸全部拒绝。
  • 无 token、错误 token、错误 Origin 全部拒绝。
  • 密码字段泄漏 = 0。
  • 默认 Prompt 不包含 Cookie、Authorization 和存储 token。
  • AI 禁用时 Agent endpoint、provider 请求、Git worktree 和检查子进程数量均为 0。
  • 浏览器 bundle、虚拟模块、HTML、日志、错误、Job 事件和生产产物中的 provider Key 泄漏 = 0。
  • 浏览器伪造 URL、模型、命令、root、路径和 apply mode 全部拒绝。
  • 空目标、重复目标、超过协议或配置上限的目标集,以及任一目标的伪造 source/code 全部拒绝;不得把浏览器代码片段直接发送给 provider。
  • Prompt injection、恶意工具参数、同轮冲突 toolCallId、超限输出和保护路径修改均 fail-closed;跨轮相同 ID 由内部 turn 安全隔离。
  • 未取得显式同意的脏工作区、被阻断的 Git 状态、并发变化、检查失败和 Apply/Revert 哈希冲突均不得覆盖用户文件;已同意纳入的 staged、unstaged、untracked 修改及其 index 状态必须在 Apply/Revert 前后保持,撤销只移除 Agent 增量。

测试用例和 CI 门禁由测试与验收规范定义 (见 doc-id:12-testing-acceptance)。