# 架构(ARCHITECTURE) > 基线:`0.2.0`|契约见 [`CONTRACT.md`](./CONTRACT.md);命令面见 [`COMMANDS.md`](./COMMANDS.md)。 > 本文回答:代码怎么分层、一次调用经过哪些模块、在哪里加东西、失败如何收口。 --- ## 1. 三层职责 ``` ┌──────────────────────────────────────────────────────────────┐ │ DSH 宿主:tools / commands / skills 注册、审批、子进程、 │ │ spill、LLM、session log │ ├──────────────────────────────────────────────────────────────┤ │ 本插件:意图化工具面、policy、job 编排、随包知识、 │ │ 引擎管理、审计、结果整形 │ ├──────────────────────────────────────────────────────────────┤ │ asc 引擎(外置):全部 ASC API 调用、--read-only 硬门禁、 │ │ JSON 输出、自身版本 │ └──────────────────────────────────────────────────────────────┘ ``` **第 0 条边界**:插件永不内嵌 `asc`,也不通过 shell 调用;执行只经过 `ctx.subprocess` 的单一 spawn 点,argv 为数组。 --- ## 2. 模块地图 | 文件 | 职责 | 关键约束 | |---|---|---| | `src/index.ts` | 组合根:注册 policy、工具、skills、commands | 只注入所需服务;可选表面缺失时降级 | | `src/registry.ts` | **唯一命令表**:23 个 action 的 `kind` / argv 构造 / `needsConfirm` / `output` / `long` | 新增 action 只在这里加一行 | | `src/classify.ts` | 任意 argv 的分类器、独立报告文件副作用、已知无歧义布尔 flag | 保守:报告不降低 unknown 风险;类型不明时字面化覆盖两种解析 | | `src/approval.ts` | policy 矩阵、审批豁免、真实批准收据、pre-execute 与最终 guard | policy 的 SSOT;收据绑定单次执行与参数 | | `src/runtime.ts` | 执行编排:超时选择、`--output` 决策、projector、审计钩子 | 不直接 spawn,交给 `exec` | | `src/exec.ts` | **唯一 spawn 点**:argv 数组、env 构造、`@` 字面化、redact、截断/spill | 无 shell;写模式 tombstone `ASC_READ_ONLY` | | `src/format.ts` | 结果契约:summary / errorClass / hint / projector / `maxDataBytes`(UTF-8 字节) | 模型可见形状的唯一来源 | | `src/audit.ts` | 选开 JSONL:脱敏、`0o600`、best-effort | 失败不影响调用 | | `src/config.ts` | Schema 与默认值 | 默认值即安全边界 | | `src/compat.ts` | asc pin / 兼容区间 / release 资源 | 版本事实的 SSOT | | `src/control/binary.ts` | engine/runtime 共用的动态二进制与缓存路径解析 | 缓存解析为宿主侧绝对路径,诊断、普通工具、job 使用同一路径;provision 后重新解析 | | `src/control/engine.ts` | 引擎管理:locate / diagnose / provision / update check | provision 默认关、校验 SHA-256;取消信号透传 | | `src/capabilities.ts` | 能力卡数据:JOBS / TOOLS / BUNDLED_SKILLS | 与 registry / skills 保持一致 | | `src/jobs.ts` | 5 个工作流实现 | 嵌套调用走同一 runtime;runner 二次闸门 | | `src/skills.ts` | 随包 skill 的 provider(rank 600) | 不落用户目录 | | `src/commands.ts` | `/asc`、`/asc-doctor` | 只读、不产生模型消息 | | `src/tools/*.ts` | 5 个工具定义(schema、描述、呈现) | 薄;逻辑在 registry/runtime | | `skills/*/SKILL.md` | 模型知识:何时用哪个 job/tool、失败恢复 | 工具面变更必须同步 | | `scripts/vendor-path.mjs` | 开发脚本的 vendor 引擎路径 | 下载、verify、contract、audit、smoke、overlay 共用平台后缀 | --- ## 3. 一次调用的数据流 ``` 模型调用工具 → approval.inspectCall(工具名, 参数, config) # 分类 + confirm 事实 + 参数校验 → decideCall() # invalid / allow / ask / deny → approval.request() # ask 需真实 allowed-once,生成本次调用收据 → tools.guard # 最终复核 deny/confirm/收据,防 listener 绕过 → 工具 handler → runtime.runAction(tool, action) 或 runArgv # 超时、--output、审计 → registry spec.build(args) # validate → argv 尾部 → exec.execAsc() # globalFlags + literalize + spawn → format.formatResult() # summary / projector / data 闸门 → audit.record() # best-effort → 工具 output.render() ``` job 的差别:`asc_job` 一次调用内运行多步,每步都走同一 `runAction`/`runArgv`(超时、`--output`、审计),但**不会**重新经过宿主 `tools/pre-execute`;审批只在外层工具调用发生一次。`runJob` 在 runner 内再复核 `mode`/`confirm`,`runReadOnlyArgv` 对只读探针再跑一次分类器。 --- ## 4. 扩展点 | 要加什么 | 改哪里 | 必须同步 | |---|---|---| | 新 action | `src/registry.ts`(`define` 一行,含 `validate` 守卫)+ 所属工具 schema | 测试(registry + `test/tools.test.ts`)、`docs/COMMANDS.md`、`scripts/contract-combos.mjs`、相关 skill、CHANGELOG | | 新 job | `src/capabilities.ts` 的 `JOBS` + `src/jobs.ts` 实现 | 测试(jobs)、能力卡、对应 skill、`docs/COMMANDS.md` | | 新 skill | `skills//SKILL.md` + `capabilities.BUNDLED_SKILLS` | `test/skills.test.ts`、`docs/COMMANDS.md` | | 新 command | `src/commands.ts` | `test/commands.test.ts`、`docs/COMMANDS.md` | | 新配置项 | `src/config.ts` | 默认值安全侧、`docs/COMMANDS.md` 配置表、测试 | | 新宿主能力 | `src/index.ts` 的可选注册 | 缺失时必须降级为 no-op | **禁止**:绕过 registry 直接拼 argv;在工具层直接 spawn;把 policy 判断写进工具 handler。 --- ## 5. 失败与收口 | 失败 | 结果 | 模型可见 | |---|---|---| | 二进制缺失 | `errorClass: missing-binary` | hint: `brew install asc` / `ascPath` / provision | | 认证失败 | `auth` | hint: `asc auth status --validate` | | 参数/flag 错 | `usage`(exit 2) | hint: 查 `--help`;仅只读且明确拒绝 output flag 时单次重试 | | 状态冲突 | `state-conflict` | hint: 先查状态再重试 | | 超时/取消 | `timeout` / `cancelled` | 不自动重试 mutation | | 输出非 JSON | `parse` | `stderrTail` + 截断标记 | | 超大 `data` | `dataOmitted`(不落盘) | 缩窄查询;不产生 `spillPath` | | stdout 截断 | `truncated` + `spillPath` | 读 spill 文件 | | 限流 | `rate-limit` | 等待后重试,避免大页 `--paginate` | | 网络/Apple 5xx | `network` | 查 `asc system-status` 后重试 | | 其他 | `unknown` | 看 `stderrTail` | 完整 10 类 `errorClass` 见 [`COMMANDS.md §6`](./COMMANDS.md#6-结果契约);错误分类、重试边界与超时策略以 [`CONTRACT.md §3`](./CONTRACT.md#3-安全契约不可变) 与 `src/format.ts` 为准。 --- ## 6. 模块与契约的对应 | 契约条目 | 实现位置 | |---|---| | policy 矩阵(I1–I3) | `src/approval.ts` + `test/approval.test.ts` | | 参数守卫(审批前拒绝) | `src/registry.ts` 的 `validate` + `src/jobs.ts` 的 `validateJobArgs` + `test/registry.test.ts` | | `@` 字面化(I4) | `src/exec.ts` + `test/exec.test.ts` | | 单一执行点与 env(I5) | `src/exec.ts` | | 工具面(I6) | `src/index.ts` + `src/registry.ts` | | 知识随包(I7) | `src/skills.ts` + `package.json files` | | 引擎契约(I8) | `src/compat.ts` + `src/control/engine.ts` + `test/engine.test.ts` | | 审计(I9) | `src/audit.ts` + `test/audit.test.ts` | | 禁区(I10) | `src/classify.ts` | | 结果契约 | `src/format.ts` + `src/tools/common.ts` | | 版本事实 | `src/compat.ts` + `package.json` |