MingDao Harness API 文档

面向二次开发者:把 MingDao 内核以库形式嵌入自己的 Node 程序(v0.4.0 起公共 API 契约化冻结)。

稳定契约:所有 @stable 导出在 minor 版本内保持向后兼容;@experimental 接口可能调整。零运行时依赖——公共 API 只使用 Node ≥ 18.17 内置能力,无需任何 npm 依赖。

快速开始

npm i mingdao-harness
import { createProvider, createAgent, createPermission, createIO } from 'mingdao-harness';

// 1) 建 Provider:cfg 结构与 ~/.mingdao/config.json 相同
const provider = await createProvider({
  provider: 'deepseek',
  model: 'deepseek-v4-flash',
  baseUrl: 'https://api.deepseek.com/v1',
  apiKey: process.env.DEEPSEEK_API_KEY,   // 或走环境变量 DEEPSEEK_API_KEY 自动解析
}, 'deepseek-v4-flash');

// 2) 建 Agent 循环
const io = createIO();                       // 也可自实现 print/ask 接口
const agent = createAgent({
  provider,
  permission: createPermission('ask', io),
  io,
  modelName: 'deepseek-v4-flash',
  workingDir: process.cwd(),
  cfg: {},                                   // 可选:温度/预算/护栏等
});

// 3) 跑一轮
const res = await agent.runTurn([
  { role: 'system', content: '你是代码助手。' },
  { role: 'user', content: '帮我看看 package.json 的依赖' },
]);
console.log(res.text);       // 最终文本
console.log(res.usage);      // token 用量(含 cache hit/miss)
console.log(res.perf);       // 耗时/步骤/子代理等

以上即最小可用闭环。Agent 循环内置了权限门控、工具审计、省钱(Schema 瘦身/缓存前缀稳定)、成本护栏、自动压缩、子代理派发——你只负责接入 Provider 与 IO。

三种接入方式

方式适合命令 / 入口
产品终端开箱即用的 DeepSeek 省钱 Coding Agentnpm i -g mingdao-harness && mingdao
Agent Preset不改代码,声明式定制智能体mingdao --preset <名> / JSON 文件
库嵌入把 Agent 嵌进自己的 Node 程序import { createAgent } from 'mingdao-harness'

Agent 内核 @stable

createAgent

createAgent(params) → agent
createAgent({
  provider,       // 必填:createProvider 返回值(或其 chat() 兼容对象)
  permission,     // 必填:createPermission 返回值
  io,             // 必填:createIO 返回值(或 { print, ask } 兼容对象)
  modelName,      // 必填:模型名(用于 model-caps / tokenizer / 定价)
  workingDir,     // 必填:工作目录(工具默认作用域)
  cfg = {},       // 可选:与 config.json 同结构(temperature/maxOutputTokens/contextBudget/costGuard/...)
  undoStore,      // 可选:undo 备份存储(跨轮/跨模型复用)
  maxSteps,       // 可选:单轮最大步数(默认 24)
  mcp,            // 可选:MCP 客户端(提供 toolSchemas()/isReadonly())
  onCompact,      // 可选:压缩回调
  sessionRef,     // 可选:会话引用(共享 /model 切换、undo)
})

返回的 agent 核心方法:

createPermission

createPermission(rawMode, io) → { mode, check }
// rawMode: 'ask' | 'auto' | 'readonly' 或 { mode, allow: [], deny: [] }
const perm = createPermission('ask', io);   // 默认逐次询问
const perm2 = createPermission({ mode: 'auto', allow: ['bash:git *'], deny: ['write'] }, io);
await perm.check('bash', { command: 'git status' }, '标签');  // → boolean

权限模式:ask(逐次确认,默认)· auto(自动放行)· readonly(只读工具放行、写操作询问)。allow/deny 支持「工具名:参数前缀」规则;deny 优先于 allow

createIO

createIO({ quiet = false }) → io
const io = createIO({ quiet: true });   // quiet: 静默 worker(无终端输出)
io.print('...');        // 打印
const ans = await io.ask('是否继续?[y/N]');  // 交互确认(权限询问用)

自定义 IO:只需提供 print(text)ask(question) → Promise<string> 两个方法即可对接自己的 UI。

style / C

style(text, code) → string · C 颜色常量
import { style, C } from 'mingdao-harness';
io.print(style('警告', C.yellow));   // 终端着色

Provider 与模型 @stable

createProvider

createProvider(cfg, modelName, opts?) → Promise<provider>
const provider = await createProvider(cfg, 'deepseek-v4-flash', { timeoutMs: 600000, retries: 2 });
// provider = { name, config, chat(opts) }
const stream = await provider.chat({ messages, tools, temperature, maxOutputTokens, stream: true });

返回对象提供 chat(opts):OpenAI 兼容 SSE 流式接口,内置重试(429/5xx/网络错误)与分层超时(本地/远程自适应)。cfg 支持自定义端点(custom:<模型名> 走 OpenAI 兼容直连)与自定义 Provider 模块(~/.mingdao/providers/<name>.mjs)。

resolveProviderConfig

resolveProviderConfig(cfg, modelName) → { name, baseUrl, apiKey, ... }

从 cfg 解析出某模型实际使用的服务商配置(含服务商选择、baseUrl、Key 解析优先级:环境变量 > 凭证库 > 配置字段)。

modelPreset / providerPreset

modelPreset(name) → object | undefined · providerPreset(name) → object | undefined

内置模型/服务商预设(上下文窗口、默认输出、温度、计价等)。MODELS / PROVIDERS 为完整内置表。

resolveModelCaps / safeBudget / isLocalBaseUrl

resolveModelCaps(cfg, modelName) → { contextWindow, maxOutputTokens, isLocal, budgetTokens, preset }
safeBudget(cfg, caps) → number
isLocalBaseUrl(baseUrl) → boolean

模型能力推导:contextWindow 取自定义声明 > 内置预设 > 本地兜底 32k / 远程兜底 128k;safeBudget 把期望预算收紧到 min(期望, 窗口×75%, 窗口−输出−余量),保证 prompt 永不逼近窗口边缘。

工具与第三方注册 @stable

registerTool(v0.4.0)

registerTool(tool) → schema
import { registerTool, createAgent } from 'mingdao-harness';

registerTool({
  name: 'weather',
  description: '查询城市天气',
  parameters: {
    type: 'object',
    properties: { city: { type: 'string', description: '城市名' } },
    required: ['city'],
  },
  run: async (args, ctx) => ({ ok: true, output: `${args.city}:晴 24°C` }),
  readOnly: true,   // 可选:只读工具(进 READONLY_TOOLS,只读档自动放行)
});
// 之后 createAgent 的模型即可调用 weather;执行走统一权限/审计/省钱链路。

约束:名字 [A-Za-z0-9][A-Za-z0-9_-]{0,63}、不得以 mcp__ 开头、不得与内置工具或已注册工具同名;run 抛异常会转成结构化错误回填(不中断会话)。

listRegisteredTools / isRegisteredToolReadonly

listRegisteredTools() → string[] · isRegisteredToolReadonly(name) → boolean

mountConfigTools(声明式 config.tools)

mountConfigTools(cfg) → string[](本次新挂载名)
// config.json: 不改代码,shell 命令包装成工具
{ "tools": [ { "name": "date-now", "description": "当前时间", "command": "date" } ] }

command/bin/bash -lc 执行;参数以 MINGDAO_TOOL_ARGS(JSON)环境变量传入,不做字符串拼接(防注入);执行受权限引擎门控。非法条目跳过不崩(用 console.error 提示)。

buildToolSchemas / dispatch / toolSchemas

buildToolSchemas(usedNames, extra?) → schemas[]
dispatch(name, args, ctx) → Promise<result>
toolSchemas() → schemas[] · READONLY_TOOLS Set<string>

buildToolSchemas 按「已用工具剥描述」生成省钱 Schema(内置 + MCP + 第三方合并);dispatch 是工具调用统一分发入口(内置 + 第三方 + MCP)。

Agent Preset @stable

声明式智能体预设:一个 JSON = 系统提示定制段 + 工具白名单 + 权限 + 模型 + 参数。三级遮蔽(后者覆盖前者):项目 .mingdao/presets/ → 用户 ~/.mingdao/presets/ → 内置。

{
  "name": "code-reviewer",
  "label": "代码审查员",
  "description": "只读审查并输出分级报告",
  "systemPrompt": "你是代码审查员。只读审查,按严重度分级输出,每条带文件:行号证据。",
  "tools": ["read", "ls", "glob", "grep", "skill", "git", "fetch", "todo"],
  "permission": "auto",
  "model": "deepseek-v4-flash",
  "temperature": 0.3,
  "maxOutputTokens": 4096,
  "maxRounds": 4,
  "contextBudget": 96000
}

字段全部可选(缺省保持当前配置);未知字段校验报错(防拼写静默失效);tools 白名单外工具对模型不可见、调用被硬拦;model 是建议(CLI 未显式 -m 时采纳)。

listPresets / loadPreset

listPresets(workingDir) → Array<{ name, label, description, source, file, ... }>
loadPreset(workingDir, name) → object | null

validatePreset

validatePreset(obj) → { ok: true } | { ok: false, errors: [] }

presetConfigOverrides / presetPermissionOverride / presetSystemBlock

presetConfigOverrides(preset) → { permission?, model?, temperature?, maxOutputTokens?, maxRounds?, contextBudget?, presetTools? }
presetPermissionOverride(preset, currentPermission) → { permission, escalated }
presetSystemBlock(preset) → string · presetDirs(workingDir) → string[]
安全presetPermissionOverride 会拦截预设 permission 提权(当前 ask → 预设 auto 属提权,忽略并返回 escalated:true)。克隆恶意仓库含 auto 预设时,不会静默跳过用户确认。

上下文与压缩 @stable

trimMessages / approxTokens / clampText

trimMessages(messages, budget, count?) → messages
approxTokens(text) → number · clampText(text, maxChars?) → string

trimMessages 按预算裁剪消息(保留系统提示与最新对话);clampText 工具结果截断(默认上限 TOOL_RESULT_LIMIT = 20000 字符)。

compactConversation / summarizeConversation

compactConversation({ messages, budget, count, provider, executorModel, triggerRatio, force }) → Promise<...>
summarizeConversation(provider, model, convoText) → Promise<string>

长会话超出预算时把被裁段落压成 ≤500 字摘要注入,替代「失忆」;triggerRatio 触发线(默认 0.8,滞回压到约 60%)。

配置与凭证 @stable

配置

mingdaoHome() → path · ensureHome() · loadConfig() → object · saveConfig(cfg)
runWizard(io) → Promise<...> · effectiveApiKey(cfg, providerName) → string | undefined

凭证

credentialsPath() · loadCredentials() → object · saveCredentials(creds)
getStoredKey(providerName) · setStoredKey(providerName, key) · removeStoredKey(providerName)
maskKey(key) → string · resolveApiKey(cfg, providerName, envKeyHint?) → string | undefined

密钥存独立凭证库(~/.mingdao/credentials.json,600 权限),绝不写入 config.json;解析优先级:环境变量 > 凭证库 > 配置字段。配置文件的完整字段见 docs/CONFIG.md

计价与计量 @stable

estimateCost / estimateCostLabel

estimateCost(modelName, promptTokens, completionTokens, cache?, date?) → number(元)
estimateCostLabel(modelName, promptTokens, completionTokens, usage?) → string
import { estimateCost } from 'mingdao-harness';
const yuan = estimateCost('deepseek-v4-flash', 5000, 800, { hit: 4500, miss: 500 });
// cache: { hit, miss } 命中按 cacheHit 价(1/30)、余量按未命中计,不漏计

isPeakHour

isPeakHour(date?) → boolean · PRICE_DATA_AS_OF = '2026-08'

北京时间峰谷判定(工作日 9:00–12:00、14:00–18:00 为高峰,高峰输入价 2 倍)。

countTokens / makeTokenCounter / heuristicTokens / isTokenizable

countTokens(text, modelName) → number · makeTokenCounter(modelName) → (text) => number
heuristicTokens(text) → number · isTokenizable(modelName) → boolean

DeepSeek 官方词表精确 BPE 计数(与 HF tokenizers 逐值一致);非 DeepSeek 模型回退启发式估算。

实验性 API @experimental

分组导出说明
更新updateCheck · mingdaoUpdate · mingdaoRollback · findRepoRoot自更新/回滚
审计writeAudit · listAudit · redactSecrets · auditFile工具调用审计 + 密钥脱敏
技能trustSkill · skillDirHash · readSourceMeta · tamperedSkillNames技能库完整性
MCPMcpClient · startMcpServersModel Context Protocol 客户端
会话createSession · latestSession · listSessions · appendMessages · loadSession会话存储/检索

稳定契约与约定

完整源码与文档:公共 API 定义在 src/index.js;开发者指南见 docs/DEVELOPER.md;配置详解见 docs/CONFIG.md(GitHub/Gitee 仓库均有)。