160 lines
11 KiB
Markdown
160 lines
11 KiB
Markdown
# 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`)。
|