From d7d2acf502deed09b724e5f4effc313008f94444 Mon Sep 17 00:00:00 2001 From: Wanderingss Date: Mon, 2 Nov 2026 20:15:51 +0800 Subject: [PATCH] Docs: CHANGES.md with the full account of this fork's improvements --- CHANGES.md | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++ SETUP.md | 24 +++++++++ 3 files changed, 183 insertions(+) create mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..d33eb2f --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,155 @@ +# 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..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 `;`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 `)也做 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 时注意。 + +## 已知限制 + +- 无 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`)。 diff --git a/README.md b/README.md index 39e1c4a..4651b9f 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,7 @@ Read the numbered documents in order; each builds only on the ones before it. ## License [MIT](LICENSE) + +## 本 fork 的改动 + +本仓库是上游 dsh-superagent 研究原型的 fork:新增 Gitea Source(自建 Gitea,纯轮询拉取,无 webhook、无需公网回调)、Windows 全面移植修复,以及默认模型头部快切、Source 活性上报、工单目录回收等部署运维改进。逐提交的动机与改动位置、部署实战要点和已知限制见 [CHANGES.md](CHANGES.md)。 diff --git a/SETUP.md b/SETUP.md index 87a8366..a138a24 100644 --- a/SETUP.md +++ b/SETUP.md @@ -77,3 +77,27 @@ Send one message, under 300 words: what will be installed, the accounts and CLIs - How to try it: from an account on the allowed list, comment `@ ` on an issue in the repository. A Ticket appears on the overview within about a minute. - Three rules for the operator, from the Responsibility section of `docs/000-philosophy.md`: start narrow, tune for less noise, own what the agent says. - Where to read more: the numbered documents in `docs/`, in order. + +## Gitea Source 配置要点(本 fork) + +替代或并存于 GitHub 的自建 Gitea 来源。设计决策见 `GITEA-DESIGN.md`,逐提交说明见 `CHANGES.md`。 + +- **启用与 sourceId**:bundle 的 `cordis.patch.yml` 里插件 `dsh-superagent-gitea` 默认 + `disabled: true`,部署 profile 需启用它;设置保存在 `Settings.sources.gitea`(sourceId + `gitea`),字段为 `url`、`repository`(owner/name)、`tokenEnv`、`pollSeconds`、 + `failuresBeforeError` 与三个观察开关。 +- **url 直连实例**,不要套反向代理——部分反代会吞掉 `Authorization` 头,表现为一切 401。 + 地址不带末尾斜杠(`^https?://[^/]+$`);Gitea 走 http 也可以,凭据行按部署 URL 自身的 + scheme 写(见 `e8b91ab`)。 +- **tokenEnv 存的是环境变量的名字**(默认 `GITEA_TOKEN`),凭据本体绝不进 Settings。惯常做 + 法:写入 `~/.dsh/.credentials.yaml`,由 DSH 注入 orchestrator 进程环境。同一个 token 两用: + 既做 API 认证(`token `)也做 git 的 HTTPS 密码(worker 的 `gitea-git-credentials` + store)。1.26+ 的实例写路由必须 token;`login:password` Basic 仅作 token 校验损坏时的临时 + 兜底(见 `589b5a6`)。 +- **三条可选仓库流**:`observeIssues` / `observePulls` / `observeComments`,全部默认 false; + 通知流(`/notifications`)始终在轮询,这三条按团队需要再开。轮询间隔 `pollSeconds` 默认 + 60 s(最小 60,实例给了 `X-Poll-Interval` 更长时以实例为准)。 +- **allowlist**:Policy 的 `allowed` 列 Gitea login(`^[A-Za-z0-9._-]+$`);`bots` 名单要手工 + 维护——Gitea API 没有 `is_bot`,不在名单里的账号一律按 human 处理。 +- **彩排**:可以用 SQLite 后端的单机 Gitea 实例试跑整套配置,不必先上生产。用 CLI 建管理员 + 账号时带 `--must-change-password=false`,否则首次登录强制改密会让 API 建 token 返回 403。