Changelog, dev startup, README, AgentHub 0.4.0, model catalog, and landing site (#7)

Branch-length batch covering tooling, the model layer, the Web App and the public
surfaces. Highlights:

- Changelog: a per-release `changelog/<version>/` tree, grouped by the surface each
  change touches, with a root CHANGELOG.md holding one line per release.
- Dev startup: `scripts/dev-prebuild.mjs` serializes the skills+core prebuild behind a
  lock and keeps `pnpm install` current; `pnpm dev` runs server+web together.
- AgentHub 0.3.3 -> 0.4.0: OmniMessage complete payloads carry one opaque `fidelity`
  object in place of item-level `signature`/`phase`, threaded verbatim through Trace,
  replay and resume; malformed classification adapted to the new error types.
- Model layer: a model is always referenced by an explicit `(provider, model_id)` pair.
  The provider is never inferred, guessed or defaulted -- both the catalog inference and
  the unique-match config resolution are gone, and CLI, SDK, server routes and
  run_subagent all require the complete pair. Catalog gains the Qwen Token Plan, Qwen
  Pay-As-You-Go and Fireworks AI gateways, plus an expanded OpenRouter group.
- Web App: catalog preset sync and per-group speed test on the Models page, positional
  slash commands, a markdown renderer, skill-library update reminders, and a vertically
  centred draft page whose upward menus size themselves to the room available.
- Public surfaces: restructured READMEs, the penguin.ooo landing site and blog, refreshed
  benchmark results for both suites, and the demo videos playing on the landing page.

Includes the fixes from a full review of the branch: 23 confirmed findings, among them a
provider-inference bug that could send one vendor's API key to another vendor's endpoint,
and an Escape handler that destroyed the composer's contents unrecoverably.

Verified on the branch head: pnpm test (1127 passing, 7 packages), pnpm typecheck and
pnpm format:check clean, Playwright e2e 14/14.
This commit is contained in:
Yaowei Zheng
2026-07-21 17:43:31 +08:00
committed by GitHub
parent abf0a2f248
commit d4faee3a1e
214 changed files with 9406 additions and 1688 deletions
+85 -2
View File
@@ -80,12 +80,13 @@ export const zh = {
login: "登录",
logout: "登出",
admin: "管理员",
defaultAdminNote: "首次使用请以内置管理员登录:admin / admin123,登录后请尽快修改密码",
defaultAdminNote: "首次使用请以内置管理员登录:admin / penguin-2026,登录后请尽快修改密码",
},
account: {
changePassword: "修改密码",
oldPassword: "当前密码",
oldPasswordHint: "内置管理员的默认初始密码为 penguin-2026",
newPassword: "新密码",
confirmPassword: "确认新密码",
passwordMismatch: "两次输入的新密码不一致",
@@ -262,6 +263,21 @@ export const zh = {
groupEmptyHint: "该分组暂无模型,点「新增模型」添加",
searchPlaceholder: "搜索模型:id / 名称 / 厂商",
noSearchResults: "没有匹配的模型",
syncCatalog: "同步预置",
syncCatalogHint:
"用内置目录更新预置模型:新增缺失条目、以目录字段为准刷新差异;本地新增模型与 API key 保持不变",
syncDone: (added: number, updated: number) => `预置模型已同步:新增 ${added}、更新 ${updated}`,
syncUpToDate: "预置模型已是最新",
homepage: "模型主页",
speedTest: "测速",
speedTestTitle: "分组测速",
speedTestConfirm: (n: number): string =>
`将对该分组的 ${n} 个模型逐个发起一次真实请求,测量首 token 延迟(TTFT)与输出速率(TPS),会消耗少量 API 额度。是否继续?`,
speedTestStart: "开始测速",
speedPending: "测速中…",
speedFailed: "测速失败",
ttftTitle: "首 token 延迟(TTFT)",
tpsTitle: "输出速率(TPS)",
modelCount: (n: number): string => `${n} 个模型`,
modelId: "模型 ID",
modelIdHint: "上游 API 使用的模型 id,如 gpt-5.5",
@@ -351,7 +367,7 @@ export const zh = {
},
vault: {
desc: "本 Agent 专属的环境变量(存于 agent_state/.vault.toml):键值对注入其 shell 命令(exec_command)的子进程环境;键名会告知模型,值不进入模型上下文。子 Agent 使用各自的保险柜,不继承。",
desc: "本 Agent 专属的环境变量(存于 agent_state/.vault.toml):键值对注入其 shell 命令(exec_command)的子进程环境;键名会告知模型,值不进入模型上下文。子 Agent 使用各自的保险柜,不继承。保存后自下一个任务起生效(进行中的任务不受影响)。",
key: "键名",
value: "值",
valueMasked: "值(掩码)",
@@ -429,6 +445,10 @@ export const zh = {
usedByAgents: (n: number): string => (n === 0 ? "未被使用" : `${n} 个 Agent 在用`),
/** Top toast shown on successful install / uninstall. */
installedToast: (skill: string, agent: string): string => `已将 ${skill} 安装到 ${agent}`,
updateOutdated: (n: number): string => `有新版本:更新 ${n} 个 Agent 的安装`,
updateAction: "更新",
updatedToast: (skill: string, n: number): string =>
`已将 ${skill} 更新到最新版(${n} 个 Agent)`,
uninstalledToast: (skill: string, agent: string): string => `已从 ${agent} 卸载 ${skill}`,
},
@@ -443,6 +463,66 @@ export const zh = {
workspaceClear: "改用自动临时目录",
workspaceDirInvalid: "目录不存在或无法访问,已回退",
draftSubtitle: "最擅长 AI 开发任务的自进化 Agent",
/**
* Example task cards on the draft screen: one click auto-submits the canned prompt (game
* card first, RAG card below/after it). These are the FULL working prompts — the README and
* landing page show a condensed one-sentence version of the RAG example for reading, and
* the cards' own desc lines stay short, but what actually gets submitted stays detailed:
* build quality depends on it.
*/
exampleTasks: {
game: {
label: "示例:2D 企鹅雪橇越野小游戏",
desc: "可爱南极企鹅滑雪橇跳石头,难度由易到难的 2D 纯前端小游戏",
prompt:
"做一个可爱的南极企鹅滑雪橇越野 2D 小游戏:按空格键起跳,跃过冰面上迎面而来的石头;" +
"开局要足够简单、上手无压力,滑行速度与障碍密度随时间平滑、循序渐进地上升,避免突然变难," +
"实时计分,撞上石头即结束并可一键重新开始。" +
"2D 横版画面、可爱卡通风,纯前端实现(单个 HTML 文件即可),界面遵循 web-design 技能。" +
"完成后在浏览器里自测一次,确认开局能轻松玩过几秒,并告诉我怎么打开和怎么玩。",
},
lol: {
label: "示例:英雄联盟音乐播放器",
desc: "用 SoundCloud Widget API 播放历届 Worlds 主题曲,单文件即开即用",
prompt: `用 SoundCloud Widget API(见 https://developers.soundcloud.com/docs/api/html5-widget)做一个英雄联盟 Worlds 主题曲播放器,单文件 index.html,file:// 打开即用。
## 技术约束
- 使用 SC.Widget JS API(widget.load / widget.toggle / widget.setVolume / widget.seekTo),引入 https://w.soundcloud.com/player/api.js
- iframe 必须可见(180px 高),visual=true color=f0b90b single_active=true
- 仅包含以下 8 首已确认可播曲目(oEmbed 验证通过),不要添加未经 oEmbed 验证的曲目:
- Warriors (S4) — soundcloud.com/leagueoflegends/warriors
- Worlds Collide (S5) — soundcloud.com/leagueoflegends/worlds-collide
- Legends Never Die (S7) — soundcloud.com/leagueoflegends/legends-never-die
- Phoenix (S9) — soundcloud.com/leagueoflegends/phoenix
- Burn It All Down (S11) — soundcloud.com/leagueoflegends/burn-it-all-down
- GODS (S13) — soundcloud.com/leagueoflegends/gods
- Heavy Is The Crown (S14) — soundcloud.com/linkinpark/heavy-is-the-crown
- Sacrifice (S15) — soundcloud.com/leagueoflegends/sacrifice
## 布局
- 左侧 260px 粘性侧边栏:曲目列表(S4/S5/… 标签 + emoji + 曲名 + 年份),点击高亮金色边框,SC.Widget.load() 切歌 + auto_play
- 右侧主区域:Hero 标题 + 桌面时钟(80px 等宽金色 HH:MM:SS,每秒刷新,冒号闪烁)+ 心情标签
- 播放器卡片:SoundCloud iframe + 自定义控制栏(⏮ ▶/⏸ ⏭ + 曲目信息 + 音量滑块,点击喇叭图标静音切换)
- 心情波动区:15 根金色动画柱,切歌时重新随机生成
- 键盘快捷键:空格播放暂停、← → 切歌、↑ ↓ 调音量
## 设计
Penguin 视觉风格(见 web-design 技能),深色/浅色主题(<html data-theme>),默认深色,localStorage 记忆。响应式:手机端侧边栏变为顶部横向滚动。
完成后在浏览器打开 index.html 自测一次。`,
},
rag: {
label: "示例:构建 Claude Code 文档专家",
desc: "收集 claude-code-docs 仓库,生成可对话、带来源引用的 RAG 知识应用",
prompt:
"收集 https://github.com/ericbuess/claude-code-docs 的文档,构建一个 RAG 知识应用:" +
"克隆仓库并整理语料,建立检索索引;应用化身 Claude Code 配置专家," +
"检索增强回答 Claude Code 相关问题并标注可点击的来源引用——" +
"引用要能展示命中的原文片段,并链接到真实文档;" +
"按 web-design 技能提供美观的 Web 聊天界面,空态展示几个示例问题。" +
"完成后运行应用、自测一个问题验证流式回答,并告诉我访问方式。",
},
},
sessionList: "Session",
defaultSessionTitle: "新对话",
model: "Model",
@@ -466,6 +546,7 @@ export const zh = {
statusRunning: "运行中",
statusCompacting: "压缩中",
pendingApprovals: (n: number) => `${n} 个待审批`,
jumpToLatest: "回到最新消息",
inputPlaceholder: "输入消息,Enter 发送,Shift+Enter 换行,可粘贴图片",
inputPlaceholderShort: "输入消息…",
send: "发送",
@@ -527,6 +608,7 @@ export const zh = {
mentionRemove: "移除 @ 目标",
/** Skill multi-select dropdown (input toolbar): button text, search box, empty state, and no-match hint. */
skillsSelect: "技能",
skillRemove: "移除技能",
skillsSearchPlaceholder: "搜索技能",
skillsNoMatch: "没有匹配的技能",
skillsEmptyHint: "暂无已装技能,去技能库添加",
@@ -574,6 +656,7 @@ export const zh = {
title: "文件",
upload: "上传",
download: "下载",
openInNewTab: "新页面打开",
refresh: "刷新",
root: "根目录",
empty: "空目录",