# pilot-harness

> DSH 共享底座 profile bundle，通过 cordis.patch.yml 一次性插入 100+ 个基础插件行，作为所有 profile 的第一层。

## Metadata

- Author: [@op7418](https://github.com/op7418)
- Repo: <https://github.com/op7418/pilot-harness.git>
- GitHub: [op7418/pilot-harness](https://github.com/op7418/pilot-harness)
- Stars: 240
- Language: TypeScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `ai-agent`, `codepilot`, `deepseek`, `deepseek-harness`, `desktop-app`, `dsh`, `dsh-plugin`, `electron`, `linux`, `macos`, `typescript`, `windows`
- Forks: 13
- Open Issues: 16
- Last push: 2026-08-20T12:42:53.000Z
- Added: 2026-08-20T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:op7418/pilot-harness/packages/bundle/base
```

## Wiki

## 一句话定位
这是 DSH 的共享底座组合包（profile bundle），它本身不含运行时代码，而是用一份 `cordis.patch.yml` 把 DSH 默认开启的全部基础插件行（模型适配器、工具、持久化、策略、设置/凭证、遥测、宿主级 subagent provider）一次性铺到空 profile 根上，作为每个 profile 的第一层 patch。

## 核心能力
- 一次性挂载 DSH 默认的模型层：DeepSeek 官方适配器、Pi-AI 多 provider 适配器（默认挂载但等待 `llm-pi-ai:` 设置启用）、`deepseek-v4-flash` 默认模型选择
- 挂载代理、会话、工具注册表与设置/凭证管理：会话持久化（JSONL）、附件落盘、设置热重载、凭证解析
- 启用内置工具集合：bash/pwsh 工具（按平台二选一）、文件系统读写、glob 搜索、网页搜索（DeepSeek 搜索）、Todo、SubAgent、Goal、Workflow、Ralph、Skill 等
- 启用策略与沙箱：workspace-write / read-only / danger-full-access 三档权限预设、沙箱策略、用户审批、文件观察策略、token-meter、spill 策略
- 平台门控两个 shell 栈：bash 在非 win32 启用，pwsh 仅在 win32 启用，每个平台恰好挂载一个 shell 栈
- 挂载但默认关闭会话遥测，需要 `DSH_TELEMETRY_MODE` 环境变量显式打开；默认也不开启 SQLite 全文本搜索

## 技术实现
- **语言**: TypeScript（`src/index.ts` 为空导出，无运行时 API）
- **关键依赖**: 仓库 workspace 内的 100+ 个 `@deepseek-ai/dsh-*` 包（LLM/Agent/Session/FS/Sandbox/Tools/SubAgent 等），以及 `@deepseek-ai/cordis`、`@deepseek-ai/cordis-plugin-hmr`、`@deepseek-ai/cordis-plugin-timer`
- **架构模式**: 不是代码插件，而是 profile bundle；`package.json` 的 `dsh.bundle.patch` manifest 字段指向 `cordis.patch.yml`，由 profile composer 读取并以「整行替换 config」的方式把基础插件行插入空的 profile 根，后续 bundle 层（web-app、headless 等）和用户 `cordis.patch.yml` 按 id 覆盖这些行
- **入口文件**: `cordis.patch.yml`（实际生效的「内容」），加上 `src/index.ts`（空导出占位）、`src/invariant.ts`（无运行时检查的 invariant companion）

## 适用场景
这是所有 DSH profile（headless、web-app、用户自定义 profile 等）默认携带的第一层；当你需要在 DSH 之上搭建自定义 agent、命令行模式、Web 模式或工作流时，base bundle 已经把所有必备的内核能力（模型层、Agent 循环、会话、工具、权限、沙箱、持久化）装好，无需再逐一接入各 dsh-* 包。**base bundle 通常不会被单独安装**，它跟着任意一个上层 profile bundle（headless、web-app 等）一起生效。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH 框架 | 0.1.0-rc.7 同代 | 与仓库 workspace 内其他 `@deepseek-ai/dsh-*` 包同步发布 |
| Node.js | ^22.19.0 \|\| >=24.0.0 | 见仓库根 `package.json` 的 `engines.node` |
| macOS / Linux | — | bash-sandbox、tool-bash 启用；pwsh 行被禁用 |
| Windows | — | pwsh-sandbox、tool-pwsh 启用；bash 行被禁用，可通过 profile 覆盖恢复 |
| 平台限制 | 跨平台 | 同一份 patch 文件按 `process.platform` 自动门控两个 shell 栈 |
| 原生模块 | 无 | 不引入 native 模块；沙箱的 win32 链由 `@deepseek-ai/dsh-sandbox-windows-acl` 单独提供 |

## 安装方式
```bash
dsh plugin --profile web add github:op7418/pilot-harness/packages/bundle/base
```

## 配置项
base bundle 本身没有「用户级」配置开关，它通过 patch 文件把若干个插件行的 `config` 写死或绑定到环境变量。patch 文件里出现的环境变量与默认值如下：

| 环境变量 / 配置来源 | 取值 | 说明 |
|---|---|---|
| `DSH_TELEMETRY_MODE` | 默认 `DISABLED` | 控制会话遥测模式；设为 `FULL` 或 `FEEDBACK_ONLY` 才会真正上报 |
| `DSH_TELEMETRY_OTLP_URL` | 默认 `https://harness-telemetry.deepseeksvc.com/v1/logs` | OTLP/HTTP 上报地址 |
| `DSH_TELEMETRY_DISABLED` | 任意非空值 | 设为非空即关闭遥测（包括 `'0'`、`'false'`），由启动器改写该行为 `disabled` |
| `DSH_PERMISSION_MODE` | 默认 `workspace-write` | 全局权限模式，可选 `read-only` / `workspace-write` / `danger-full-access` |
| `DEEPSEEK_API_KEY` | 必填（生产使用） | 解析会话标题、网页搜索和聊天模型调用所需的凭证，由 `dsh-credentials-local` 在请求时按引用解析 |
| `agent-default-model` 的 provider/model | `deepseek-official` / `deepseek-v4-flash` | 默认代理使用的模型与适配器，可在用户 `settings.yaml` 覆盖 |
| session-title-llm 标题长度 | 目标 5 词 / 10 CJK 字 | 自动生成会话标题的长度目标与上限 |
| `bash-sandbox` 超时 | `60000` 毫秒 | bash 命令执行的默认超时 |
| 工具结果裁剪 | 阈值 8192 字符，头 4096 尾 1024 | 工具返回过长时进入对话前的裁剪窗口 |
| Ralph 子代理最大轮数 | 64 | Ralph 工具的最大迭代轮数 |

## 常见问题
**Q: 装上 base bundle 后还需要装什么？**

A: base bundle 只提供「底座」，即所有 profile 都共享的内核；它本身不暴露 CLI 入口。要使用 CLI，需要在 profile 的 `dsh.profile.bundles` 里追加 `dsh-headless`；要做 Web 界面，追加 `dsh-web-app`。这些上层 profile 已经在 patch 上叠加了具体配置，base 只是最底层。

**Q: 我能否在 base 之上重新指定默认模型？**

A: 可以，但要走「整行替换」：在 profile 或 home 的 `cordis.patch.yml` 里写一条 `insert` 行，id 仍为 `agent-default-model`，把 `provider` 与 `model` 字段重述一次，整行替换 base 写入的默认。也可以在 web 模型的设置页直接修改 `settings.yaml` 的 `llm-deepseek:` 段，重启即生效。

**Q: Windows 上我想用 bash 工具而不是 pwsh，怎么办？**

A: 在 profile 或 home 的 `cordis.patch.yml` 里同时写两条改动：把 `pwsh-sandbox` 与 `tool-pwsh` 重写为 `disabled: true`，再把 `bash-sandbox` 与 `tool-bash` 的 `disabled` 字段删除或设为 `false`。两条命令都注册同一个 `bash` 服务，配方不完整会在加载时直接报错。

**Q: 默认不开启的内容搜索怎么打开？**

A: base 里挂载的 `session-query-sqlite` 默认 `openAt: never`，SQLite 文件也不会被打开。在后续 profile 层覆盖这一行，把 `openAt` 设为 `first-search` 或 `startup`，并给一个持久化的 `path`，全文本搜索就会启用。

**Q: 卸载或重置 base 后，已有的会话记录会丢吗？**

A: 不会。base 本身不直接持久化数据，会话由 `dsh-session-persistence-jsonl` 写入到 `$DSH_HOME/sessions/`，附件写到附件后端目录。卸载或重写 base 不会删除这些目录；但如果同时禁用 `session-persistence-jsonl` 或 `attachment-local`，新产生的会话会落在别处，旧会话仍可被搜索型 profile 读取。

**Q: 启动时报 "row is disabled by config" 之类错误怎么办？**

A: 通常是用户层 cordis.patch.yml 写了非法 disable 配置，或与 base 的 platform 门控冲突。检查用户 patch 里是否对 bash/pwsh/sandbox 行写了不完整的覆盖（参见 Windows bash 恢复那条），再确认 `DSH_PERMISSION_MODE` 是否取了一个未在权限预设里登记的值。

## 上手难度
入门 — base bundle 没有用户级 API，不需要写代码也不需要单独配置，普通用户通过 `dsh plugin add` 安装一次即可由上层 profile 自动套用；自定义开发者只要读懂 `cordis.patch.yml` 与「整行替换」语义就能改动。

## 已知问题与限制
- **patch 替换整行 config，不做深度合并**：profile 必须重述该行需要保留的全部字段，没有逐字段合并层（见 `packages/bundle/base/README.md:21`）
- **Windows 临时目录授权是按会话私有**：workspace-write 把写入限制在工作区 + 会话自己的 temp 子目录（`<temp>\dsh-<hash>`），受限子进程的 TMP/TEMP 被改写；read-only 不授予任何临时写入权限。详见 `@deepseek-ai/dsh-sandbox-windows-acl`
- **不会自动挂载 Codex / Claude Code provider**：base 的依赖列表里不含 `@deepseek-ai/dsh-subagent-codex` 与 `@deepseek-ai/dsh-subagent-claude-code`，需要哪个就由哪个 profile 单独 opt-in
- **同 patch 文件按平台门控两个 shell 栈**：bash 在 win32 上行 disabled，反之 pwsh 在 POSIX 上行 disabled；只有完整配对修改才能切栈

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [pilot-harness](https://deepseek-plugin.org/plugins/op7418/pilot-harness/packages/bundle/base)
Wiki generated by AI (model: `MiniMax-M3`)
