隔离

隔离从 session 边界开始:每个 session 绑定一个 workspace,委派子运行接收有边界的 context。宿主直接工具调用是特权操作;暴露给用户前,应在宿主应用内先做权限判断。

工作区边界

TypeScript
const session = agent.session('/repo');

相对文件、搜索、shell 和 Git 操作都从会话工作区求值。Security Provider 与钩子属于 会话选项;把它们当作生产边界前,应验证依赖的精确策略路径。

Shell 命令还会在 session 的原生进程沙箱中运行,默认拒绝网络。沙箱、process-host 显式启用以及按调用的网络授权见安全。

效果隔离

效果隔离让 session 的写入在宿主提升之前不落到源检出目录。启用后,session 构建时会 在源根目录旁的同级目录 .a3s-isolate-<session_id> 中,以源仓库当前 HEAD 绑定一个 Git worktree,session 的工具和 shell 都在其中工作。源根目录不是 Git 仓库时失败关闭: session 创建会返回 isolation unavailable 错误,而不是回退到共享写入。

Rust
let options = SessionOptions::new().with_effect_isolation(true);
TypeScript
const session = agent.session('/repo', { effectIsolation: true });
Go
session, err := agent.Session(ctx, "/repo", &code.SessionOptions{
EffectIsolation: code.Ptr(true),
})

9.0.0 的 Python SessionOptions 没有该字段的公开 setter。

提升是 Rust 宿主操作:a3s_code_core::effect_isolation::promote_current(session_id) 捕获 worktree 的变更集,只向源树应用一次,并返回 Applied { digest };重放同一 digest 时返回 Idempotent { digest };如果绑定后源 HEAD 已移动,则返回 Conflict { bound_revision, current_revision },且不应用任何内容。丢弃 worktree 从不 触碰源树。自行提升变更的宿主可以用 session.notePromotedDigest(digest)(Node.js)、 session.note_promoted_digest(digest)(Python)或 session.NotePromotedDigest(ctx, digest)(Go)记录已应用的 digest。

只读 session(with_read_only_session(true)、Node.js readOnlySession、Go ReadOnlySession)不会创建隔离 worktree。该标志只跳过隔离绑定,不会移除写入工具。 需要禁止写入时请使用权限策略。

路径规则

path_rules(Rust with_path_rules、Node.js pathRules、Go PathRules)是按路径 作用的指令片段,每项为 { glob, text }。当工具调用或计划指向匹配路径时,匹配的文本会 加入下一次模型输入,总量上限为 4 KiB(core/src/path_instructions.rs);没有匹配时 不注入任何内容。它们用于引导模型, 不是访问控制。路径边界应通过权限规则和 workspace 沙箱强制执行。

委派上下文

task 与自动子智能体委派会隔离子运行推理。父 Agent 只接收紧凑结果,而不是完整 transcript,从而减少 prompt 污染并简化证据审查。

存储隔离

不同产品、租户或测试套件应使用独立的 memory 与会话存储目录:

TypeScript
import { FileMemoryStore, FileSessionStore } from '@a3s-lab/code';
const session = agent.session('/repo', {
memoryStore: new FileMemoryStore('./.a3s/memory'),
sessionStore: new FileSessionStore('./.a3s/sessions'),
});

原生 Harness Worktree

当 a3s code harness 指向 Git 仓库时,每个获得准入的协议会话都会在源仓库 HEAD 创建自己的临时 detached worktree。准入时要求源 worktree 干净,因此宿主本地修改不会 被静默遗漏。不同会话不共享可变文件;移除 Harness 会话时,其临时 worktree 也会被 删除,但任何修改都不会自动写回源 checkout。

每次运行前后,Core 都会通过隔离的临时 Git index 捕获 tracked 以及未忽略的 untracked 内容。运行进入终态后,它生成一份支持二进制、完整 index 的 unified diff,并把结果 tree 固定到私有 Git ref。不可变证据只属于该精确运行;冲突的第二次捕获会被拒绝。 持久化会话恢复时,会把最近一次捕获的结果 tree 恢复到新的 detached worktree。

非 Git 工作区仍使用配置的共享路径,不能生成这种 Git changeset 证据。源 Git worktree 不干净时,隔离准入会失败,而不会回退到共享写入。

有边界的 Changeset 协议

事件流到达 completed、failed 或 cancelled 后,把精确运行身份 POST 到 /v1/agent/changes:

JSON
{
"schema": "a3s.code.agent-change-set-request.v1",
"identity": {
"schema": "a3s.code.agent-run-identity.v1",
"protocol": "a3s.code.agent.v1",
"agent_release_identity": "sha256:<release-digest>",
"session_id": "conversation-018f4f86",
"run_id": "run-018f4f86-attempt-1"
}
}

响应使用 a3s.code.agent-change-set.v1,包含相同 identity、终态、base_tree、 result_tree、patch_digest、patch_bytes、observed_at_ms,以及格式为 git_unified_diff_v1、编码为 base64 的 patch_base64。原始补丁上限为 4 MiB。

变更捕获在 worker 结束后紧接着完成,因此已经进入终态的运行可能短暂返回 a3s.code.agent_protocol.change_set_pending;此时重试同一查询。 a3s.code.agent_protocol.change_set_unavailable 表示工作区不兼容 Git,或捕获结果无法 满足协议上限。

由调用方决定是否以及在何处应用补丁,接口本身不会自动合并。解码或应用前,应校验 声明的字节数与 sha256: 摘要,并保留两个 Git tree 身份。这样既能隔离并发会话写入, 又能向宿主提供确定性的合并或审查制品。

外部 Harness

钩子与权限策略是集成点。将其视作生产边界前,应使用自己的 Harness 测试真实组织策略。