Files
dsh-superagent/CHANGES.md
T

160 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CHANGES — 本 fork 的改动
给接手这个 fork 的人。上游是 deepseek-ai/dsh-superagent-prototype(DeepSeek 团队的研究原型),
基线 = `6337fc8`(pristine);其上是本 fork 的 11 个提交(`4379973` → `cf40686`)。
## 总述
本 fork 在上游研究原型之上做了三件事:
1. **新增 Gitea Source**:面向自建 Gitea 实例的纯拉取轮询——不用 webhook、不需要外网回调,
部署机器无需公网入口。设计决策见 [GITEA-DESIGN.md](GITEA-DESIGN.md)。
2. **Windows 全面移植**:上游原型只在 Linux/macOS 上跑通,本 fork 修复了四个卡死部署的
Windows 缺陷(都在 `4379973` 内,见下文)。
3. **部署 / 运维层若干改进**:面板头部一键切换部署默认模型、Source 活性与轮询停滞上报、
结束工单执行目录的磁盘回收、复用会话的角色模型重新指认等。
## 提交清单
按时间序;每条的"改了哪里"都对照 `git show --stat` 核实过。
### `4379973` Sources: add Gitea; fix Windows shims for shell tool, repo paths and stateDir
最大的一笔,Gitea Source 全家桶 + 四个 Windows 缺陷修复合在同一个提交里。
Gitea Source:新增 `src/sources/gitea.ts`(Source kind、轮询 round、`GiteaConfigSchema`、
插件入口)、`src/sources/gitea-api.ts`(最小 REST 客户端:凭据只从 `env[tokenEnv]` 读、zod
校验、分页、可注入 fetch)、`src/sources/gitea-trigger.ts`、`src/sources/gitea-channel.ts`;
`src/kv.ts` 加 `gitea` 命名空间(since / retry / 每线程与每流游标);`src/deployment.ts` 加
`GiteaSetupSchema`、`giteaOnly()`、`resolveGiteaDeployment()`;`src/app/setup.ts` 加
`source:gitea` 分支与 `resolve()` 的 gitea-only 路径;`src/setup.ts` 加 gitea 的 setup problem
码;`src/infra/environment.ts` 加 Gitea 的 gh 封禁 shim 与 git 凭据 store;bundle 接线
(`cordis.patch.yml` 插件行默认 disabled、`package.json` 导出 `./gitea-watch`、
`tsdown.config.ts` 入口);`src/client/locales.ts` 文案;docs/007 模块与测试表;
`GITEA-DESIGN.md`;`tests/gitea.spec.ts`。
四个 Windows 缺陷:
- **stateDir**:`src/deployment.ts` 的 `absolute()` 校验正则 `/^\//` 拒绝 `D:\...` 盘符路径
→ 扩为接受盘符与 UNC。
- **worker shim / git 凭据**:Gitea 的 git 凭据不走 POSIX shell 型 credential helper,改用
git 内置的 `store --file=...`(`src/infra/environment.ts`,代码注释:Windows git 行为一致),
写入 gitconfig 的路径反斜杠统一换成正斜杠。
- **仓库路径**:Gitea 的 API 不认 `%2F` 编码的 owner/repo,`resolveGiteaDeployment` 直接以
字面 `repos/${repository}` 请求(`src/deployment.ts`)。
- **shell 工具**:`src/app/sessions.ts` 给 master 会话的 `tools.restrict` 白名单硬编码
`'bash'`,Windows 上注册的 shell 工具是 `pwsh`,症状 = 既有会话 resume 必挂
unknown global tool → 新增 `SHELL_TOOL = process.platform === 'win32' ? 'pwsh' : 'bash'`
并让白名单与 WORK_TOOLS 都用它。
### `e8b91ab` Infra: write gitea git-credentials line with the deployment's own scheme
动机:凭据行写死 `https://`,与 http 实例的 `credential.<url>.helper` 查找不匹配,git 取不到
凭据。改了哪里:`src/infra/environment.ts` 的 `writeGiteaCredentials()` 改按部署 URL 自身的
scheme/host 写这一行。
### `589b5a6` Sources: accept login:password Basic credential for Gitea (API + git store)
动机:当时所用实例的 token 校验链损坏,需要以账号密码 Basic 顶替。改了哪里:
`src/sources/gitea-api.ts` 新增 `giteaAuthorization()`——凭据含 `:` 走 HTTP Basic,否则按
`token <x>`;`src/infra/environment.ts` 的 `writeGiteaCredentials()` 同一规则(Basic 时 store
只存冒号后的密码段)。API 与 git 两路一致。历史注记:上游 Gitea 1.26 收紧 Basic 后本部署
已改回细粒度 token,此提交保留为兜底能力(见"部署实战要点")。
### `a662188` Panel: switch the deployment default model from the header
动机:面板只能显示 DSH 默认模型不能改,换 Executor / Assessor 模型要手工编辑 profile。
改了哪里:新增 `src/app/default-model.ts` 的 `switchDefaultModel()`——走
`sessionController.selectModel`(与 DSH 模型选择器同一路径,写 profile 的
`agent-default-model`),按 catalog 校验、值未变报 unchanged、后台保存靠轮询确认;
`src/app/http.ts` 加 `PUT default-model` 路由;`src/client/default-model.tsx` 头部控件;
`tests/default-model.spec.ts`。只影响新会话(既有会话见 `dee76b1` / `acacbb2`)。
### `b348d69` Overview: report liveness and a Source whose poll loop stopped
动机:轮询循环死了的服务照常响应每个请求,面板看不出来。改了哪里:
`src/app/views/overview.ts` 新增 `liveness`(startedAt / uptimeMs / lastPollAt / pollAgeMs /
polling),Source 连续 3 个自己的轮询周期没有新动静(且不低于 180 s)报 `stalled` problem;
`src/app/http.ts`、`src/app/runtime.ts` 传入进程 startedAt;`src/client/overview.tsx` 展示。
### `3bee231` Execution: reclaim the directories of Ended Tickets after a retention
动机:每张工单留下 clone 与产物,长期吃磁盘。改了哪里:新增 `src/app/reclaim.ts`——
Reclaimer 每小时扫一次执行根,删超过保留期的 ended 工单目录;`src/policy.ts` 加
`executor.reclaimAfterDays`(默认 14,0 = 关闭,0–365);面板 overview / execution 显示回收
摘要。防误删四道闸:过了保留期、路径必须在执行根内、目录 `basename` 必须等于
`execution.name`、该目录不在任何 open 工单的目录集中。
### `d527ad8` Sessions: account a restored Session to its Workspace
动机:数据迁移后面板看不到 worker 会话——`ensure()` 只在新建会话时登记 Workspace,从盘上
恢复的会话永远不在 Workspace 记录里。改了哪里:`src/app/sessions.ts` 改为对每次 ensure 都
幂等 `workspaceRegistry.create(cwd)`,未记账的既有会话补 `attachSession`(失败只 warn,不拦
启动)。
### `dee76b1` + `acacbb2` Roles: re-point reused Sessions when a role model drifts
动机(`dee76b1`):角色模型只给新建会话播种,旧会话被唤醒后永远用旧路由——症状是改了
`executor.model` 之后老工单仍用 DeepSeek,新开任务才生效。改了哪里:`src/app/role-models.ts`
新增 `alignRoleModel()`,dispatch 时比对既有会话的持久 selection / header 与配置的角色模型,
漂移则 append `model/selection` 事件;`src/app/sessions.ts` 对非新建会话调用它。
关键修正(`acacbb2`):只 append 事件不刷内存缓存,真机复跑下一半秒的装配仍用旧模型——
prompt 装配读的是 Session Controller 的内存 selection。改为优先调用 `sessionController`
实例内部的 `agents.selectForNextRequest()`(append + 刷 Agent 缓存,且不动全局默认),该
内部面不存在时回退公开 `selectModel()`(会顺带保存默认,换正确性)。
`tests/role-models.spec.ts` 钉住快路径与回退路径两条。
### `17be661` Default model: let the picker choose a reasoning effort
动机:头部快捷菜单只有 provider/model,catalog 声明的思考强度只能去 Eval / Execution 表单
里手打。改了哪里:`src/client/default-model.tsx` 为目录里带 efforts 的行展开二级菜单(模型
默认 + 各档位);`src/app/default-model.ts` 校验 effort 属于目录、只改强度也算真切换、转发
带 brand 的 id;docs/006、docs/007 同步。
### `cf40686` Sources: carry the Gitea kanban convention in the Source's own instructions
动机:看板约定是部署侧约定,不该写进用户仓库的 AGENTS.md。改了哪里:`src/sources/gitea.ts`
注册环境变量 `DSH_SUPERAGENT_GITEA_KANBAN`,约定文本写进它自己的 description,经既有的环境
说明机制注入 worker 提示词。约定内容:Issue 挂在项目看板上,列名与标签同名,现共 9 列 =
文档性维护、已完成、进行中、下一顺位、排队、等外部资源、暂缓、观察、开放问题(首列原名
Uncategorized,已改名);该 Gitea 版本没有 Projects API,改标签或移卡后跑外部同步脚本
card-sync.mjs,只碰这个看板、不直接操作数据库。
## 测试基线
上游原型在 Windows 本机跑 `pnpm test` 有约 38 个固有失败(tmux、lark-cli、POSIX shims、
deployment 相关、docs 测试的 path.sep 枚举、NTFS 语义等)。本 fork 的门禁口径 = **零回归**:
失败集合必须始终 ⊆ 基线,即 `pnpm typecheck` 全绿且失败集合不新增条目。新增测试文件:
`tests/gitea.spec.ts`(21 例)、`tests/role-models.spec.ts`(14 例)、
`tests/default-model.spec.ts`(14 例)、`tests/reclaim.spec.ts`(5 例)。
## Gitea 部署实战要点(运维备忘)
- **1.25 / 1.26 / 1.27 均无 Projects REST API**,Projects API 首发于 1.28。判断以在跑的二进制
为准——main 分支的 swagger 是预览文档,≠ 已发布能力。看板同步只能靠外部脚本(见
`cf40686`)。
- **1.26 起密码 Basic 在 API 写路由被拒**(报 `token is required`),只读路由仍可用;必须用
细粒度 token。scope 按最小需要给,本部署用的例子:`write:issue` + `write:notification` +
`read:user` + `write:repository`。
- **一个凭据两用**:同一个 token 既做 API 认证头(`token <x>`)也做 git 的 HTTPS 密码——本
fork 的 `gitea-api.ts` 与 `environment.ts` 凭据 store 是同一规则,轮换时一处换新两处都要动。
- 实例若 token 校验链坏掉,可临时回退 `login:password` Basic(`589b5a6`);升级 1.26 后写
路径必须回到 token。
- **Windows git 兼容性**:MSYS bash 自带的 ucrt64 构建 git 2.56 与该 Gitea 实例的 http/2
协议交互挂起(clone 卡死);Git for Windows 2.39 正常。worker 用的是系统 PATH 里的 git,
不受 MSYS 那份影响,但手工在 MSYS 环境里跑长 clone 时注意。
- **插件不自动升级**:装时锁版本。升级纪律=一次升一个、升前看 changelog、升后跑门禁+验
overview;带本地热补丁的插件(如 `workbuddy-to-dsh` 桥脚本)升级会盖掉补丁,升完要重打补丁
并核对两侧桥脚本哈希一致(不一致时 buildDiff 判 stale,桥互杀重启);重度依赖在跑的插件
没有明确收益不升。详见 INSTALL.md「插件升级纪律」。
## 已知限制
- 无 webhook,纯轮询(默认 60 s,可配 60–3600 s);响应速度换部署侧零入站端口。
- bot 不自动 merge PR,人工合并是设计(Gitea 侧 auto-merge 在 MVP 裁剪线之外,见
GITEA-DESIGN.md)。
- Gitea API 无 `is_bot` 字段,非 agent、不在名单里的 actor 一律按 human 处理;自动化账号要
手工维护进 Policy 的 `bots` 名单。
- 结束的工单可被 human 唤醒,所以执行目录回收留有保留期(默认 14 天,见 `3bee231`)。