deepcode 首页

技术文档

实现细节:七个设计决定、六个模块与那条链、权限规则的写法、十阶段路线图、九条已知限制。 它是什么、怎么装、怎么用,看 首页。

7 条

七个设计决定

零运行时依赖

package.json 里只有 devDependencies,跑起来不下载任何东西。

更严的永远赢

固定优先级 deny > ask > allow,与写在哪一层、排第几都无关。

fail-closed 到底

非 TTY 默认拒绝;不认识的工具拒绝;tool.check 走到链尾无人应答也是 deny。

没有 --yes

写盘与执行命令都要有人按一次 y。

依赖边界由脚本守

contract/ 不许 import 任何东西,builtins/ 只许 import contract/。

账本不许自己违反自己

每次请求都带着工具清单,/context 的合计一度只算了系统提示与消息 —— 实测某一轮真切发出去约 448 token,账本报 11,其中 97.5% 是漏掉的工具。

内建工具不是特例

八个工具走同一条接口:清单来自注册面上 tool.call 的 matcher 取值, 再过白名单与 hidden 两道。core/tool-list.ts 是这一份清单的唯一算法。

6 个模块 · 1 条链

六个模块,共用一条链

每个调用点抛出一个事件,插件把自己的 hook 注册成中间件,按 tier 权威序串成一条链。 一个 hook 可以应答 / 改写 / 拒绝 / 委托下层。下面是这条链上的六个模块。

执行引擎所有调用从这里进

入口
engine.dispatch —— 每个调用点抛出一个事件
链
五个 tier 按权威序串成一条:prepend → user → append → builtin → core
续体
next(e) 是 Koa 式续体,不是事件发射器 —— 链尾只有一处,没有第二个地方可以漏

权限谁说了算,谁不能

来源
五层,从低到高:user → project → local → flag → policy
判定
deny > ask > allow —— 与写在哪一层、在同一数组里排第几都无关
上限
allow 的效力上限是把 ask 变成 allow;托管层能收紧,不能放宽

安全闸门工具调用必经

三道
tool.check(预览后停下等一个答案:y 这一次 / a 本次会话这一类 / 其余算拒绝)→ sandbox(只有 Bash,路径闸)→ 真进程 / 真文件
兜底
fail-closed —— 非 TTY 默认拒绝 · 链尾无人应答 = deny · 不认识的工具 = deny
位置
坐在链的下游 —— 任何 allow 规则都绕不过它

记忆与上下文记在哪、怎么接上

指令
AGENTS.md × 5 层拼进系统提示(每轮重算,改了当场生效)—— 五层里只有托管层能写
转录
用户级目录、0600 的 JSONL,--resume 接上一次会话
历史
~/.deepcode/history;/context 看账本(不报「还剩多少」),/compact 手动压、自动压缩默认关

工具与插件一条接口,没有硬编码表

工具
八个走同一条接口:六个内建 + Task / Skill(第一方插件给的)
插件
不 import 内核,只拿到注册回调;内建 mod 与第三方插件走同一个接口
边界
破坏依赖边界,pnpm lint 直接红 —— 不靠自觉

遥测默认关

开关
默认关。只有用户层能打开(项目级写的会被丢弃并出声),托管层能一句话关掉
内容
六个事件各写一行到用户级 telemetry.jsonl,没有网络上报
隐私
零自由文本 —— 任何一行不合规就整条丢掉,不是截断也不是改写
构建时的目录分层:谁许 import 谁
src/
  contract/    事件目录与契约类型 —— 不 import 任何东西
  core/        引擎:dispatch、链、tier                 → contract
  providers/   anthropic / openai 两种协议              → contract
  builtins/    六个内建工具                             → contract
  cli/         装配、REPL、渲染、设置、权限解析          → 上面全部
mods/          四个内建 mod(与第三方插件同一个接口)     → node: 内建 + contract + 自己
plugins/       第三方插件 —— 不 import 内核,只拿到注册回调

这张表就是 scripts/check-boundaries.mjs 里的规则表;破坏边界 pnpm lint 直接红。

42 个事件已声明,23 个已实现;八个工具里六个内建、两个由第一方插件提供 (Task / Skill)。其余 19 个事件用到时会抛 NotImplementedError。

5 层来源 · 3 组数组

权限:谁能放宽,谁不能

配置管连接(config.json),权限是另一套(.deepcode/settings.json), 三组数组 allow / ask / deny 都可以省略。五个来源从低到高: user → project → local → flag (--settings=)→ policy(托管)。

规则的写法与含义
写法含义
Bash该工具的所有调用
Bash(git push:*)命令行以 git push 开头,按空白 token 边界 —— git pushup ✗
Read(~/.ssh/**)file_path 按 glob 匹配:* 单段、 ** 跨段
其它工具带括号不支持:不命中,且启动时警告一次

: 是 token 边界,不是通配符;路径按绝对路径匹配(~ 会展开), 相对路径不会命中。

确认提示上除了 y(这一次)还有 a(本次会话的这一类)。 Bash 按归一后的那一条命令记,其余工具按整个工具记 —— 记住什么,按下的那一刻就打在屏幕上;只活在进程内存里,/permissions forget 一句话忘掉。 它与 settings 里的 allow 走同一条判定,所以一样掀不翻 deny。

前缀匹配不懂 shell 语法,「藏在后面」的命令它看不见。 Bash(git push:*) 拦得住 git push origin main; rm -rf ~, 但拦不住 true && git push origin main。所以 deny 是给手滑与误操作 加的一道闸,不是反对抗手段。

个人规则掀不翻任何一条 deny(allow 的效力上限是把 ask 变成 allow),会话里用 /permissions 看每条规则的来源。

全部完成

走到哪一步了

十个阶段全部完成。每阶段的出口标准与偏差记在 变更记录里。

  1. MVP

    「读 → 改 → 跑测试」的闭环;安全底线是每个会写盘或执行命令的动作按一次 y

  2. P1

    会话与体验:--resume 接上上次会话、/diff、OpenAI 兼容 provider

  3. P2

    安全与策略:五层设置来源、规则内核、托管策略掀不翻

  4. P3

    扩展面:不改引擎的插件、classic 钩子、只能拧紧不能拧松

  5. P4

    组合面:Task / Skill、沙箱、会话压缩、marketplace、遥测

  6. P5

    内建与分发:四个内建 mod、指令文件五层、托管三通道

  7. P6

    界面与交互:开场屏、活动区、内容面、两份账、历史与补全(全是行内增强,不占整屏)

  8. P7

    记忆与上下文:/context 账本(明说不报「还剩多少」)、系统提示每轮重算、/compact 能带一句说明、压缩后重新附上目录约定、两道断电器

  9. P8

    执行循环的收场:停止原因收敛成七个产品字面量、被截断就接着往下写(≤ 3 次,每次出声)、空步与炸在中途都不丢内容、两侧统一按白名单判「做完了没有」

  10. P9

    工具清单:一条清单三个消费者(模型请求 / /help / /tools)收敛到一个函数;/context 把工具算进合计;/tools 说清每个工具来自哪个插件、以及哪些没进来、为什么

  11. P10

    子代理的边界:子代理看得见哪些工具、能派多深,从调用方按「会话的全部」算挪进内核按「当前这一轮」算:嵌套一层拿不回被扣掉的面,深度上限(3 层)对 Task 与插件直派共用同一个数

9 条

已知限制

没有路径围栏

六个工具能碰到你进程权限内的任何路径,不限工作目录 —— 确认提示拦的是「要不要动手」,不是「动哪里」。

沙箱不是 OS 隔离

Bash 有一道可选的路径闸,但它靠切分 token 判路径,不是解析 shell: rm $(echo /etc/passwd) 绕得过去。

没有整屏 TUI、没有语法高亮

界面是逐行的纯文本流,markdown 只认一个子集,也不走 ui.render 事件。

hookify 的规则写坏了,工具会全停

算术写错、正则编译不过、钩子进程崩了 —— 一律非零退出,而 PreToolUse 上的非零就是 deny:Write / Edit / Bash 全部被拒绝,直到修好它。

子代理不知道项目规矩

子代理的系统提示只有 agent 文件里那段正文(为空时回落到会话的基础提示)—— AGENTS.md / CLAUDE.md 一个字都不进,工具面也只能是父级那一轮的子集。

转录里是代码

会话落成 JSONL,放在用户级目录(不进项目目录),权限 0600。 删掉那个文件就等于删掉那次会话 —— 没有别的副本。

被截断续写时,钱在悄悄花

回答撞上输出长度上限时会自动接着往下写,最多 3 次 —— 每一次都是一次真的模型请求。 这个 3 是我们自己定的数,不是测出来的。

账本全是估的

/context 按 4 字符 1 token 粗估,中英混排会失真;工具那一行算的是 名字 + 描述 + schema 的字符数,不等于适配器真正发出的字节数。

marketplace 只做本地索引

plugin add 从一份本地索引拷插件进落点:不 fetch、不 clone、没有签名与校验和, 也没有版本与升级。