mingdao-harness 0.6.7 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +132 -0
- package/README.md +18 -6
- package/SECURITY.md +128 -0
- package/docs/ARCHITECTURE.md +261 -79
- package/docs/CONFIG.md +10 -3
- package/docs/DEVELOPER.md +19 -13
- package/docs/MIGRATION-DEYI-v0.5.md +2 -2
- package/docs/PACK-API.md +4 -4
- package/docs/PROVIDERS.md +13 -2
- package/docs/SAVINGS-BENCHMARK.md +1 -1
- package/install.sh +14 -2
- package/package.json +12 -3
- package/src/help.js +6 -1
- package/src/hooks.js +27 -11
- package/src/ledger.js +1 -1
- package/src/packs.js +1 -1
- package/src/redact.js +26 -2
- package/src/replay.js +1 -1
- package/src/tools/bash.js +41 -1
- package/src/web/app.js +23 -2
- package/src/web/index.html +1 -0
- package/src/web/routes/domains/workspace.js +5 -2
- package/src/web/server.js +1 -1
- package/src/workspace.js +1 -1
- package/docs/AUDIT-v0.4.6.md +0 -241
- package/docs/AUDIT-v0.6.1-/347/254/254/344/270/211/346/226/271/346/212/245/345/221/212/347/231/273/350/256/260.md +0 -1252
- package/docs/CHANGELOG-PACK.md +0 -42
- package/docs/DESKTOP-EVALUATION.md +0 -48
- package/docs/EVALUATION-ROUND2.md +0 -58
- package/docs/EVALUATION-SYNTHESIS.md +0 -85
- package/docs/HANDOVER.md +0 -51
- package/docs/PLAN-v0.3.0.md +0 -109
- package/docs/PLAN-v0.3.2.md +0 -65
- package/docs/PLAN-v0.4.0.md +0 -38
- package/docs/PLAN-v0.4.1.md +0 -65
- package/docs/PLAN-v0.5.0.md +0 -171
- package/docs/PLAN-v0.6.0.md +0 -228
- package/docs/QA-REPORT.md +0 -335
- package/docs/QUALITY-REVIEW-0.1.68.md +0 -123
- package/docs/RELEASE-CHECKLIST.md +0 -468
- package/docs/RELEASE-TRAIN.md +0 -179
- package/docs/ROADMAP-NEXT.md +0 -55
- package/docs/STRATEGY-0.3.md +0 -121
- package/docs/STRATEGY-0.5.md +0 -262
- package/docs/STRATEGY-NEXT.md +0 -175
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,108 +1,290 @@
|
|
|
1
1
|
# MingDao-Harness 架构设计
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 版本口径:v0.6.8。本文档覆盖 `src/` 全部 **88** 个模块(`find src -name '*.js' | wc -l` = 88),
|
|
4
|
+
> 按层给出每个模块的职责与它守住的那条不变量;判定以文件头注释与实际实现为准,不做推断性描述。
|
|
5
|
+
>
|
|
6
|
+
> 阅读顺序建议:§2 模块索引(查「某件事归谁管」)→ §3 数据流(看一次回合怎么走)→ §4 已知边界(看它**保证不了**什么)。
|
|
7
|
+
> 用户视角的配置项见 [CONFIG.md](CONFIG.md),扩展契约见 [DEVELOPER.md](DEVELOPER.md) 与 [PACK-API.md](PACK-API.md)。
|
|
4
8
|
|
|
5
|
-
## 1.
|
|
9
|
+
## 1. 总体形态
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
| **Claude Code** | TUI 会话循环;工具循环(模型 ↔ 工具调用);权限系统(ask / acceptEdits / bypassPermissions + allow/deny 规则);`--continue`/`--resume` 会话续聊;headless `-p` 模式;AGENTS.md 目录约定 | 全部采纳(TUI + 三档权限 + JSONL 会话 + 单次提问 + AGENTS.md) |
|
|
10
|
-
| **OpenAI Codex CLI** | TypeScript 单体;skills;沙箱化 bash;config.toml 分层配置 | 采纳「配置分层 + 预设目录」;skills/沙箱进入路线图 |
|
|
11
|
-
| **DeepSeek-Harness** | Cordis「一切皆插件」组合架构;`llm-retry` 自动重试;`token-meter` 计量;context 预算与 compaction 分层;host/client 解耦 | 采纳「重试 + 计量 + 预算裁剪 + UI 与核心解耦」;以轻量注册表(Provider/Tool)替代重依赖的插件内核,保持零依赖 |
|
|
12
|
-
| **CodeWhale** | 单一耐用运行时(TUI / `exec` / Fleet 同内核);审批策略引擎独立(execpolicy);SQLite 持久化;OpenAI + Anthropic 双适配层;MCP/hooks 分层 | 采纳「权限引擎独立 + Provider 适配层」;`mingdao "…"` 单次提问即 headless 入口;SQLite/MCP/hooks 进入路线图 |
|
|
13
|
-
|
|
14
|
-
**结论**:一个 Harness 的最小可行内核 = `模型 Provider 层 + 工具注册表 + 权限引擎 + 上下文管理 + 会话持久化`,UI 只是内核的一个适配器。MingDao 按此分层实现。
|
|
15
|
-
|
|
16
|
-
## 2. 总体架构
|
|
11
|
+
MingDao 是一个「模型循环 + 工具 + 权限」内核,纯 Node.js ≥ 18.17、纯 ESM、**零 npm 运行时依赖**,
|
|
12
|
+
无构建步骤。能力以库形式导出(`src/index.js`),UI 只是内核的一个适配器。
|
|
17
13
|
|
|
18
14
|
```
|
|
19
|
-
|
|
20
|
-
│
|
|
21
|
-
│
|
|
22
|
-
│
|
|
23
|
-
|
|
24
|
-
│ io 接口(print/ask/confirm
|
|
25
|
-
|
|
26
|
-
│
|
|
27
|
-
│
|
|
28
|
-
│
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
│
|
|
34
|
-
│
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
│ 自定义 Provider
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
15
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
16
|
+
│ UI 适配层(可替换) │
|
|
17
|
+
│ TUI(ui.js) · 单次提问/headless(cli.js) │
|
|
18
|
+
│ WebUI(web/**,HTTP + SSE)· 后台任务(tasks/worker.js) │
|
|
19
|
+
└───────────────────────────┬──────────────────────────────────┘
|
|
20
|
+
│ io 接口(print / writeText / writeReasoning / ask / confirm / choose)
|
|
21
|
+
┌───────────────────────────▼──────────────────────────────────┐
|
|
22
|
+
│ Agent 核心循环(agent.js) │
|
|
23
|
+
│ 上下文裁剪 → Provider 流式请求 → PreToolUse 钩子 → 约束引擎 │
|
|
24
|
+
│ → 权限引擎 → 工具执行 → PostToolUse 钩子 → 结果回填 → 循环 │
|
|
25
|
+
│ (≤24 步/轮,最多 3 轮续跑)→ 输出 + 渲染 + 账本 runEnd │
|
|
26
|
+
└───┬───────────────┬───────────────┬───────────────┬──────────┘
|
|
27
|
+
│ │ │ │
|
|
28
|
+
┌───▼─────┐ ┌──────▼───────┐ ┌─────▼───────┐ ┌────▼──────────┐
|
|
29
|
+
│Provider │ │ 权限 permissions│ │ 工具 tools/ │ │ 上下文 context │
|
|
30
|
+
│providers/│ │ + 约束 constraints││ + 围栏 ssrf/ │ │ + tokenizer │
|
|
31
|
+
│ 路由/计价 │ │ + Hooks │ │ net-guard │ │ + compact │
|
|
32
|
+
└───┬─────┘ └──────────────┘ └─────────────┘ └───────────────┘
|
|
33
|
+
│
|
|
34
|
+
┌───▼──────────────────────────────────────────────────────────┐
|
|
35
|
+
│ OpenAI 兼容协议(HTTP + SSE)· 自定义 Provider 模块(<home>/providers/*.mjs)│
|
|
36
|
+
└──────────────────────────────────────────────────────────────┘
|
|
37
|
+
|
|
38
|
+
横切基础设施:config.js / credentials.js(凭证与配置分离)· atomic-write.js(原子写 + 跨进程锁)
|
|
39
|
+
session.js / ledger.js / audit.js(三类落盘记录,语义各不相同)
|
|
43
40
|
```
|
|
44
41
|
|
|
45
|
-
|
|
42
|
+
技术决策与理由:
|
|
46
43
|
|
|
47
44
|
| 决策 | 选择 | 理由 |
|
|
48
45
|
| --- | --- | --- |
|
|
49
|
-
| 运行时 | Node.js ≥ 18
|
|
50
|
-
|
|
|
51
|
-
| 模型接入协议 | OpenAI 兼容 chat/completions 作为通用层 | DeepSeek/OpenAI/Qwen/GLM/Kimi/大量网关同协议;非兼容协议走自定义 Provider 模块 |
|
|
46
|
+
| 运行时 | Node.js ≥ 18.17,纯 ESM,零 npm 依赖 | 内置 `fetch`/`readline`/`node:http` 覆盖全部需求;免构建、免依赖下载 |
|
|
47
|
+
| 模型协议 | OpenAI 兼容 `chat/completions` 为通用层 | DeepSeek/OpenAI/Qwen/GLM/Kimi 与多数网关同协议;非兼容协议走自定义 Provider 模块 |
|
|
52
48
|
| 工具协议 | OpenAI function-calling Schema | 与协议层一致,模型无需适配 |
|
|
53
|
-
| 会话存储 | JSONL 追加写 |
|
|
54
|
-
| 密钥存储 | 独立凭证库 `credentials.json`(600
|
|
55
|
-
| 权限 | 独立权限引擎,默认 ask |
|
|
56
|
-
| 上下文 |
|
|
57
|
-
| 扩展点 | Provider 模块 / Tool 注册表 / io 接口 /
|
|
49
|
+
| 会话存储 | JSONL 追加写 | 零依赖、可流式恢复、人类可读;检索另建倒排索引(session-index.js) |
|
|
50
|
+
| 密钥存储 | 独立凭证库 `credentials.json`(600) | `config.json` 可分享可提交,密钥只在本机;`mingdao key` 命令族管理 |
|
|
51
|
+
| 权限 | 独立权限引擎,默认 `ask` | 写文件/执行命令默认逐次确认;`deny` 优先于 `allow` |
|
|
52
|
+
| 上下文 | 官方 BPE 词表精确计数 + 启发式兜底 | `tokenizer.js` 内置官方词表(`assets/tokenizer-data.json.gz`,762,629 B);无词表的模型才回退估算 |
|
|
53
|
+
| 扩展点 | Provider 模块 / Tool 注册表 / io 接口 / Pack / Preset / Hooks / MCP | 零依赖注册表实现,不引入插件内核 |
|
|
54
|
+
|
|
55
|
+
## 2. 模块索引
|
|
56
|
+
|
|
57
|
+
以下逐文件列出 `src/` 全部 88 个模块。每条格式为「职责 —— 关键不变量/边界」。
|
|
58
|
+
|
|
59
|
+
### 2.1 内核循环与上下文(6)
|
|
60
|
+
|
|
61
|
+
- `agent.js` —— Agent 核心循环:消息 → 模型(流式)→ 钩子 → 约束 → 权限 → 工具执行 → 回填 → 循环,直至模型给出纯文本;附带子代理(task 工具)、todo 状态、undo 备份仓、Ctrl+C 中断 —— 每回合结局由 `outcome` 单一来源收敛,返回值与账本 `runEnd` 必须同源。
|
|
62
|
+
- `context.js` —— token 计数与预算裁剪:恒保留首条 system,从尾部向前取到预算,被裁剪时插入说明消息;工具结果上限 20000 字符(`TOOL_RESULT_LIMIT`)—— 计数优先走 `tokenizer.js` 精确计数,只在无词表模型上回退启发式;截断按码点安全切分,不留孤立代理项。
|
|
63
|
+
- `tokenizer.js` —— 精确 tokenizer:内置 DeepSeek 官方词表(`assets/tokenizer-data.json.gz`)做字节级 BPE 计数,内容级缓存使同一文本重复计数为 O(1);非 DeepSeek 模型回退启发式估算(英文≈4 字符/token、CJK≈0.75 token/字)—— 只用于上下文预算计数,不输出 token id;词表加载失败会重试而非终生锁死。
|
|
64
|
+
- `routing.js` —— 自动模型路由:长文本 + 规划关键词 → planner,极短指令 → executor,其余用 executor 模型做一次约几十 token 的分类(结果按文本哈希 LRU 缓存);执行类会话粘滞,避免 pro⇄flash 抖动破坏缓存前缀 —— 子代理固定走 executor。
|
|
65
|
+
- `titles.js` —— 会话标题自动生成:首轮完成后用 executor 模型生成简短中文标题并重命名会话文件(`autoTitle: false` 可关)—— 生成失败只跳过,不影响回合结论。
|
|
66
|
+
- `model-caps.js` —— 模型能力解析的单一来源:上下文窗口 / 单次最大输出 / 是否本地部署,供预算推导、超时、工具截断统一引用 —— 本地判定复用 `ssrf-guard.js#isPrivateHost`,不另维护一份;本地小模型自动收紧预算与超时。
|
|
67
|
+
|
|
68
|
+
### 2.2 Provider 与计价(8)
|
|
69
|
+
|
|
70
|
+
- `providers/index.js` —— Provider 注册表与工厂:`resolveProviderConfig`(模型预设 → 服务商预设 → config → 凭证库/环境变量)、超时与 429/5xx 指数退避重试、自定义模块加载(`<home>/providers/<name>.mjs`)—— 解析优先级只此一份。
|
|
71
|
+
- `providers/openai-compatible.js` —— OpenAI 兼容协议的 HTTP + SSE 流式客户端:增量解析容错跨 chunk 断行、分片 tool_calls 拼接、`reasoning_content` 分流 —— 只负责协议搬运,不感知权限与工具语义。
|
|
72
|
+
- `models.js` —— 模型与服务商预设目录:上下文窗口 1M、`maxOutputCeiling` 384K、pro/flash 的温度与输出上限、缓存命中价差(命中价 = 未命中 1/30)、峰谷价 —— 默认模型名与价格表的唯一来源,禁止在别处写模型名字面量。
|
|
73
|
+
- `model-discovery.js` —— 模型动态发现:按已设 Key 的服务商拉取线上 `/models` 名单,缓存 `<home>/model-cache.json`(TTL 1 小时)—— 线上名单优先,预设只作无网络回退;未收录模型走通用默认参数、计价显示 n/a。
|
|
74
|
+
- `pricing.js` —— 费用估算:缓存感知计价(命中部分按 cacheHit 价,未命中按 input 价)、峰谷判定锚定北京时间(高峰 = 周一至周五 9:00–12:00 与 14:00–18:00,其余含午间与周末为闲时半价)、避峰顺延(`deferToOffpeak`)—— 峰谷按**请求发起时刻**判定,非法时区回退必须与日界/护栏同源;价格可按 `pricing.overrides` 覆盖。
|
|
75
|
+
- `cachestats.js` —— 缓存命中统计:每轮用量落 `<home>/cache-stats.jsonl`(`{at, model, prompt, completion, hit, miss, cost, saved}`),支撑命中率仪表盘与 `/cost` 分解 —— 区分 ENOENT 与读失败(`listCacheStatsStrict`),读失败不得当成 0。
|
|
76
|
+
- `cost-guard.js` —— 费用护栏:按北京时间自然日累计真实费用,`warn` 线提醒、`limit` 线按 `action` 处理(`warn` / `block` / `downgrade` 到便宜模型)—— 长回合把在途费用并入今日已用,否则统计文件不变会烧穿日限。
|
|
77
|
+
- `batch.js` —— Batch API 半价通道:单轮批量任务(无工具、无流式)走 OpenAI 兼容批处理协议(`/files` → `/batches` → 轮询 → 下载结果,DeepSeek 风格结果端点回退 OpenAI)—— 端点不可用(404/405)必须明确报错告知网关不支持,**绝不静默假装成功**;计费按闲时全未命中 × 0.5,结果记入 `cachestats.js`(`batch: true`)。
|
|
78
|
+
|
|
79
|
+
### 2.3 工具与围栏(12)
|
|
80
|
+
|
|
81
|
+
- `tools/index.js` —— 工具注册表:模型可见的工具 Schema(OpenAI function-calling 格式)与执行分发器,含第三方 `registerTool` 与 `config.tools` 挂载 —— Schema 与 dispatch 分支必须成对增加。
|
|
82
|
+
- `tools/fs-tools.js` —— 文件系统工具 read / write / edit / ls / glob / grep:统一返回 `{ok, output?|error?}` —— 工作空间围栏 `withinRoot()` 对悬空软链递归复检、深度超 16 层 fail-closed;写/编辑自动备份供 undo。
|
|
83
|
+
- `tools/bash.js` —— bash 工具:子进程执行、超时强杀、输出截断;沙箱三档 `off` / `readonly`(全盘只读 + /tmp tmpfs)/ `safe`(只读文件系统 + `--unshare-net` 断网 + 工作目录可写)—— 仅 Linux + bubblewrap 可用,缺失时降级为 `off` 并在结果中注明,不静默假装沙箱。
|
|
84
|
+
- `tools/fetch.js` —— HTTP 只读抓取工具:GET 任意 http(s) URL,返回文本(512KB 上限,正文截 20K)—— 只保留工具契约(参数校验、呈现口径、错误措辞),实际抓取整条走 `safeFetchText`。
|
|
85
|
+
- `tools/git.js` —— git 只读工具:仅允许 `status/log/diff/show/blame/rev-parse/branch/tag/ls-files/shortlog`,经 `execFile` 无 shell 执行 —— 白名单之外的子命令一律拒绝。
|
|
86
|
+
- `permissions.js` —— 权限引擎:三档 `ask`(默认,只读自动放行)/ `auto` / `readonly`,规则支持「工具名」与「工具名:参数前缀」模式 —— `deny` 优先于 `allow`;非法模式一律退回 `ask`。
|
|
87
|
+
- `constraints.js` —— 约束引擎 v1(Pack API v1):PreToolUse(`tool-deny` / `tool-arg-require` / `arg-forbid` / `confirm`)、PostToolUse(`completeness` / `result-forbid`)、输出前(`output-forbid`)三个执行时机 —— **只能收紧、不能放松**,不授予权限也不改变权限引擎判定;pattern 类约束装载即 fail-closed 校验,非法正则必须报错而非静默永不命中。
|
|
88
|
+
- `regex-safety.js` —— 灾难性回溯(ReDoS)判定的单一来源:只拦「被量词修饰的组内出现歧义分支/嵌套量词」这类教科书形状 —— 判定对象来自第三方 Pack 与用户配置、匹配对象是模型输出,装载时 fail-closed 拒绝;`(a|b)+`、`(ab)+` 等常见安全写法不误伤。
|
|
89
|
+
- `ssrf-guard.js` —— SSRF 判定 + DNS 钉扎的单一来源:字面量私网/回环/元数据拒绝(含 IPv6 全形态、IPv4-mapped、NAT64)、域名必须解析且逐个地址判定、校验通过的地址 `pinned` 给连接层 —— 三条口径必须同时满足,任何一条不确定就 fail-closed;解析失败不能当安全判据。
|
|
90
|
+
- `safe-fetch.js` —— 带逐跳 SSRF 复检的文本下载单一来源:`redirect:'manual'` + 每跳过 `ssrf-guard` + 跳数上限,每跳显式过出网闸门并记账 —— 新增下载路径必须走这里,不得再写第二份;跨源重定向按反向白名单剥头。
|
|
91
|
+
- `net-policy.js` —— 出网白名单策略:纯函数,不碰 IO、不改全局状态,便于单测与文档化 —— 命中哪条规则、拦还是放,判定只此一份。
|
|
92
|
+
- `net-guard.js` —— 出网闸门:在 `globalThis.fetch` 收口做出口判定 + 记账,`guardEgress()` 供 `node:http(s)` 路径(safe-fetch、sync 的 rawRequest)复用 —— **默认不安装**(未配置 `config.net` 时完全不介入);只覆盖内核自己发起的请求,不代表整机不出网。
|
|
93
|
+
|
|
94
|
+
### 2.4 会话 / 记忆 / 任务(10)
|
|
95
|
+
|
|
96
|
+
- `session.js` —— 会话持久化:`<home>/sessions/<时间戳>-<随机>.jsonl` 每轮追加,`--continue` 载入最近一次,`--resume` 打开选择器 —— 会话文件是消息结构的唯一权威,检查点等旁挂状态另存侧车文件。
|
|
97
|
+
- `session-index.js` —— 会话检索索引:词表倒排 + 按 mtime/size 的增量同步,分片存 `<home>/sessions-index/<2位sha1前缀>.json`(256 片)—— 未变化直接用缓存词表,>8MB 文件跳过并移除条目;中文按 bigram + 单字、查询串同口径分词后 AND 匹配。
|
|
98
|
+
- `memory.js` —— 长记忆与自主进化:用户记忆 `~/.mingdao/AGENTS.md`(手动 + 会话结束自动提取用户偏好并去重追加)、会话日志 `journal.jsonl`(默认不注入系统提示,`--journal` / WebUI 勾选才取最近 3 条)—— 自动提取用 executor 模型(约几十 token),`autoMemory: false` 可关。
|
|
99
|
+
- `tasks.js` —— 多会话后台任务面板:`mingdao run` 拉起独立 worker 子进程,状态落 `<home>/tasks/<id>.json`,命令族 `run` / `tasks` / `tasks watch` / `tasks kill` —— taskId 用不可枚举随机值;kill 前必须过 PID 归属校验(见 `proc.js`)。
|
|
100
|
+
- `tasks/worker.js` —— 后台任务 worker:独立进程执行一轮任务并写状态文件,复用 Agent 核心(权限/模型/MCP/标题/会话/入账/通知/自动同步)—— 无交互,`ask` 权限降级为 `readonly`。
|
|
101
|
+
- `task-state.js` —— 任务检查点:把「一轮跑满即止」升级为「可续跑」,状态存 `<home>/taskstates/<会话名>.json`(原子写 + 锁)—— 只记 `cap` / `interrupted` 两种可续跑状态,任务正常完成即清除。
|
|
102
|
+
- `schedule.js` —— 任务队列与调度:单 `schedule-daemon` 进程以协程监督全部定时任务(一次性/周期)与依赖编排(`after`/链式),到期拉起 worker;状态落 `<home>/schedule/<id>.json`,`daemon.pid` 防重复 —— 旧式逐任务 sleeper 只作 daemon 启动失败的兜底;`tasks`/`run` 触发时自动 reconcile 补挂到期任务。
|
|
103
|
+
- `compact.js` —— 上下文自动压缩:超预算且被裁段落达阈值时,先用 executor 模型压成摘要以单条 user 消息注入,替代静默失忆 —— 被裁段落不足阈值仍走普通裁剪;摘要失败绝不阻塞会话,异常一律回退普通裁剪;保留段头部经 `cleanToolPairing` 清洗孤儿 tool 消息。
|
|
104
|
+
- `prompts.js` —— 系统提示词构建:基础角色 + 用户记忆 + 技能清单 + 项目 AGENTS.md + Pack 提示段(`packPromptBlock`)—— 记忆类内容必须「关不住围栏」,零宽/双向控制字符需剥离;断行/围栏闭合由 `fencedBlock` 统一处理。
|
|
105
|
+
- `workspace.js` —— 会话级工作空间:登记/切换/重命名/删除,每个会话记住自己的项目目录,多任务并行不串目录 —— 本模块加锁函数**全部**使用异步版 `withFileLock`,因为它正好在 WebUI 请求路径上(同步版会冻结整个事件循环)。
|
|
106
|
+
|
|
107
|
+
### 2.5 合规与账本(3)
|
|
108
|
+
|
|
109
|
+
- `ledger.js` —— 执行账本:一次回合 = 一个文件,事件流(`runStart` / `toolCall` / `constraint` / `permission` / `usage` / `netEgress` / `runEnd`)可导出、可校验、可回放 —— 三条不可让步的纪律:写入即脱敏、摘要替代原文(`*Digest`)、降级必须可见;`runEnd` 额外写封条侧车文件,使「尾部截断但链内自洽」可检出。
|
|
110
|
+
- `audit.js` —— 工具调用审计日志:每次调用(含被拒/被钩子阻止/参数解析失败)落 `~/.mingdao/audit.jsonl`(600),纯追加 + >20000 行保留最近 10000 行 —— 记录做轻量脱敏,`config.audit: false` 可关(默认开)。
|
|
111
|
+
- `replay.js` —— 决策回放:把账本里的工具调用序列按**当前**权限/约束栈重新评估 —— 承诺「决策回放」(历史上这些操作用今天的规则重判会被拦哪些),**不承诺**模型级回放;用的是账本中已脱敏的参数,依赖被掩码内容的规则可能不命中。
|
|
112
|
+
- `redact.js` —— 统一脱敏:`redactSecrets`(密钥前缀/Bearer/URL 内嵌凭据)与 `redactSensitive`(叠加私网 IP + 家目录掩码)—— 审计/日志/会话/诊断/错误消息共用同一套规则,禁止各层自扫门前雪。
|
|
113
|
+
|
|
114
|
+
### 2.6 同步与共享(2)
|
|
115
|
+
|
|
116
|
+
- `sync.js` —— 云同步客户端:会话 JSONL 跨设备同步,配置在 `config.json` 的 `sync`(无秘密),凭证在 `credentials.json` 的 `sync`(600)—— 冲突规则绝不丢数据:push 前先把远端备份为 `.server-<时间戳>.jsonl`,pull 时本地不动、远端写 `.remote-<时间戳>.jsonl`;`auto` 静默推送失败不影响对话。
|
|
117
|
+
- `sync-server.js` —— 云同步服务端(零依赖 `node:http/https`):账号 + 设备 token + 会话存储,数据布局 `<DATA_DIR>/users.json`、`devices.json`、`data/<用户名>/sessions/` —— 密码存 `sha256(salt:password)`,设备 token 只存哈希;注册开关 `open` / `invite` / `closed`。
|
|
58
118
|
|
|
59
|
-
|
|
119
|
+
### 2.7 Web 层(13)
|
|
60
120
|
|
|
61
|
-
|
|
62
|
-
|
|
121
|
+
- `web/server.js` —— WebUI 服务器:零依赖 `node:http` + SSE,复用 `createAgent` + `createPermission` + `createWebIO` —— 非回环绑定强制访问令牌(参数/配置/本次随机生成,`timingSafeEqual` 比对);Host 白名单防 DNS rebinding;同一会话并发回合由进程级 `busySessions` 串行化。
|
|
122
|
+
- `web/routes/api.js` —— API 编排器:访问控制(token / Host / CSRF)→ 静态壳资源 → 按域分发 —— 跨站浏览器请求按 `Sec-Fetch-Site` 一律 403。
|
|
123
|
+
- `web/routes/domains/config.js` —— 配置域:`/api/state` `/api/config` `/api/models-config`,服务商 Key 管理、自定义模型增删改、API 地址覆盖、系统状态快照 —— `endpointChangeGuard` 防「改地址把 Key 送到别处」。
|
|
124
|
+
- `web/routes/domains/sessions.js` —— 会话域:`/api/sessions` `/api/session` `/api/draft`,列表/搜索、载入(聚焦工作空间)、重命名/删除、按会话隔离的草稿槽。
|
|
125
|
+
- `web/routes/domains/skills.js` —— 技能域:`/api/skills` `/api/skill-library` `/api/mcp-presets`,技能列表/安装/卸载、内置库与线上注册表搜索、MCP 预设接入。
|
|
126
|
+
- `web/routes/domains/schedule.js` —— 调度域:`/api/schedule` `/api/tasks`,定时任务增删暂停恢复/链式编排,聊天 SSE 任务 + 后台 worker + 调度任务合并面板。
|
|
127
|
+
- `web/routes/domains/sync.js` —— 同步域:`/api/sync` `/api/sync-conflicts`,登录/登出/推拉/改密/分享与冲突处理。
|
|
128
|
+
- `web/routes/domains/workspace.js` —— 工作空间域:`/api/workspaces` `/api/fs-browse`,登记/切换/重命名/删除与受限目录浏览 —— 登记闸门与浏览基目录共用同一份 `allowedRoots`。
|
|
129
|
+
- `web/routes/domains/misc.js` —— 杂项域:`/api/chat` `/api/permission` `/api/abort` `/api/memory` `/api/cache-stats`,对话 SSE 流(并发上限 + 生命周期计数)、权限确认、中断、长期记忆、费用/缓存统计。
|
|
130
|
+
- `web/constants.js` —— 前端与服务端共享常量:并发上限 8(`MAX_CONCURRENT`)、附件数与体积上限、单文件读取上限 —— 数值只在此定义,防客户端预检/服务端校验/工具读取三处漂移。
|
|
131
|
+
- `web/attachments.js` —— 聊天附件构造(纯函数):图片转 `data:` URL(仅视觉模型,单张 ≤5MB、最多 4 个),文本文件拼接进消息(≤200KB)—— 服务端按同一组常量校验。
|
|
132
|
+
- `web/web-io.js` —— WebUI 的 io 适配器:把 Agent 的 io 事件翻译为 SSE 事件,权限确认/选择类交互经 `askHandler` 转发到浏览器模态框并等待 `POST /api/permission` 应答。
|
|
133
|
+
- `web/util.js` / `web/app.js` —— 前端:`util.js` 是零状态工具(选择器/转义/Markdown/格式化),`app.js` 是页面主脚本(消息渲染与轨迹、设置面板、自绘气泡与模态框)—— 地址带 `?token=` 时记入 `sessionStorage` 并从地址栏移除,此后请求统一附加 `X-MingDao-Token`。
|
|
63
134
|
|
|
64
|
-
###
|
|
65
|
-
- `openai-compatible.js`:SSE 增量解析(容错跨 chunk 断行、分片 tool_calls 拼接、`reasoning_content` 渲染);
|
|
66
|
-
- `index.js`:`resolveProviderConfig`(模型预设 → 服务商预设 → config → 凭证库/环境变量)、超时 + 429/5xx 指数退避重试、自定义模块加载。
|
|
135
|
+
### 2.8 CLI 层(13)
|
|
67
136
|
|
|
68
|
-
|
|
69
|
-
|
|
137
|
+
- `cli.js` —— CLI 入口与唯一 argv 解析点:初始化向导、凭证管理、单次提问、交互式会话分发;会话能力含 `/plan` `/compact` `/init` `/memory` `/skills` `/mode` `/verbose` `/status` `/cost` —— 遇第一个位置参数即停止解析全局 flag,子命令 flag 交子命令自行解析(防 `mingdao run "任务" --model X` 被顶层剥除)。
|
|
138
|
+
- `commands/repl.js` —— 交互式 TUI REPL 主循环:斜杠命令 + 多行输入 + 回合执行 + 自动压缩/标题/记忆/同步。
|
|
139
|
+
- `commands/key.js` —— `mingdao key`:status / set / remove / import,凭证库读写与掩码展示。
|
|
140
|
+
- `commands/update.js` —— `update` / `rollback` / `batch` / `cost` / `audit` 子命令 —— 每个 handler 返回 `true` 表示已处理,`false` 表示按普通提问继续(子命令劫持防护)。
|
|
141
|
+
- `commands/ledger.js` —— `mingdao ledger`:list / show / export(json|md,脱敏)/ verify(哈希链 + 封条)/ replay —— 输出必须写明它证明不了什么:哈希链只证明自写入后未被改动,不含可信时间戳,导出物字段经过截断。
|
|
142
|
+
- `commands/net.js` —— `mingdao net`:report(`[--since 7d|24h|N] [--json]`)与 policy —— 用途是自证「内核没有偷偷外传」,因此输出必须同时说清边界。
|
|
143
|
+
- `commands/pack.js` —— `mingdao pack`:list / trust / untrust / verify / new / info —— `pack verify` 默认**只做静态校验、不 import Pack 代码**(在 CI 上执行被审仓库代码是越权),要执行须显式 `--runtime`。
|
|
144
|
+
- `commands/schedule.js` —— `mingdao tasks` / `mingdao schedule`:任务与定时任务的增删改查、暂停恢复、链式编排、终止与守护进程状态。
|
|
145
|
+
- `commands/skill.js` —— `mingdao skill` / `web` / `sessions`:技能安装/卸载/重装/信任、启动 WebUI、会话检索 —— `--auth-token=-` 从 stdin 读,避免令牌进 argv/shell 历史。
|
|
146
|
+
- `commands/sync.js` —— `mingdao sync`:login / logout / passwd / share / shares / accept / unshare / conflicts / conflict-resolve / push / pull / status。
|
|
147
|
+
- `commands/workspace.js` —— `mingdao workspace` 与 `mingdao mcp preset`:工作空间登记切换、MCP 生态预设列出与接入。
|
|
148
|
+
- `commands/desktop.js` —— `mingdao desktop`:定位仓库、检查 `desktop/node_modules` 里的 Electron 并拉起,缺依赖给出镜像安装指引。
|
|
149
|
+
- `commands/diagnose.js` —— `mingdao diagnose`:只读打包诊断信息(环境/版本/config 脱敏/日志尾/审计尾/工作空间),凭证库只注明存在不读内容 —— 单文件输出,便于贴到反馈渠道。
|
|
70
150
|
|
|
71
|
-
###
|
|
72
|
-
渐进式披露:技能清单(名称+描述)注入系统提示,模型按需用 `skill` 工具加载 SKILL.md 全文。三级来源与同名覆盖优先级:**用户级 `~/.mingdao/skills/` > 项目级 `<项目>/.mingdao/skills/` > 内置 `<安装包>/skills/`**(借鉴 DeepSeek-Harness 的 SKILL.md 格式,内置 14 个常用技能)。
|
|
151
|
+
### 2.9 运行时基础设施(19)
|
|
73
152
|
|
|
74
|
-
|
|
75
|
-
|
|
153
|
+
- `config.js` —— 配置管理:`<mingdao-home>/config.json`(默认 `~/.mingdao/config.json`)与初始化向导,优先级「命令行 > 环境变量 > config.json > 内置预设」—— 文件**不含任何密钥字段**;损坏配置会被隔离(`quarantineCorruptConfig`)而非静默重置。
|
|
154
|
+
- `credentials.js` —— 凭证管理:Key 存 `<home>/credentials.json`(600,保存时 chmod 收权),解析优先级「环境变量 > 凭证库 > config.json 显式字段(兼容旧版)」—— 绝不写入仓库、绝不写入 `config.json`;提供 `maskKey` 脱敏。
|
|
155
|
+
- `atomic-write.js` —— 原子写与跨进程互斥:`atomicWriteFileSync`(tmp 名含 pid + 随机后缀,写完 rename)、`withFileLockSync`(O_EXCL lockfile + 超时 + 陈旧锁回收)、`withFileLock`(同语义异步版)—— 常驻服务请求路径必须用异步版:同步版用 `Atomics.wait` 会让整个事件循环停摆。
|
|
156
|
+
- `proc.js` —— 进程身份核验:判断「pid 活着」与「pid 仍属于我启动的那个进程」;判定语义严格区分 `true`(命令行含 needle)/ `false`(明确不是自己的进程,绝不能杀)/ `null`(无从判断,由调用方 best-effort)—— 把 `null` 当 `false` 就会误杀复用 pid 的无关进程;同时提供 EPIPE 管道守卫。
|
|
157
|
+
- `hooks.js` —— 生命周期钩子(PreToolUse / PostToolUse):向子进程 stdin 写 JSON,`PreToolUse` 的 stdout `{decision:"block", reason}` 可阻止执行 —— matcher 支持精确名、逗号分隔多名、`*` 通配;PostToolUse 只记录不阻塞。
|
|
158
|
+
- `mcp.js` —— MCP 客户端(零依赖 stdio 传输 + JSON-RPC 2.0):配置 `config.json` 的 `mcpServers`,工具命名 `mcp__<服务器>__<工具>` 后与内置工具合并 —— 服务器以子进程运行,其网络行为不受 `net-guard.js` 约束。
|
|
159
|
+
- `mcp-presets.js` —— MCP 生态预设:9 个官方常用服务器的一键接入目录 —— 每个可用预设**钉死版本**(`<pkg>@<version>`),npx 不再按 dist-tag 漂移;升级是改这里版本的**有意动作**。
|
|
160
|
+
- `skill-registry.js` —— 线上技能库客户端:默认仓库 registry 的 `registry/index.json`(github/gitee/gitcode 三镜像回退),`MINGDAO_REGISTRY_URL` 可指向企业内网自建;本地缓存 TTL 1 小时 —— 逐文件 sha256 校验,索引缺哈希时 **fail-closed 拒绝安装**。
|
|
161
|
+
- `skill-lib.js` —— 技能库安装器:来源四选一(内置库名 / 本地目录 / 远程 URL / git 仓库),统一装到用户级 `<home>/skills/<name>/` 并记录 `.mingdao-source.json` —— 下载路径必须走 `safeFetchText`,不得自己写第二份。
|
|
162
|
+
- `skills.js` —— Skills 技能系统:三级来源覆盖优先级「用户级 `<home>/skills/` > 项目级 `<项目>/.mingdao/skills/` > 内置 `<安装包>/skills/`」,每个技能一个目录含 `SKILL.md` —— 清单(名称+描述)注入系统提示,全文按需用 `skill` 工具加载(渐进式披露);项目级技能来源不可验证,标注提示且 `disableProjectSkills` 可整层关断。
|
|
163
|
+
- `presets.js` —— Agent Preset:把「系统提示 + 工具集 + 权限 + 模型 + 参数」打包为可安装/分享的 JSON;发现顺序「项目级 > 用户级 > 内置」—— 未知字段校验报错(防拼写错误静默失效);`model` 只是建议,WebUI 以用户当前模型为准。
|
|
164
|
+
- `packs.js` —— 垂域 Pack 加载器(Pack API v1):manifest(`pack.json`)+ contributions(`pack.mjs`)—— 坏 Pack 绝不阻塞启动(每条贡献独立 try/catch,失败只 warn 并跳过);加载幂等;静态校验与动态 import 分离;`permissions` 只是**声明**,内核不据此强制。
|
|
165
|
+
- `autostart.js` —— 开机自启:Linux XDG `.desktop` / Windows 启动文件夹 `.bat` / macOS LaunchAgent plist —— 命令 `mingdao autostart on|off|status`。
|
|
166
|
+
- `notify.js` —— 桌面通知(零依赖):后台任务完成/失败时经 `notify-send` / `osascript` / PowerShell 弹出 —— `config.notify` 可关,通知失败静默忽略,绝不影响任务流程。
|
|
167
|
+
- `update.js` —— 自更新:`update`(拉取并跑冒烟测试,失败自动回滚)、`update --check`、`rollback`(依据 `<home>/update-state.json`)—— 安装形态是仓库 + 全局符号链接,仓库更新即命令更新。
|
|
168
|
+
- `log-writer.js` —— 统一日志写入器:追加写 + 超限轮转(改名式,不做 O(n) 全量重写)—— 桌面主进程与 Web 服务端共用同一实现,消除双文件口径漂移。
|
|
169
|
+
- `ui.js` —— TUI 输入输出层:流式 Markdown 渲染、轻量语法高亮、写入预览与编辑 diff、bash 退出码徽章、思考流、spinner、Ctrl+C 中断、Tab 补全、会话横幅与状态行 —— 核心引擎只依赖 io 接口,换 UI 只需实现同一接口。
|
|
170
|
+
- `help.js` —— 帮助文本的唯一来源:CLI 与 REPL 各用 `variant` 声明自己多出来的那几行 —— 两个入口的差异必须是显式的,不允许各存一份正文。
|
|
171
|
+
- `index.js` —— 库公共 API:导出 `@stable`(minor 内向后兼容)与 `@experimental` 两组能力,契约与示例见 DEVELOPER.md。
|
|
76
172
|
|
|
77
|
-
|
|
78
|
-
|
|
173
|
+
模块计数核对:6 + 8 + 12 + 10 + 3 + 2 + 13 + 13 + 19 = **86** 个条目,其中 §2.7 的
|
|
174
|
+
`web/util.js` / `web/app.js` 一条覆盖 2 个文件,故条目覆盖 **88** 个 `src/**/*.js`,
|
|
175
|
+
与 `find src -name '*.js' | wc -l` 的结果一致。
|
|
79
176
|
|
|
80
|
-
|
|
81
|
-
只读工具(read/ls/glob/grep/skill)默认放行;写/命令按模式处理。`deny` 优先于 `allow`;规则支持「工具名」与「工具名:参数前缀」模式匹配(如 `bash:git *`)。
|
|
177
|
+
## 3. 数据流:一次 `runTurn` 的关键路径
|
|
82
178
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- `credentials.js`:独立凭证模块。Key 存 `~/.mingdao/credentials.json`(权限 600);解析优先级为「环境变量 → 本地凭证库 → config.json 显式字段(兼容旧版本)」;提供 `maskKey` 脱敏(前 6 位…后 4 位)与 `mingdao key status/set/remove/import` 命令族。安装包与仓库零密钥,每个用户安装后配置自己的 Key。
|
|
179
|
+
以 CLI(`cli.js` / `commands/repl.js`)、WebUI(`web/routes/domains/misc.js#/api/chat`)与后台任务(`tasks/worker.js`)三条入口为例,
|
|
180
|
+
它们最终都汇聚到同一个 `agent.js#runTurn`。
|
|
86
181
|
|
|
87
|
-
|
|
88
|
-
`~/.mingdao/sessions/<时间戳>-<随机>.jsonl`,每轮自动追加;`--continue` 载入最近会话,`--resume` 打开选择器(首条消息预览 + 相对时间);`/init` 生成项目 AGENTS.md,`/memory` 维护用户级记忆(`~/.mingdao/AGENTS.md`),两者自动注入系统提示。
|
|
182
|
+
**① 入口与准备**
|
|
89
183
|
|
|
90
|
-
|
|
184
|
+
1. `cli.js` 解析 argv(遇第一个位置参数即停),或 `web/server.js` 收到 `POST /api/chat` 并过掉令牌/Host/CSRF 三道闸;
|
|
185
|
+
或 `schedule.js` 到期拉起 `tasks/worker.js` 子进程。
|
|
186
|
+
2. `config.js#loadConfig` 读配置,`credentials.js` 按优先级解析 Key,`model-caps.js` 依据模型解析窗口/输出上限/是否本地,
|
|
187
|
+
`pricing.js` 给出当前时段的峰谷单价,`cost-guard.js` 检查今日已用(含在途费用)。
|
|
188
|
+
3. `prompts.js` 组装系统提示(角色 + 用户记忆 + 技能清单 + 项目 AGENTS.md + 已挂载 Pack 的提示段);
|
|
189
|
+
`agent.js` 挂载工具(`tools/index.js` 与 `mcp.js` 合并的 MCP 工具 + Pack 贡献的工具),
|
|
190
|
+
并建立本回合账本 `ledger.js#createLedger(newRunId())`,把出网事件注册成账本 sink(`registerEgressSink`)。
|
|
91
191
|
|
|
92
|
-
|
|
93
|
-
2. **推理内容流式展示**:`reasoning_content` 以暗色增量渲染,与正文同流。
|
|
94
|
-
3. **峰谷定价适配**:每轮后展示 prompt/completion tokens,便于用户把批处理放在谷时段(v4 系列 2026-08-17 起峰谷定价,高峰=北京工作日 9:00–12:00、14:00–18:00,闲时价=高峰一半)。官方同时提供 Responses API 与 Anthropic 兼容接口;MingDao 默认走 OpenAI 兼容 chat/completions,如需原生协议可写自定义 Provider 模块。
|
|
95
|
-
4. **模型路由(路线图)**:规划用 v4-pro、执行用 v4-flash 的自动分工;Provider 抽象已支持任意切换。
|
|
96
|
-
5. **可靠性**:超时 + 指数退避重试,长上下文下避免瞬时错误中断任务。
|
|
192
|
+
**② 回合内每一步**
|
|
97
193
|
|
|
98
|
-
|
|
194
|
+
4. `context.js` 用 `tokenizer.js` 精确计数,超预算时 `compact.js` 先把被裁段落压成摘要再注入,否则执行尾部保留裁剪。
|
|
195
|
+
5. `routing.js` 决定本步用 planner 还是 executor(会话粘滞);`providers/index.js` 解析出 Provider 配置,
|
|
196
|
+
`providers/openai-compatible.js` 发流式请求,SSE 增量经 `ui.js`(TUI)或 `web/web-io.js` → SSE(浏览器)渲染。
|
|
197
|
+
6. 若响应带 `tool_calls`:`hooks.js` 先跑 PreToolUse(`block` 即中止)→ `constraints.js` 求值(可强制人工确认或拒绝)
|
|
198
|
+
→ `permissions.js` 判定(`ask` 模式下经 io 询问用户;WebUI 走浏览器模态框)→ `tools/index.js#dispatch` 执行。
|
|
199
|
+
7. 工具内部再受各自围栏约束:文件类过 `tools/fs-tools.js#withinRoot`,出网类过 `ssrf-guard.js` + `net-guard.js`
|
|
200
|
+
(`tools/fetch.js` → `safe-fetch.js`),命令类过 `tools/bash.js` 沙箱档。
|
|
201
|
+
8. 结果回填为 `tool` 角色消息,`hooks.js` 跑 PostToolUse,`ledger.js` 写 `toolCall` / `permission` / `constraint` / `netEgress` 事件;
|
|
202
|
+
`cachestats.js` 累计用量。回到步骤 4 循环(单轮 ≤24 步,跑满可续跑,最多 3 轮)。
|
|
99
203
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
204
|
+
**③ 收束与落盘**
|
|
205
|
+
|
|
206
|
+
9. 循环出口统一调用 `markOutcome()`,由同一份 `outcome` 同时供返回值与 `ledger.js#runEnd` 使用(口径分裂属缺陷);
|
|
207
|
+
账本写 `usage` 事件(含 `usageUnknown` 标记)与 `runEnd`,并写封条侧车文件。
|
|
208
|
+
10. `session.js` 追加本轮消息到 JSONL,`session-index.js` 增量更新检索分片,`pricing.js` +
|
|
209
|
+
`cachestats.js` 计入费用,`cost-guard.js` 复核日限。
|
|
210
|
+
11. `titles.js` 首轮后生成标题并重命名会话文件;`memory.js` 按需提取用户偏好与写 journal;
|
|
211
|
+
`sync.js#maybeAutoSync` 静默推送;`tasks.js#patchTask` 更新任务状态;`notify.js` 弹完成通知。
|
|
212
|
+
12. `audit.js` 全程独立记录每次工具调用(含被拒与被钩子阻止),与账本互为补充:
|
|
213
|
+
audit 记「工具调用了什么」,账本记「这一步是怎么被决定的」。
|
|
214
|
+
|
|
215
|
+
## 4. 已知边界
|
|
216
|
+
|
|
217
|
+
以下边界是实现的真实状态,写在代码里也写在这里;把它们读成更强的承诺即为误用。
|
|
218
|
+
|
|
219
|
+
**出网与网络**
|
|
220
|
+
|
|
221
|
+
- `net-guard.js` 只覆盖内核自己发起的请求。用户在 **bash 里自己敲 curl 不走这里**——那是子进程的网络栈;
|
|
222
|
+
MCP 服务器同样以子进程运行,其网络行为不受闸门约束。它证明的是「内核没有偷偷外传」,不是「这台机器绝对没有外传」。
|
|
223
|
+
- `sync.js` 在自签名证书场景走 `node:https`(`rawRequest`)而非全局 `fetch`,该路径由调用方显式调 `decideEgress()` 记账;
|
|
224
|
+
漏调即漏记。
|
|
225
|
+
- `safe-fetch.js` 走 `node:http(s)`,不经 `https_proxy` 等环境变量代理;需要代理的部署环境需自行确认可达性。
|
|
226
|
+
- `ssrf-guard.js` 的判定是 fail-closed 的保守口径,可能拒绝个别合法的内网域名解析场景。
|
|
227
|
+
|
|
228
|
+
**平台与进程**
|
|
229
|
+
|
|
230
|
+
- PID 归属校验是 best-effort:Linux 读 `/proc/<pid>/cmdline`、macOS 用 `ps`、**Windows 两者都读不到**,
|
|
231
|
+
此时返回 `null`(无从判断),调用方按 best-effort 处理——不存在「Windows 上也能确认 PID 归属」的保证。
|
|
232
|
+
- bash 沙箱三档仅在 **Linux + bubblewrap** 下成立;其他平台与未装 bwrap 时自动降级为 `off`,只在工具结果里注明。
|
|
233
|
+
- `atomic-write.js` 的同步锁在等待期间阻塞整个事件循环,仅限纯同步调用链使用;
|
|
234
|
+
极端争用下仍有 15 处同步锁会阻塞其所在进程(多为守护进程内部或一次性 CLI 命令),未宣称「阻塞面已彻底消除」。
|
|
235
|
+
|
|
236
|
+
**权限与隔离**
|
|
237
|
+
|
|
238
|
+
- Pack 的 `permissions`(`fs` / `net` / `env`)只是**声明,内核不据此强制**:`pack.mjs` 在宿主进程内运行,
|
|
239
|
+
可读写任意文件、可出网。装载第三方 Pack 等同于信任其作者;`pack verify` 默认静态校验正是为此。
|
|
240
|
+
- `constraints.js` 只能收紧不能放松,但它拦的是**已声明的**红线;没写进 manifest 的行为不会被拦。
|
|
241
|
+
- `mcp-presets.js` 钉死版本只保证「装的是哪一个确切版本」,不保证该版本自身安全。
|
|
242
|
+
- `skill-registry.js` 的 sha256 校验保护的是传输链路与本地文件;`skills.js` 的项目级技能来源不可验证(仅能发现安装后被改动)。
|
|
243
|
+
|
|
244
|
+
**WebUI 与多用户**
|
|
245
|
+
|
|
246
|
+
- **共享令牌 = 同一用户**:同一 token 下 `/api/abort`、`/api/tasks` 没有每客户端作用域,可中断他人任务、读取他人任务消息;
|
|
247
|
+
多人使用需一人一实例(已在 README 安全一节写明)。
|
|
248
|
+
- 回环绑定且未配置令牌时不启用认证(本机信任模型);跨站请求按 `Sec-Fetch-Site` 拒绝,但这依赖浏览器如实上报。
|
|
249
|
+
- 桌面版遥测只在 `app.isPackaged` 且未关闭时发一次 POST;CLI 与 WebUI 不发遥测。
|
|
250
|
+
|
|
251
|
+
**账本与合规**
|
|
252
|
+
|
|
253
|
+
- `ledger.js` 的哈希链只能证明「自写入后未被改动」,**不含可信时间戳**,不等同于审计级不可否认;
|
|
254
|
+
导出物字段经过截断,不是原始数据的完整副本,不能当作证据原件。
|
|
255
|
+
- `replay.js` 只重判决策,不重跑工具、不重放模型输出;用的是已脱敏参数,依赖被掩码内容的规则可能不命中。
|
|
256
|
+
- 账本/审计写失败时按「降级必须可见」提示用户,但**不保证**磁盘写满等情况下记录仍然完整。
|
|
257
|
+
|
|
258
|
+
**Token 与计价**
|
|
259
|
+
|
|
260
|
+
- `tokenizer.js` 的官方词表只覆盖 DeepSeek 系模型;其他模型回退启发式估算(英文≈4 字符/token、CJK≈0.75 token/字),
|
|
261
|
+
预算裁剪因此是近似值而非精确值。
|
|
262
|
+
- 未收录模型无内置价格表,费用显示 n/a,护栏对这类模型只能提示「用量未知」。
|
|
263
|
+
|
|
264
|
+
## 5. 扩展指南速览
|
|
265
|
+
|
|
266
|
+
- **加一个模型网关**:`mingdao init` → custom → 填 `baseUrl`(OpenAI 兼容即可)。
|
|
267
|
+
- **加一个非兼容协议**:写 `<mingdao-home>/providers/<name>.mjs`,导出 `createProvider(cfg)` 返回 `{ chat(opts) }`,
|
|
268
|
+
见 [PROVIDERS.md](PROVIDERS.md)。
|
|
269
|
+
- **加一个工具**:在 `tools/index.js` 的 Schema 表与 `dispatch` 分支成对增加(返回 `{ok, output|error}`),
|
|
270
|
+
或用 `registerTool` / `config.tools` 从外部挂载。
|
|
271
|
+
- **加一个垂域 Pack**:`mingdao pack new`,契约见 [PACK-API.md](PACK-API.md)(v1 已冻结)。
|
|
272
|
+
- **换 UI**:实现 `io` 接口(print / writeText / writeReasoning / ask / confirm / choose),`createAgent` 不感知终端。
|
|
104
273
|
- **库方式复用**:`import { createAgent, createProvider, dispatch } from 'mingdao-harness'`。
|
|
105
274
|
|
|
106
|
-
##
|
|
275
|
+
## 6. 尚未实现(路线图)
|
|
276
|
+
|
|
277
|
+
以下条目取自审计登记中明确「未闭合/未纳入本批」的清单,**不是**已发布能力。
|
|
107
278
|
|
|
108
|
-
|
|
279
|
+
- **Pack 子进程隔离**(P2):`pack.mjs` 目前与宿主同进程,`permissions` 声明无强制力。
|
|
280
|
+
- **`ledger --sign-key`**:账本可选签名仍未实现,封条只提升到「防误删/漏写」。
|
|
281
|
+
- **出网闸门对子进程的补强**(P1-4,含 bash `curl` 与 MCP 子进程)。
|
|
282
|
+
- **在线缓存命中基准**(P0-2):需要真实 Key 与额度,目前「删 token 不会额外劣化」是推理而非实测。
|
|
283
|
+
- **`agent.js` 拆分**(P1-1,`runTurn` 已达千行量级)、**结构化摘要 + 关键事实钉死**(P1-2)、
|
|
284
|
+
**预算状态注入上下文**(P1-3)。
|
|
285
|
+
- **sync-server 设备级吊销、push 配额与限流**(M-8)与 **memory 子系统的锁外读-改-写**(M-9)。
|
|
286
|
+
- **自更新多镜像取最大版本、`install.sh` 校验和**(H-6/L-18):供应链完整性锚需要发布流程配合,
|
|
287
|
+
与 `ledger --sign-key`、在线缓存基准同列为 v1.0 前项。
|
|
288
|
+
- **Electron 更新包签名校验**与 **IDE 令牌安全存储**:随桌面版/IDE 侧发布流程推进。
|
|
289
|
+
- **`require-citation` 约束 kind** 与 `mingdao constraint test <pack>`:契约中已标注「请勿依赖」,
|
|
290
|
+
需先与下游定规格。
|
package/docs/CONFIG.md
CHANGED
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
每个工具调用(含被拒/被钩子阻止/参数解析失败)自动落 `~/.mingdao/audit.jsonl`(600 权限):
|
|
35
35
|
时间、会话、模型、工具名、参数(`sk-` 系 Key 自动脱敏)、执行结果/退出码/超时/耗时/输出大小、
|
|
36
36
|
拒绝原因。查看:`mingdao audit [数量]`(默认 20 条)或会话内 `/audit`;`"audit": false` 关闭。
|
|
37
|
-
超过
|
|
37
|
+
超过 **4 MB**(约 2 万行,按平均 200 字节/条估算)触发轮转,保留最近 **10000 行**。
|
|
38
|
+
判据是**字节数**而不是行数(`src/audit.js`:进程内行计数对 CLI 会话永远不触发);工具参数很长时
|
|
39
|
+
实际保留的行数会明显少于 2 万行。
|
|
38
40
|
|
|
39
41
|
## 技能完整性(P3-3)
|
|
40
42
|
|
|
@@ -234,7 +236,10 @@ mingdao net report --since 7d --json # 支持 7d / 24h / 90m
|
|
|
234
236
|
环境变量 `MINGDAO_WEB_TOKEN`、`web.token`、`mingdao web --auth-token <令牌>`(或 `--auth-token=-` 从 stdin 读);
|
|
235
237
|
**命令行字面量会进入 argv 与 shell 历史(`ps` 可见)**,故用它时会给出告警;
|
|
236
238
|
- 令牌同时接受 URL `?token=`、请求头 `X-MingDao-Token` 或 `Authorization: Bearer`;
|
|
237
|
-
- 服务端校验 `Host` 头必须等于回环名或绑定地址(防 DNS rebinding),代理场景会 403
|
|
239
|
+
- 服务端校验 `Host` 头必须等于回环名或绑定地址(防 DNS rebinding),代理场景会 403 属预期。
|
|
240
|
+
**例外(v0.6.8 补记)**:当 `host: 0.0.0.0` 且已启用令牌时,服务端**不再校验 Host**——
|
|
241
|
+
此时访问边界是令牌(攻击者页面拿不到令牌)。这是为局域网/手机访问设计的模式,
|
|
242
|
+
所以**必须**配令牌;未显式配置时服务端会自动生成随机令牌(见上文 `web.token`);
|
|
238
243
|
- **跨站浏览器请求一律 403**(按 `Sec-Fetch-Site` 判定,含 `<img>`/`no-cors` 盲打):
|
|
239
244
|
本机端口不受同源策略保护,这是"任意网页盲打本机 API"的唯一有效拦截点;
|
|
240
245
|
- 云元数据端点(`169.254.169.254` 等)在任何绑定与任何开关下**都拒绝**。
|
|
@@ -316,7 +321,9 @@ completion 计费,防止推理吃满上限时空轮白烧)、`compactTrigger
|
|
|
316
321
|
`MINGDAO_SYNC_REGISTRATION=open|invite|closed`(默认 open)+ `MINGDAO_SYNC_INVITE_CODES=码1,码2`
|
|
317
322
|
(invite 模式生效),公网自建建议至少 `invite`。
|
|
318
323
|
|
|
319
|
-
##
|
|
324
|
+
## 会话日志与「带上文」(跨会话连续性)
|
|
325
|
+
|
|
326
|
+
每个会话结束时写入 `~/.mingdao/journal.jsonl`(首条用户消息 + 结果摘要)。**默认不注入**新会话的
|
|
320
327
|
系统提示——新会话应当全新开始,避免「新会话却接着上一次会话的工作」的上下文混淆;需要延续上次
|
|
321
328
|
工作时显式开启:
|
|
322
329
|
|
package/docs/DEVELOPER.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# MingDao Harness
|
|
1
|
+
# MingDao Harness 开发者指南
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 契约化起于 v0.4.0;本指南不绑定具体版本,接口稳定性以 `src/index.js` 的 `@stable`/`@experimental` 标注为准。
|
|
4
|
+
|
|
5
|
+
> 战略依据:[STRATEGY-NEXT.md](internal/STRATEGY-NEXT.md)(垂直产品 × 开放内核)。
|
|
4
6
|
> 本指南面向**用 MingDao 做二次开发 / 定制自己智能体**的开发者。
|
|
5
7
|
> 稳定契约:`@stable` 导出在 minor 版本内保持向后兼容;`@experimental` 可能调整。
|
|
6
8
|
|
|
@@ -127,7 +129,7 @@ export function createPack(ctx) {
|
|
|
127
129
|
3. **只收紧、不放松**:约束不授予任何权限,也不改变 `permissions.js` 的判定。
|
|
128
130
|
|
|
129
131
|
完整契约(manifest 字段、约束 kind、`ctx.llm` 语义、版本兼容窗口)见 [PACK-API.md](PACK-API.md);
|
|
130
|
-
Pack API 变更史见 [CHANGELOG-PACK.md](CHANGELOG-PACK.md)。
|
|
132
|
+
Pack API 变更史见 [CHANGELOG-PACK.md](internal/CHANGELOG-PACK.md)。
|
|
131
133
|
|
|
132
134
|
## 三、库嵌入:最小示例
|
|
133
135
|
|
|
@@ -153,17 +155,21 @@ console.log(res.text, res.usage, res.perf);
|
|
|
153
155
|
|
|
154
156
|
## 四、公共 API 速查(@stable 面)
|
|
155
157
|
|
|
156
|
-
| 分组 |
|
|
158
|
+
| 分组 | 导出(共 70 个 `@stable`) |
|
|
157
159
|
| --- | --- |
|
|
158
|
-
| Agent 内核 | `createAgent` · `createPermission` · `createIO` |
|
|
159
|
-
| Provider
|
|
160
|
-
|
|
|
161
|
-
| Agent Preset | `listPresets` · `loadPreset` · `validatePreset` · `presetConfigOverrides` · `presetSystemBlock` |
|
|
162
|
-
|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
|
|
166
|
-
|
|
160
|
+
| Agent 内核 | `createAgent` · `createPermission` · `createIO` · `style` · `C` |
|
|
161
|
+
| Provider 与模型 | `createProvider` · `resolveProviderConfig` · `MODELS` · `PROVIDERS` · `modelPreset` · `providerPreset` · `resolveModelCaps` · `safeBudget` · `isLocalBaseUrl` |
|
|
162
|
+
| 工具(含 v0.4.0 第三方注册) | `toolSchemas` · `dispatch` · `registerTool` · `listRegisteredTools` · `mountConfigTools` · `buildToolSchemas` |
|
|
163
|
+
| Agent Preset(v0.4.0) | `listPresets` · `loadPreset` · `validatePreset` · `presetConfigOverrides` · `presetSystemBlock` · `presetDirs` |
|
|
164
|
+
| 垂域 Pack(v0.5.0 契约,Pack API v1) | `listPacks` · `loadPack` · `validateManifest` · `mountPacks` · `satisfiesRange` · `packDirs` · `coreVersionOf` · `loadedPackNames` · `SUPPORTED_PACK_API` · `CONSTRAINT_KINDS` |
|
|
165
|
+
| 约束引擎(v0.5.0 契约,Pack API v1 的领域红线) | `compileConstraints` · `checkPreTool` · `checkPostTool` · `checkOutput` · `blockedOutputText` · `toolMatches` |
|
|
166
|
+
| 上下文与压缩 | `trimMessages` · `approxTokens` · `clampText` · `TOOL_RESULT_LIMIT` · `compactConversation` · `summarizeConversation` |
|
|
167
|
+
| 配置与凭证 | `mingdaoHome` · `ensureHome` · `loadConfig` · `saveConfig` · `runWizard` · `effectiveApiKey` · `credentialsPath` · `loadCredentials` · `saveCredentials` · `getStoredKey` · `setStoredKey` · `removeStoredKey` · `maskKey` · `resolveApiKey` |
|
|
168
|
+
| 计价与计量 | `estimateCost` · `estimateCostLabel` · `isPeakHour` · `PRICE_DATA_AS_OF` · `countTokens` · `heuristicTokens` · `makeTokenCounter` · `isTokenizable` |
|
|
169
|
+
|
|
170
|
+
`@experimental`(接口可能调整,共 19 个):`updateCheck` · `mingdaoUpdate` · `mingdaoRollback` · `findRepoRoot` · `writeAudit` · `listAudit` · `redactSecrets` · `auditFile` · `trustSkill` · `skillDirHash` · `readSourceMeta` · `tamperedSkillNames` · `McpClient` · `startMcpServers` · `createSession` · `latestSession` · `listSessions` · `appendMessages` · `loadSession`。
|
|
171
|
+
|
|
172
|
+
> 本表由 `node scripts/gen-api-table.mjs` 从 `src/index.js` 生成;`scripts/doc-lint.mjs` 会校验它与代码一致(漂移即失败)。
|
|
167
173
|
|
|
168
174
|
## 五、约定
|
|
169
175
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
> 面向:下游 Line B(中医垂域层,Linux 原机开发)
|
|
11
|
-
> 上游契约:`PACK-API.md`(v1 已冻结)· 变更史 `CHANGELOG-PACK.md`
|
|
11
|
+
> 上游契约:`PACK-API.md`(v1 已冻结)· 变更史 `internal/CHANGELOG-PACK.md`
|
|
12
12
|
> 触发:**v0.5.0 发布即回迁**(决策已确认)。目标:把 3 个域工具从 `providers/dify.mjs` 的 `chat()` 里搬出来。
|
|
13
13
|
> 纪律:下游**只通过扩展点接入,绝不修改上游源码**;内核 bug 在上游修。
|
|
14
14
|
|
|
@@ -251,7 +251,7 @@ mingdao key set deepseek <你的 DeepSeek Key> # 写入 <MINGDAO_HOME>/crede
|
|
|
251
251
|
| --- | --- |
|
|
252
252
|
| 声明 | `pack.json` 写 `apiVersion: 1` + `engines.mingdao: ">=0.5 <0.7"` |
|
|
253
253
|
| 支持窗口 | 上游承诺支持最近 2 个 minor(0.5 / 0.6 支持 v1) |
|
|
254
|
-
| 上游变更 | 任何 Pack API 变更同步更新 `PACK-API.md` + `CHANGELOG-PACK.md` + 兼容性矩阵 |
|
|
254
|
+
| 上游变更 | 任何 Pack API 变更同步更新 `PACK-API.md` + `internal/CHANGELOG-PACK.md` + 兼容性矩阵 |
|
|
255
255
|
| 下游义务 | 每个上游 minor 发布后跑一次 `pack verify`,并入 CI |
|
|
256
256
|
| 破坏性变更 | 走 major + 迁移指南(如可行再配 codemod) |
|
|
257
257
|
|
package/docs/PACK-API.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# 垂域 Pack API v1(草案)
|
|
2
2
|
|
|
3
3
|
> 状态:**v1 已冻结(2026-09-11,决策已确认)**,随 v0.5.0 发布生效。本文是上游与下游之间的接口契约。
|
|
4
|
-
> 变更纪律:v1 冻结后,minor 版本只增不改;任何改动必须同步更新本文 + `CHANGELOG-PACK.md` + 兼容性矩阵。
|
|
4
|
+
> 变更纪律:v1 冻结后,minor 版本只增不改;任何改动必须同步更新本文 + `internal/CHANGELOG-PACK.md` + 兼容性矩阵。
|
|
5
5
|
> 定位:让垂域团队(中医、法律、教育、制造、政务…)**不修改内核源码**就能做出可私有化、可审计、受约束的智能体。
|
|
6
|
-
> 关联:`STRATEGY-0.5.md`(战略)、`DEVELOPER.md`(现有扩展点)、`CONFIG.md`(配置)。
|
|
6
|
+
> 关联:`internal/STRATEGY-0.5.md`(战略)、`DEVELOPER.md`(现有扩展点)、`CONFIG.md`(配置)。
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -221,7 +221,7 @@ export function createPack(ctx) {
|
|
|
221
221
|
| --- | --- | --- |
|
|
222
222
|
| PreToolUse | `tool-deny`、`tool-arg-require`、`arg-forbid`、`confirm` | 前三个命中即阻止执行,回填工具错误给模型,写审计;`confirm` 见下方说明 |
|
|
223
223
|
| PostToolUse | `completeness`、`result-forbid` | `completeness` 缺项 → 拒绝该工具结果,要求模型补采;`result-forbid` → 屏蔽结果并提示 |
|
|
224
|
-
| 输出前 | `output-forbid`、`require-citation` | 命中 → 按 `action` 处理:`block`(改为固定合规文案)/ `block-and-rewrite`(再请求一次修正)/ `warn`(放行并标注) |
|
|
224
|
+
| 输出前 | `output-forbid`、`require-citation`(❌ **未实现,见 §4.1,请勿依赖**:`KINDS` 里没有它,写进 Pack 会被判 kind 非法、整个 Pack 装载失败) | 命中 → 按 `action` 处理:`block`(改为固定合规文案)/ `block-and-rewrite`(再请求一次修正)/ `warn`(放行并标注) |
|
|
225
225
|
|
|
226
226
|
**`confirm`(v0.6.4 起真正求值)**:即使在 `auto` 权限档位下,命中的工具调用也**必须**先得到人工确认才执行
|
|
227
227
|
——它的存在意义正是「权限已经放行,领域还要再问一次」。两种情形一律按 fail-closed 阻止:
|
|
@@ -345,7 +345,7 @@ manifest 可声明 `budget.dailyYuan` + `budget.action`。超限时 `ctx.llm()`
|
|
|
345
345
|
> `pack verify` 当 CI 门禁,等于**在 CI 上执行被审仓库的任意 Node 代码**(且不经 `pack trust` 信任门)。
|
|
346
346
|
> 现在默认确实是静态的;约束与提示词段的合法性由代码产出,静态阶段无法校验,需要那种覆盖请显式加 `--runtime`。
|
|
347
347
|
|
|
348
|
-
**上游义务**:任何 Pack API 变更必须同步更新本文 + 兼容性矩阵 + `docs/CHANGELOG-PACK.md`。
|
|
348
|
+
**上游义务**:任何 Pack API 变更必须同步更新本文 + 兼容性矩阵 + `docs/internal/CHANGELOG-PACK.md`。
|
|
349
349
|
|
|
350
350
|
---
|
|
351
351
|
|
package/docs/PROVIDERS.md
CHANGED
|
@@ -72,12 +72,23 @@ export async function createProvider(cfg) {
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
然后配置(**key 不要写进 `config.json`** —— 本仓三处文档都承诺"配置文件不含任何密钥、可安全分享/提交"):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 1) 把 Key 存进本机凭证库(<home>/credentials.json,权限 600)
|
|
79
|
+
mingdao key set anthropic # 交互式输入,或 echo "<key>" | mingdao key set anthropic
|
|
80
|
+
# 2) 或者用环境变量(CI/容器推荐)
|
|
81
|
+
export ANTHROPIC_API_KEY=<key>
|
|
82
|
+
```
|
|
76
83
|
|
|
77
84
|
```json
|
|
78
|
-
{ "provider": "anthropic", "model": "claude-sonnet-4-5"
|
|
85
|
+
{ "provider": "anthropic", "model": "claude-sonnet-4-5" }
|
|
79
86
|
```
|
|
80
87
|
|
|
88
|
+
> 说明(F-H2,v0.6.8 文档审计):本文件此前给出的示例里带 `"apiKey"`,与
|
|
89
|
+
> `PROVIDERS.md:7` / `CONFIG.md:3` / `README.md` 的承诺直接冲突。`config.json` 里的 `apiKey`
|
|
90
|
+
> 仅作为**旧配置兼容**读取,**不要**再这样写——配置可能被分享、提交或进入团队仓库。
|
|
91
|
+
|
|
81
92
|
规则:`provider` 名不在内置预设中且存在同名模块文件时,优先加载该模块;否则按 OpenAI 兼容方式使用 `baseUrl`。
|
|
82
93
|
|
|
83
94
|
### 声明能力(可选,v0.6.3 起)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 省钱基准(SAVINGS-BENCHMARK)
|
|
2
2
|
|
|
3
|
-
> 对应规划:`docs/PLAN-v0.3.0.md` P0-1
|
|
3
|
+
> 对应规划:`docs/internal/PLAN-v0.3.0.md` P0-1
|
|
4
4
|
> 运行:`node test/bench/bench-savings.mjs`(已并入 `npm run bench`)
|
|
5
5
|
> 首期基线:v0.2.8 代码,2026-09-03 记录
|
|
6
6
|
> 一句话承诺:**「同一件事,MingDao 比裸调 DeepSeek API 省 X%,且任务做得到底」——本文件就是把 X 变成可复现、可回归的数字。**
|