Repository navigation
Releases: Fish-under-sea/dsh-agent-teams-fish
Release list
AgentTeams v1.0.1
1.0.1
2026-10-21
- Aligns the standalone AgentTeams package with the Fish aggregate and its ten in-repository members at
1.0.1. - Keeps the
agent-teamsrow id,.agent-teamsstate directory, configuration fields, tool names and Host compatibility policy unchanged. - No AgentTeams runtime behavior changes are included in this version.
pnpm verify,pnpm typecheckandpnpm buildpass. One symlink test is skipped because this Windows host does not permit symlink creation.
AgentTeams v1.0.0
1.0.0
2026-10-19
- npm identity migrates from
dsh-agent-teams-fishto@fish-under-sea/dsh-agent-teams-fish. - Coordinates version 1.0.0 with the Fish aggregate and its ten in-repository members.
- Keeps the
agent-teamsrow id,.agent-teamsstate directory, configuration fields, tool names and Host compatibility policy unchanged. - Updates bundle entry, diagnostic package discovery and isolated runtime-test resolvers for scoped installation.
- Rewrites Chinese and English public READMEs around current capabilities, migration and safety boundaries. Upstream attribution and historical notes remain available.
- This is a packaging migration, not a claim of newly merged upstream functionality or repaired historical sessions.
AgentTeams v0.7.3
v0.7.3:修复 DSH NEXT 2.0.19 上的成员派发与子会话可读性
发布日期:2026-10-18
问题
DSH Desktop NEXT 2.0.19-next 内置的宿主版本是 0.2.1-rc.2。在这个宿主上建队、批准、派任务都正常,但成员一启动就失败,日志里是两句话:
agent-teams: member dispatch failed for verifier: Error: agent-teams: provider "spawn" cannot apply a member persona
tool-session-query: ... session "eb3c1b69-..." contains event type "agent-teams/member-composition" (seq 4)
unknown to this harness and not marked ignorable; refusing to interpret the log
两个问题彼此独立:
- 宿主不再声明 persona / toolFilter 能力。
@deepseek-ai/dsh-subagent-spawn-in-process的 capabilities 固定为agentOptions/agentPreset/outputSchema/depthLimit,SubagentStartRequest也没有persona/toolFilter字段 —— 成员人设与工具限制改由宿主预设(agent preset)负责,启动请求根本不接受这两项。旧代码按「宿主支持 persona」直接发起派发,于是被能力检查拒绝,成员一个也起不来。 - 插件往会话里写宿主不认识的会话事件。 旧代码用
session.append('agent-teams/member-composition', ...)存组合快照;0.2.1-rc.2的事件目录里没有这个类型,事件也没标ignorable,于是该子会话从此不可读(SessionFormatUnsupportedError),后续send_message/ 交付一律报subagent "..." is unavailable。这不是显示层问题:日志本身被拒绝解释。
修复
- preset-only 宿主走组合安装路径。 能力位里没有
persona/toolFilter、但有agentPreset且宿主提供startActivation时,不再把它们塞进启动请求,改为在成员子上下文里自行安装:childCtx.tools.restrict(toolFilter)限制可用工具;childCtx.systemPrompt.section({ name: 'deployment:persona-prefix', order: DEPLOYMENT_PERSONA_PREFIX, text: persona })写入成员人设。
宿主原生支持这两项能力的老版本仍走原路径,行为不变。
team.json成为冷恢复的唯一权威。 宿主的 continuable descriptor 在启动路径上不保存 persona / toolFilter,所以现在每个成员都把生效的toolFilter记进team.json;冷恢复时从这份持久记录重建人设与过滤,并在成员第一个请求之前安装。没有过滤记录的成员拒绝恢复(has no durable composition snapshot),不会以「无限制工具」的形态偷跑;tools.restrict失败同样中止派发。- 不再写任何自定义会话事件。 删除
agent-teams/member-composition的写入与SessionEventMap增强,组合状态只存在于插件自己的持久记录里。宿主日志保持可读。 - 兼容矩阵收录
0.2.1-rc.2。compatibility.json增加该宿主(track: preview),21 条@deepseek-ai/dsh-*peer 白名单同步枚举;推荐宿主仍是0.2.0-rc.2,未提升任何版本的支持等级。
回归覆盖
- 新增
scripts/member-composition.test.mjs(4 个用例,已挂进verify:harness-contract):preset-only 宿主在首个请求前装好人设与精确过滤;restrict失败必须中止派发而不是放行;冷恢复只从team.json重建且不追加会话事件;无过滤 / 损坏过滤的成员记录被拒,普通子代理不受影响。 harness-compat-tdd32/32、member-failure-tdd三种矩阵各 10/10、lifecycle-verify的--activation-harness/--modern-harness/--delivery-harness全部通过;quality-gates、stability、lineage、tool-policies、capabilities等既有脚本照常执行。- 本机宿主
0.2.1-rc.2(DSH NEXT 2.0.19)的实际日志是本版本两个缺陷的原始证据;修复后同一宿主上类型检查、构建与上述回归全部通过。需要说明的是:capabilities.test.mjs中依赖真实子进程(PTCrun_code、spawnSync)的用例在被沙箱限制子进程的环境里会以EPERM失败,这与本版本改动无关,在无限制环境或 CI 中应全绿。
已知遗留
- 已被 0.7.2 写坏的子会话无法由本插件修复。 那些会话的日志里已经有宿主不认识的
agent-teams/member-composition事件,拒绝发生在插件加载之前的会话读取路径。可行做法是不要再向这些成员发消息,直接归档团队并重建;插件不会静默删除或改写用户的历史会话。
升级
dsh plugin --profile <profile> add dsh-agent-teams-fish@0.7.3升级后重启 DSH。已存在的 team.json 无需迁移:成员缺少 toolFilter 记录时,冷恢复会明确拒绝并在派发前报错,而不是降级放行;重新派发一次即可写入新的持久组合。
v0.7.2:修复新版宿主上的成员派发故障
v0.7.2:修复新版宿主上的成员派发故障
发布日期:2026-10-17
问题
DSH 0.2.1-alpha.2 上,团队计划可以建立和批准,但成员启动失败,任务无法进入执行。日志中的错误为 Cannot read properties of undefined (reading 'call')。
新版 @deepseek-ai/dsh-subagent 移除了 start / startContinuable,改用 startActivation。旧插件的委派守卫仍无条件包装旧方法,导致启动时调用不存在的函数。此前宿主白名单更新只解决了安装与插件行兼容声明,没有覆盖这条运行时启动路径。
修复
-
在宿主兼容层集中选择成员启动接口:优先
startActivation,旧宿主回退到startContinuable;两者都缺失时给出明确错误。 -
新版入口显式使用
delivery: 'parent',保留持久子会话、父级通知与恢复语义;不切换为临时调用方交付。 -
委派守卫只包装真实存在的方法,覆盖新版入口,不再伪造旧方法;保留原方法的
this。 -
停止成员时优先使用新版
drainChildren,旧宿主回退到drainContinuableChildren;仍只停止指定成员子树,等待递归停止完成,保留失败上抛,避免重派或归档误报成功。 -
保持成员工具过滤、成员相对委派深度预算、已退出成员与团队停机的准入限制;热重载卸载后还原原始方法描述符。
-
成员最终模型路由及备用路由遵守当前会话捕获的子代理模型白名单,越界在子会话创建前拒绝;省略参数也不再继承白名单外的队长模型。不自动挑选替代模型,无捕获策略的旧宿主保持原有行为。
-
通过
ctx.get('sessionProjections')读取可选投影服务,服务缺失时回退会话持久事件;避免在真实 Cordis 插件上下文中直接读取未注入服务导致建队失败。
回归覆盖
新增启动、停止和模型策略回归,覆盖仅有新版 API 的成员启动、嵌套守卫卸载、成员委派预算与停机检查、缺失启动和停止契约的显式错误。模型策略测试覆盖显式选择、继承、备用路由、宿主投影、损坏策略、投影缺失时的持久日志恢复,以及旧宿主无策略行为;真实 Cordis 上下文分别验证可选服务存在与缺失。新增 --activation-harness 生命周期矩阵,并挂入 verify:harness-contract;原有旧版、modern 和 delivery 矩阵继续执行。
Windows / DSH Desktop 内置 0.2.1-alpha.2 的本机验收已实际通过计划创建与 Web 批准、成员领取与状态更新、32/32 回归测试、父级回报及工具归档。独立读取成员原始持久请求头,确认实际使用预选 qwen-f/qodercn/qfmodel 与 low 推理档位,未继承队长模型;没有修改其他团队数据。该结果不替代其他宿主与平台的 CI 矩阵。
源码与产物同步更新。发布流程继续要求 Ubuntu / Windows 静态检查及支持清单中的全部真实宿主验收通过,然后发布同一份候选包;推荐宿主仍为 0.2.0-rc.2,不扩大未知版本的支持范围。
升级
dsh plugin --profile <profile> add dsh-agent-teams-fish@0.7.2升级后重启 DSH,使新的宿主端模块生效。无需修改团队记录或迁移配置;已失败的派发需在插件重新加载后显式重试,本版本不自动恢复已失败任务,也不会擅自恢复用户暂停的团队。
v0.7.1
dsh-agent-teams-fish v0.7.1
修复「可选字段填空串就建不起团队」:把内联计划(inline plan)里所有可选文本字段的 "" / 纯空白按未提供处理,与 add_member / edit_plan 已有的口径统一。
另修两处会打红 pnpm verify 的问题:#161 的毫秒容差过小(Windows 上约一半概率误判),以及符号链接用例在无权限主机上不会跳过。
症状
某个会话里 agent_teams_create 连续失败 34 次、0 次成功,另一个会话 14 次、0 次成功,两个代理一个都没启动。报错在两条之间反复:
Error: profiles.inline-plan.members[0].tool_policy must not be empty (x15)
Error: profiles.inline-plan.members[0].provider must not be empty (x6)
Error: profiles.inline-plan.members[0].model must not be empty
Error: profiles.inline-plan.members[0].reasoning_effort must not be empty
Error: unknown tool policy "default" (no toolPolicies are configured in this deployment)
Error: AgentTeams profile "inline-plan" has no members
同一个习惯还在另外五个工具上一致复发,证明这是调用方行为、不是某一个工具的契约:
Error: `provider` must be non-empty (list_subagent_models)
Error: working-directory: cd must not be empty (working_directory)
Error: include must be a non-empty glob when given (grep)
Error: invalid justification: expected a non-empty sentence (pwsh / edit)
根因
队长模型把每一个可选参数都填成空字符串,而不是省略它们。模型自己的推理记录写得很直白:
"It seems I'm filling in all fields without realizing it! The tools I need require all parameters, even the optional ones, to work correctly."
"I'm thinking through whether we can omit keys in the tool call."
工具 schema 里这些字段确实是可选的(required 只有 ["name"],且没有任何 strict / default 约束),所以这不是 schema 逼出来的,是模型的调用习惯。而插件侧两条路径的口径不一致:
| 路径 | 归一化函数 | "" 的结果 |
|---|---|---|
add_member / edit_plan |
trimmedOptional(src/tools.ts:309) |
洗成 undefined,放行 |
inline plan(agent_teams_create) |
optionalNonEmptyString(src/profiles.ts) |
抛 must not be empty,必失败 |
agent_teams_create 的顶层 profile 早已做了空串归一(src/tools.ts:1002,注释写明 "Some models materialize optional parameters as "" instead of omitting them (issue #99)"),但内联计划内部的成员字段没有——缺口就在这里。
于是模型陷入死循环:填 "" 撞空串校验 → 改填字面量 "default" 撞未配置的 toolPolicies → 回到 "",反复十余次后放弃。这不是模型不可用,也不是工具缺失:同一台机器上,只要可选字段省略不填,agent_teams_create 一次即成功。
修法
只改一处——src/profiles.ts 的 optionalNonEmptyString:
function optionalNonEmptyString(value: unknown, path: string): string | undefined {
if (value === undefined) return undefined
if (typeof value !== 'string') {
throw new Error(`${path} must be a string`)
}
const trimmed = value.trim()
- if (trimmed === '') {
- throw new Error(`${path} must not be empty`)
- }
+ if (trimmed === '') return undefined
return trimmed
}这一处覆盖全部可选文本字段 —— 成员级 role / provider / model / reasoning_effort / executionPrompt / tool_policy,计划级 description / protocol / executionPrompt,任务级 description。必填字段一律不动:requiredNonEmptyString 仍然严格,name / id / subject / fallback.provider / fallback.model / 依赖项 id 填空串照旧报错,非字符串值照旧是硬错误,provider 给了 model 却没给仍然报 sets provider without model。
为什么不是「补一句更友好的报错」:空串本身不携带任何信息,它和「没填」在语义上就是同一件事。既然编辑路径已经这么判了,计划路径跟随同一口径比多写一条错误消息更简单,也更不容易再分叉。
附带修复:#161 判决漂移检测的毫秒容差过小
质量门的「这条判决还覆盖当前版本吗」在无摘要的旧版判决上只能靠文件 mtime 判断,而 mtimeMs(文件系统时钟)与 updatedAt(Date.now(),整毫秒)来自两个不同的时钟。Windows 上文件系统时钟会领先进程时钟:40 万次写入实测 p99 1.7ms、最坏 2.2ms(含 Date.now() 的 1ms 截断);8 路并发写盘压力下最坏 1.2ms。
原容差 MTIME_TOLERANCE_MS = 1 低于这个偏移,于是判决之前写好的文件会被读成「判决之后被修改」,报出假的
lib/stats.js was modified after this verdict closed (file mtime is later than the verdict)
scripts/quality-gates-content-bound.test.mjs 的前置断言因此在 Windows 上约 57.75%(231/400 次实测)的概率失败;单跑常绿、与其他 6 个测试文件并发跑时约 3~4 次挂 1 次,正是这个原因。
容差改为 4ms:覆盖实测偏移并留余量,同时远小于任何真实的判决后编辑(回归测试用 10ms 真实等待来验证「判决后的写入必须被发现」)。改后同一探针 400 轮失败 0/400,测试连跑 10/10 全绿。
新增两个确定性回归用例(不依赖真实时钟抖动,任何平台都能验证容差):把 mtime 人为设成 updatedAt + 2.2ms 必须仍判可交付;设成 updatedAt + 500ms 必须仍判漂移。
附带修复:符号链接用例在无权限主机上不跳过
#174 与 doctor 的两个用例要真的建符号链接,而 Windows 需要开发者模式或提权(否则 EPERM)。原来只有一处写了 process.platform === 'win32' ? 'symlink probe' : false 这种平台硬编码,另两处完全没跳过,于是在没有该权限的主机上必红。
改为统一的运行时探测 scripts/symlink-support.mjs(实际尝试建一次链接,false = 支持、字符串 = 跳过原因),三处用例共用:有开发者模式的 Windows 仍跑完整覆盖,无权限时以明确原因跳过,POSIX 永不跳过。
验证
新增回归测试 scripts/inline-plan-blank-optional-tdd.mjs(已挂进 pnpm verify 主链,另有 pnpm verify:inline-plan-blank),23 项断言全绿:
- 绿:截图中那次失败调用的原样参数(成员六个可选键全是
"")现在可以建出计划,且六个字段全部归一为 absent; - 绿:纯空白
" "与空串同解; - 绿:真实值(
provider/model/tool_policy)照常保留并 trim; - 红(保持严格):空成员名、空任务 id、空 subject、空依赖 id、非字符串可选值、
provider无model、provider给值而model给空串 —— 七条都必须继续抛错。
关于「子代理调不动」
排查过程中一并确认:截图会话里 subagent / subagent_fork / subagent_codex / subagent_claude_code 一次都没有被调用过,工具定义 70 个请求里 100% 在位。所以「调不动子代理」的真实形态是模型没有发起调用,加上它一旦发起就会沿用同样的空串习惯去填 list_subagent_models 的 provider(实测该调用确实被 `provider` must be non-empty 打回)。本版修掉的就是这条参数习惯的落地障碍。
顺带提示:这些会话的队长路由是
gpt(第三方中继),而subagent-model-selection-settings的allowedModels白名单只收录bailian-he/deepseek-f/kimi-f/qwen-f/glm-f/hy-f/mimo-f/en-f/cn-f/minimax-f,不含gpt。子代理若不显式指定路由,会从队长会话继承gpt,从而撞上宿主的child LLM route "gpt/…" is not allowed for this Session。这属于部署侧白名单配置,不是本插件的缺陷;要在这个路由上委派,请给子代理显式指定白名单内的provider+model,或把gpt加入allowedModels。
v0.6.1
dsh-agent-teams-fish v0.6.1
修复在 DSH 0.2.1-alpha.2 上被整行禁用的问题:compatibility.json 的 supportedHosts 与全部 21 条 @deepseek-ai/dsh-* peer 白名单此前未收录该版本,DSH 判定不兼容并自动停用插件行。
症状
升级 DSH 到 0.2.1-alpha.2 后,宿主日志出现:
dsh: disabling profile plugin row "agent-teams": Plugin dsh-agent-teams-fish@0.6.0
is incompatible with dsh 0.2.1-alpha.2: peerDependencies { … }
面板上插件显示「异常」,AgentTeams 团队协作完全不工作 —— 但这不是崩溃,是 DSH 的主动保护:声明面没跟上运行时,它宁可停用也不冒着崩溃或数据损坏的风险加载。
根因
本仓库用 compatibility.json 作为单一事实源,并由 scripts/compatibility.mjs 强制一条不变量:
每条
@deepseek-ai/dsh-*的 peer 范围必须精确枚举supportedHosts(+ sourceCandidates)里的每一个版本。
校验逻辑(validatePackageCompatibility):
const declared = range.split('||').map(value => value.trim())
if (declared.length !== declaredVersions.length
|| declaredVersions.some(version => !declared.includes(version))) {
throw new Error(`${name} peer range must enumerate the supported host targets and source candidates`)
}DSH 运行时升到 0.2.1-alpha.2 后,这个枚举列表没有跟上 —— 插件代码本身没有任何缺陷。
为什么不能写成 >=0.2.0-rc.2 一劳永逸:DSH 用 includePrerelease 语义比较,宽范围会宣称「与所有未来版本兼容」,而本仓库的立场是逐版本实测后再声明。所以新增宿主必须显式列出。
修法
compatibility.json:把0.2.1-alpha.2以track: "preview"加入supportedHosts。recommendedHost保持0.2.0-rc.2不动 ——validatePolicy明确禁止 alpha / beta 充当普通用户推荐目标。
package.json:给全部 21 条@deepseek-ai/dsh-*peer 范围追加|| 0.2.1-alpha.2(按既有约定放在最前)。scripts/compatibility.mjs门禁会强制第 2 步:只改compatibility.json而漏改 peer,立刻红。
验证
node scripts/compatibility.mjs
# Compatibility targets: 0.2.0-rc.2, 0.2.1-alpha.2, 0.1.7-rc.2, … rc=0- TDD 红→绿:先只改
compatibility.json→ 门禁报@deepseek-ai/dsh-agent peer range must enumerate the supported host targets(红)→ 补齐 21 条 peer → 转绿。 scripts/compatibility.test.mjs:10 通过 / 1 失败。那 1 个失败是doctor runs through an installed bin symlink(Windows 建符号链接需开发者模式,EPERM)—— 在未改动的上游克隆at-upstream里跑出完全相同的 10 / 1,证明是既有的环境限制,非本次回归。- 运行时依赖实核:本插件编译产物只 value-import 两个包 ——
@deepseek-ai/dsh-llm(createUserMessage)与@deepseek-ai/schemastery(default),两者在新宿主上均照常导出。其余@deepseek-ai/*均为类型导入(编译期擦除)。
兼容性约定(供后续维护)
| 项 | 值 |
|---|---|
recommendedHost |
0.2.0-rc.2(稳定线,普通用户推荐) |
| 新增支持 | 0.2.1-alpha.2(preview 轨道) |
| 已验证的历史宿主 | 0.1.7-rc.2 / 0.1.5-rc.3 / 0.1.5-rc.2 / 0.1.5-rc.1 / 0.1.2-rc.1 / 0.1.2-alpha.5 / 0.1.2-alpha.2 |
下次 DSH 升版时:改
compatibility.json一处 → 跑node scripts/compatibility.mjs让门禁把该改的 21 条 peer 全部点名 → 补齐 → 绿。
v0.6.0
dsh-agent-teams-fish v0.6.0
合并上游 v0.1.23 + 完整保留 Fish 自建功能版:把上游 NanmiCoder/dsh-agent-teams v0.1.23(1094b84)的 37 个提交合入本 fork,同时一行未丢地保留 Fish 的厂商美术系统与队长头像跟随路由。
基线由上游 tag v0.1.22(commit 9cba4fe)推进到 v0.1.23;版本号走 minor:上游是大版本功能更新(新增成员工具策略、工作区审计、lineage 身份链、质量门契约绑定等),不是补丁。
一、为什么是 minor 而不是 patch
本 fork 的版本语义是「Fish 自己的发布节奏」,与上游版本号解耦。本次一次带入上游四个新模块(src/tool-policies.ts / src/workspace-audit.ts / src/lineage.ts / src/artifacts.ts),并改动了质量门、快照、工具参数等既有行为,属功能更新,故 0.5.1 → 0.6.0。
二、上游 v0.1.23 带来了什么
| 上游改动 | 内容 | 落点 |
|---|---|---|
| 成员工具策略(#59) | 部署方在配置里按 id 声明可复用的 allow / deny 工具列表,成员通过 tool_policy 选用;四项协调工具始终保留,队长专属工具永不重开。这管的是工具可见性,不是操作系统沙箱 |
src/tool-policies.ts〔新增〕 · src/tools.ts |
| 质量门契约与修订绑定(#161) | review / verification 的判定绑定到产物字节,补齐修复沿革;改动过的文件在被可靠判定覆盖之前阻断交付 | src/quality-gates.ts |
| 修复契约与失效源告警(#174, #175) | 保留修复契约、暴露失效源告警,并为验证失败自动创建修复 / 复验跟进任务,且不会因依赖失败而死锁 | src/quality-gates.ts |
| 越界 findings 升级(#183) | 落在允许修复范围之外的 finding 带原因升级,而不是造出不可能完成的修复任务 | src/quality-gates.ts |
| lineage 身份沿 fork 链(#176) | 队长身份在热 fork 与冷 fork 之间保持,含多跳沿革与成员唤醒;成员侧仍是精确 id 匹配,父链里出现的队长绝不被误读为队长身份 | src/lineage.ts〔新增〕 |
| 会话源元数据与恢复工具(#160) | 会话源元数据改用当前格式写入,并提供显式、非破坏性的历史恢复工具(带源备份) | src/artifacts.ts〔新增〕 |
| 工作区审计 | 新增工作区审计,面板与 status 工具据此得出同一交付结论 | src/workspace-audit.ts〔新增〕 · src/snapshot.ts |
损坏 team.json 的边界降级 |
team.json 损坏时按边界降级处理,不再整体崩掉 |
src/state.ts |
| 卡片 id 匹配回归修复(#203) | 修 Unicode 团队卡片与历史事件折叠,成员花名册改用与持久化状态一致的团队身份 | src/client/agent-teams-card-definition.ts |
| finding 行号校验(#188) | 落库前拒绝非法 finding 行号,可恢复的历史值做归一化 | src/quality-gates.ts |
| 成员模型与 provider 同解(#189) | 成员模型连同其配置的 provider 一起解析,显式 provider 选择被保留 | src/members.ts |
| 迟到的成员失败记录(#205) | 任务结算之后才到达的成员轮次失败,记为持久化的队长备注 | src/tools.ts |
三、Fish 自建功能:一项未丢
合并的取舍原则是两侧并存、不删任何一边。3 处源码冲突的处理:
src/snapshot.tsimport 块:本版captainRouteOf(成员路由取值)与上游canDeclareDelivery/describeQualityLoop/verdictAuditNotes/auditTeamWorkspace(质量审计)并存 —— 命名空间与用途不重叠。src/snapshot.ts函数体:本版「队长 live route 优先于快照」(liveCaptain→captainRouteOf→captainProvider/captainModel,实时值优先)与上游「面板要与 status 工具得出同一交付结论」(audit/deliveryCheck/staleById/verdictStale)并存 —— 两者写的是不同字段、读的是不同数据源,互不排斥。现在面板同时拿到实时队长路由与统一交付结论。src/tools.tsadd_member参数:保留本版详细role文案(十个岗位桶 + 「不要发明新角色名,否则掉兜底图」——这是 Fish 美术 fallback 行为的线上说明),同时保留上游新增的tool_policy参数。
保留项逐条核对:
src/artwork-source.ts—— 自定义美术目录解析与扩展名 → MIME 表,243 行在位src/client/artwork.ts—— 十七厂商识别 / 厂商头像命名空间 / 降级链厂商+岗位 → 厂商 → 岗位/ 商标徽标 / 高清 WebP 族,351 行在位src/members.ts的captainRouteOf与队长头像跟随路由(captainProvider/captainModel)src/index.ts的美术路由与 MIME 按扩展名送达assets/agent-teams/随包素材 286 个文件,零改动scripts/verify.mjs的美术门禁、custom-artwork.test.mjs/artwork-role-match.test.mjs/import-vendor-artwork.mjs- 角色词表含 10 个岗位桶(
audio/video在内) - 身份字段三处同步:
package.json的name/cordis.patch.yml的插件行name/lib/client.js里__ModuleLoader__.load的id,均为dsh-agent-teams-fish
四、改动文件
| 文件 | 改动 |
|---|---|
src/*.ts(17 个) |
合入上游 v0.1.23;snapshot.ts / tools.ts 的 3 处冲突按「两侧并存」解 |
src/tool-policies.ts · src/workspace-audit.ts · src/lineage.ts · src/artifacts.ts |
上游新增模块 |
lib/** |
构建产物,由构建链整体重建(非手工编辑) |
package.json |
版本 0.5.1 → 0.6.0;身份字段一律取本版;保留上游新增脚本 |
README.md · README.en.md |
补 0.6.0 横幅;旧版本横幅全部改为 release-notes 链接形式 |
release-notes/v0.1.23.md |
上游发布说明,直接取用 |
五、验证
- 构建链五步全部 exit=0(
clean-build→tsc服务端 →tsc客户端 →tsdown→git-artifacts --write),且构建后无未暂存改动(幂等,说明lib/产物与src/一致) custom-artwork.test.mjs21 项全绿、artwork-role-match.test.mjs41 项全绿、quality-gates-amend.test.mjs9 项全绿 —— 与合并前基线一致,无回归- 全仓搜
^<<<<<<<为空,无冲突标记残留 compatibility.test.mjs仍为既有 9 pass / 2 fail 基线(source candidates pass the peer gate...、doctor runs through an installed bin symlink...)—— 合并前即如此,非本次引入;该失败源于本机未开开发者模式的 Windows 无法symlinkSync,属环境限制
AgentTeams v0.5.0
dsh-agent-teams-fish v0.5.0
第三轮厂商素材版:厂商命名空间 15 → 17,新增 StepFun(阶跃星辰) 与 Jev(TypeSafe System One)。
两家都只有「厂商通用图 + 立绘高清」这一档素材(没有 Q 版岗位图),因此与既有的 meta / mistral / rwkv / seed / ernie 同档:成员请求 member-<vendor>-<role>-v2.png 时第 1 跳落空、第 2 跳命中岗位通用图,头像照常显示、不破图。
本版改动
一、厂商命名空间 15 → 17
src/artwork-source.ts:ARTWORK_VENDORS追加stepfun、jev。这个数组同时是宿主端的放行白名单(artworkCandidates只服务登记在册的 token)与队长图的可达性判据(leadArtUrl对未登记 token 会回落),漏登记就是「素材在包里但请求永远 404」或「队长头像不跟随路由」。scripts/verify.mjs:三处硬枚举同步 ——ARTWORK_VENDOR_ALT正则、GENERIC_ONLY_VENDORS、以及ARTWORK_VENDORS.length === 15这条断言(改为 17)。两档合起来必须恰好等于ARTWORK_VENDORS,多一个少一个都失败。scripts/import-vendor-artwork.mjs:VENDORS同步,保证以后用pnpm import:artwork导入member-stepfun-*.png一类文件时不被判成「无法识别」。
二、厂商识别规则
| token | 命中 |
|---|---|
stepfun |
stepfun / step-<数字>(含 step_5 / step5)/ 阶跃 |
jev |
jev / typesafe / type-safe |
step-<数字> 那一条不是锦上添花,是必需的。 本机三条 Step 路由的 provider 段是 cn-f/cline/cline-free 这类,没有一个字母是 stepfun,而 model 是 step-5-preview。只按 provider 名匹配会让整队成员掉兜底头像 —— 与 0.4.1 修掉的 hunyuan3 / rwkv7 / seed1.6 是同一类病根:厂商主名后面跟数字时,旧的尾部 \b 边界匹配不上。
jev 同理认 provider 段(en-f/cline/typesafe)与模型名(jev-router)两处,任一侧命中即可;type-safe 带连字符的写法一并收进,避免换写法就漏。
两者都保留词边界,且刻意不写成裸 \bstep[\w.-]*:否则 stepper / steps / stepstone 会被吞成阶跃。step 后面必须跟分隔符或数字。单测钉住了这两组负例。
三、素材(+6 文件,280 → 286)
| 文件 | 规格 |
|---|---|
member-stepfun-v2.png · member-jev-v2.png |
256×256 8-bit RGBA |
member-stepfun-full-v2.webp · member-jev-full-v2.webp |
1086×1968 / 1109×2016,alpha WebP q92 |
brand-stepfun.svg · brand-jev.svg |
24×24 单色内联 path |
制作口径与既有素材一致:四边连通洪水填充(阈值 238)+ 边缘收缩 1px 去背 → 裁到实心包围盒;256 头像是立绘的等比缩小版(高度 254px、水平居中),实测既有素材的墨迹高在 246–255 之间,落点一致。
四、⚠️ brand-jev.svg 是占位字标,不是官方商标
TypeSafe / Jev 目前没有公开商标 —— simple-icons 与 LobeHub icons 都查不到(jev / typesafe / jev-router 一律 404)。而仓库门禁 every vendor ships its own artwork tier 强制每个厂商都必须有 brand-<vendor>.svg,缺了直接红。
所以本版按既有归一化口径(墨迹框最大边缩到 0.84×24、居中)自制了一个简洁的「J」字标并注入品牌色。它不是官方商标,拿到官方素材后直接替换同名文件即可,命名与尺寸口径都不用改。
brand-stepfun.svg 没有这个问题:取自 LobeHub icons 的官方单色 path,同样过了 svg 自洽性门禁(无外链、无脚本)。
验证
pnpm typecheck通过;pnpm build后artwork-role-match.test.mjs新增 11 条厂商用例(含 6 条 stepfun / 4 条 jev / 1 条队长图跟随)与 2 组防误伤负例,custom-artwork.test.mjs的命名空间断言同步到 17;pnpm verify的素材门禁逐张断言 271 张厂商素材的尺寸、格式、编码与 alpha,并钉住「厂商 + 岗位第一跳」与「队长图跟随路由」两条可达性;- 素材目录 286 个文件 / 约 29.3 MB。
AgentTeams v0.4.3
dsh-agent-teams-fish v0.4.3
纯文档修正版:功能与 0.4.2 完全一致,只把一处自相矛盾的 README 文案带上 npm。
为什么要单独发一版
0.4.2 修掉了「队长头像永远是 DeepSeek 那张」,README 里也把主说明改成了「队长跟随自己的模型路由」。但同一份 README 的「已知限制与踩过的坑」表里漏改了一行:
| **队长不跟随模型路由** | 面板快照里只有成员的 provider/model,没有队长的……
所以队长用文件覆盖,想自动跟随需给快照加字段 |
于是同一份文档里,「队长跟随路由」与「队长不跟随路由」并存 —— 而且那句「想自动跟随需给快照加字段」描述的正是 0.4.2 已经做完的事。
代码没问题(快照字段、宿主写入、客户端识别都已在 0.4.2 上线并验证),问题在于 npm 包里的 README 在 npm publish 那一刻就被冻结,事后无法修改 —— 这是本仓库 scripts/readme-version.mjs 开头写明的约束,也是它存在的理由。仓库里的 README 已经修正(提交 f7e1aa9),但要让 npm 页面显示正确文案,只能再发一版。
本版改动
README.md/README.en.md:把「已知限制」表里的「队长不跟随模型路由」改为「(0.4.2 已修)」,并说明修复方式(TeamState新增captainProvider/captainModel、快照实时值优先、老团队记录回落随包图)。- 两份 README 的「本版新增了什么」表补一行,说明本次是文档修正。
package.json0.4.2 → 0.4.3;README 的dsh-agent-teams-fish依赖示例同步到^0.4.3。
兼容
与 0.4.2 二进制等价 —— lib/ 产物逐字节相同,没有一行运行时代码变化。升级到 0.4.3 与停留在 0.4.2 的行为没有区别;升上来只是为了让 npm 页面上的文档不再自相矛盾。
验证
node scripts/readme-version.mjs # README 版本引用与 package.json 一致
node scripts/git-artifacts.mjs # 构建产物戳与源码一致(本版未改源码)升级
pnpm update dsh-agent-teams-fish装完重启 DSH。若撞上 pnpm 的供应链静置期(新发布 24h 内),升法见 v0.3.1 里的说明 —— 重建 lock 后 pnpm install --config.minimumReleaseAge=0。
AgentTeams v0.4.2
dsh-agent-teams-fish v0.4.2
队长头像跟随路由修复版:队长换成 qwen / claude / glm 等厂商后,面板里的队长头像始终是 DeepSeek 那张鲸鱼。查下来不是「少画了一张图」——10 个厂商的队长图早就在包里,是客户端把队长图写死成了一个常量,从来不看队长实际跑在哪个模型上。
病根:队长图是硬编码常量,不是识别结果
成员头像是函数,按成员的 provider + model 推断厂商:
resolveMemberArtwork({ name, role, vendor: vendorSlug(member) })队长头像却是常量,三处渲染点(面板、会话卡片、标题栏入口)都直接引用它:
export const LEAD_ART = `${ART_BASE}team-lead-deepseek-v2.png`于是 team-lead-qwen-v2.png、team-lead-claude-v2.png、team-lead-glm-v2.png 等随包的另外 9 张队长图永远不可达——素材齐全、降级链也通,只是没人去请求它们。
更深的根因在宿主端根本没有队长的模型路由。TeamState 只记 captainSessionId(队长是拥有团队的会话本身,不是成员),所以客户端即使想算也无从算起:
// 修复前的 team.json
{ "captainSessionId": "session-…", "members": [{ "name": "Alice", "provider": "bailian-he", "model": "qwen3.7-plus" }] }
// ^ 成员有路由 ^ 队长只有会话 id这也是它长期隐形的原因:verify.mjs 里 artworkReachesPackage(LEAD_ART) 一直是绿的——那张 deepseek 图确实在包里、确实可达。门禁钉的是「文件存在」,而不是「队长跟随路由」。
本版改动
TeamState新增captainProvider/captainModel(src/types.ts):建队时写入队长的provider/model,isTeamState同步校验。两者可选,老团队记录照常读取,只是继续用随包兜底。captainRouteOf()(src/members.ts):从session.requestHeader()?.config读当前请求路由,未发出请求时退回options;空值整段省略,不写进无法使用的空路由。tools.ts(建队)与snapshot.ts(快照)共用。- 快照暴露队长路由(
src/snapshot.ts):实时值优先于持久化值 —— 团队跑到一半手动切换会话模型,队长头像会跟着换;会话已冷/不在线时仍由建队时记录的值作答。 leadArtUrl()/leadFullArtUrl()(src/client/artwork.ts):取代三处硬编码常量,走与成员同一套vendorSlug()识别。已知厂商集合直接从VENDOR_ART派生(不重抄一遍),未知 token 一律回落——否则会请求一个artworkCandidates拒绝的 slug,面板当场破图。- 三处渲染点接入:
ActivityPanel.tsx(头像 + 点击放大)、AgentTeamsCard.tsx(会话卡片)、TeamChatEntry.tsx(标题栏入口),historicCardTeam投影同步带上路由。 LEAD_ART/LEAD_FULL_ART保留,作为厂商未知时的兜底与既有 URL 契约,不再是唯一入口。
回归测试
scripts/artwork-role-match.test.mjs:新增 4 条用例(跟随厂商、高清图跟随、厂商未知回落、未知 token 不得拼进 URL),30 → 34 条。scripts/verify.mjs:新增 2 条门禁 —— 「队长图第一跳必须是自己的厂商」与「未知厂商回落」。已验证有效性:把leadArtUrl退回硬编码常量后,门禁立刻报FAIL the captain artwork follows the captain vendor on the first hop。
验证
node scripts/artwork-role-match.test.mjs # 34 pass / 0 fail
node scripts/custom-artwork.test.mjs # 20 pass / 0 fail
node scripts/verify.mjs # all checks passed
node scripts/git-artifacts.mjs # 戳与源码一致
pnpm typecheck # 宿主与客户端均通过兼容
- 老团队记录(无
captainProvider)不受影响:读取照旧,队长图回落到随包素材,与修复前表现一致。 - 素材、降级链、宿主路由零改动;随包文件名未变,不需要换素材。
- 只影响展示:队长的实际模型路由、成员派生逻辑、调度与质量门均未触碰。
已知边界
- 队长图取的是会话当前路由。团队跑到一半切换模型,头像会跟着变——这是刻意的(与成员一致),不是缓存残留。
- 厂商识别仍是
provider + model两段拼接后匹配VENDOR_ART。若队长路由两段都没命中已知别名,回落随包队长图,与成员侧的降级行为一致。
升级
pnpm update dsh-agent-teams-fish装完重启 DSH。若撞上 pnpm 的供应链静置期(新发布 24h 内),升法见 v0.3.1 里的说明 —— 重建 lock 后 pnpm install --config.minimumReleaseAge=0。