Changelog

Product changelog

Every Simmetric Chat release, from the newest version to the oldest.

Unreleased

Added

  • Widget 所有权模型(2026-10-05 "wown",quick 261005-scn)。Widget 现在有了所有者(创建者)与用户(按 widget 的授权): - widget_users 关联表(增量迁移)+ 按所有者的 users.widgetQuotaLimit 上限;NULL/fail-closed 0 — 不动既有安装即可,行为零变化(Phase-206 的 maxSponsoredUsers 网格习语)。 - Widget 管理操作为非管理员的 OWNER 重新开通:POST /api/widgets 走新的 widget:manage 权限 + 管理员分配的上限(超过 → 409 { error, quota: "widgets" });安装级的许可开关(widget_enabled / max_widgets)保持不变。GET 列表/详情、PUT 设置 + workspace 绑定可达 creator | widget-user | admin(他人 ID 仍为不暴露存在性的 404);DELETE 仍为 creator | admin。 - Widget 区新增管理区块(/widgets?tab=quotas,配额标签页):按用户的行 — user | owned | max-widgets — 支持 assign / clear (null) 写入、分页搜索、写入审计日志(user.widget_quota.updated)。 - Widget 详情页新增"用户"标签页(WidgetDetail,编辑模式下):OWNER 选择 widget 的用户(以整体替换的 PUT 写入、授权目录探测 /widgets/owner-user-search — 一个挑选身份的表面,不含邮箱);获授权用户得到只读分支。 - 测试:30 个 pin 的服务器矩阵(widgetOwnership.test.ts)、前端分支(WidgetUsersTab/WidgetQuotasTab)、e2e 路线(widget-ownership.spec.ts)。

Fixed

  • 共享 workspace 默认给了 editor(编辑者)角色,而不是 viewer(2026-10-05 "viewer-default")。报告的症状 — "获得授权的用户无法在共享 workspace 中新建聊天,也无法在既有聊天里写入" — 经一条新的 4-pin 集成探测(granteeChatWriteAccess.test.ts)追溯到授权的角色,而不是门槛:在 WORKSPACE_ROLE_ENFORCEMENT=true 下,editor 的 WorkspaceAccess 行可通过 GET /chats(200)与 POST /chat(200)(服务端);VIEWER 行可通过读分支,但按设计其写分支被判 403;没有行的被授权者得到不暴露存在性的 404(SC-4)。两个授权表面(所有者的共享对话框 WorkspaceAccessDialog 与 Settings 面板 SettingsWorkspaceAccess)在单选与批量选择器中都预选了 viewer — 管理员不动下拉直接授权就会产生只读授权。UI 的默认还与 schema 层的意图相抵触:Phase 189 D-12 的迁移注释将遗留行的回填定为 editor("NOT the spec's viewer suggestion")。两个表面现在默认 editor(单选 + 批量);角色的接线 pin(一个明确选择 viewer 再验证 viewer 贯穿的测试)保持不动。注:在这次修复之前获得授权的用户保留其持久化角色 — 再次授权(upsert 角色)可重新评估。
  • "总结附件" 不再用那句千篇一律的"没找到结果"话术说谎(附件-先于-ingest 的缺口收口,2026-10-05)。在聊天里附加文件走的是异步 ingest 流水线;消息过一拍去问,文档还没有分块(chunk 为零),因此按路由的 attachedDocumentId 解析(chat.ts 中的两个聊天分支)什么都没有注入:agent 回落去 rag_search,新文档还没进向量库,buildSystemPrompt 的规则 3 就用那句 "Non ho trovato informazioni su questo argomento nei documenti del workspace. Verifica che i file pertinenti siano stati caricati e indicizzati." 兜底 — 这是个假话(workspace 可以是满的,只有刚附上的文档还没就绪)。两个解析位置都改为检出零分块分支并注入一句诚实的提示(buildAttachmentNoticeChunk(new utils/attachmentNotice.ts)):orchestrator 的 non-empty-ragContext 分支剔除 rag_search,模型就按提示作答 — "仍在处理中,稍后重试"(pending/processing)或"处理失败/已取消,请重新上传"— 并且该提示明确禁止那句兜底话术。状态与进度感知(collector 的 0-100 进度只在 > 0 时出现);提示上照常叠加 DLP 的 rag-context 扫描。帮助器上 5 个单元 pin(pending/processing/failed/cancelled 分支 + 反规则 3 指令)。前端延伸 — 附件 chip 的ingest 等待器:chip 每 1.5s 轮询新的 GET /api/documents/:id/attachment-status 端点(元数据的三重 IDOR 检查、{ name, status, progress } 投影、不带 chunk),ingest 中显示转圈 + % 进度,直至 "completed" 前按住发送门槛(canSend + 经 attachmentPending 的回车路径),在 failed/cancelled 时带提示丢弃附件(404 → 静默丢弃;瞬时网络错误继续轮询 — 服务器打嗝不应毁掉一次健康的 ingest)。门槛是客户端的礼貌:对陈旧标签页,服务端提示仍是诚实的后盾。documentIdor.test.ts 里 4 个端点 pin(access-user 投影 + D-04 的管理分支);ChatInputArea 里 2 个 pin(pending 禁用发送 + 进度 chip;completed 重新启用)。
  • Settings → "工作区访问" 的授权行重新显示用户名(Q6-fast 的设置同步,2026-10-05)。工作区访问对话框修过这个一模一样的缺陷(用户名 → id 的回退链),但 Settings 面板保留了 PRE-Q6 的链 user ? displayName(user) : userId — 而 D-15 的线上格式从不发送内嵌的 user 对象(T-189-11),于是面板里每条展开的行都落回裸 UUID。面板现在镜像对话框的链(内嵌 user 的遗留形态 → grant.username → id),其测试套件锁定了真实的线上形态:静态 mock 删掉了从不发送的 user 字段,一条新测试断言用户名被渲染、UUID 从不出现(+1 测试)。
  • 共享工作区的入口:Workspaces 页面的行现在能打开(2026-10-05 "open-row")。一个仅在 workspace 层获得授权的用户(viewer/editor,没有项目访问权)从来无法在聊天界面中进入被共享的 workspace:侧栏选择器是项目优先的 — 项目下拉走 GET /api/projects(只列出创建者拥有的 / ProjectAccess 项目,没有 workspace 授权分支),workspace 下拉在选定项目前 disabled 且按 projectId === selectedProjectId 过滤 — 而 Workspaces 页面确实列出了被共享的 workspace(GET /api/workspaces 的 OR 过滤包含授权分支 accessGrants.some(userId))但其行只是管理用途,没有进入方式。WorkspaceRow 增加了 "Open" 动作(在动作列的第一位,对列出的每一行渲染 — 能被列出即意味着至少有 viewer(按服务器的 OR 过滤);客户端的门槛只是 UX,聊天路由每条请求都会重新设防),它经 ChatContext 挂载该 workspace(setWorkspaceId(id) + navigate("/") — 持久化 lastWorkspaceId,重启恢复时无需选择器即可重回该上下文)。App 中加了个镜像效果,在活动 workspace 于选择器之外变化时保持侧栏下拉一致 — 仅当父项目在用户的列表中;聊天表面只受 currentWorkspaceId 驱动。侧栏选择器得到一个静态的 "Shared with me" 条目(PROJECT 下拉中的哨兵值 __shared__,仅在共享集合非空时渲染):选中项目后 workspace 列表照旧限定于该项目;选哨兵(或启动时选择为空)则 workspace 下拉列出项目不可见集合(按 OR 过滤可达、但父项目不在 GET /api/projects 里的所有 workspace)— 之前不可达的 only-workspace 被授权者的共享列表,现在由这条路打开,因为此前控件要求先有项目,而且 Radix Select 一旦选中具体项目 ID 就无法清空(单向死胡同;这个命名条目是永远可点击的回归之路)。占位文本经 sidebar/navModel.tsx 里共享的 workspaceSelectPlaceholderKey 帮助器按内容驱动(从被撤销共享得来的过期空哨兵会退化为通用措辞,而不是保持"已启用但为空的共享范围";workspaceSelectDisabled 给 AppNavOverlay 的挂载设门 — 内联的 AppSidebarNav 只在宿主条目上渲染)。i18n 键 workspace.open + sidebar.selectSharedWorkspace + sidebar.sharedProjects 加入全部 8 种语言 — 同步绿灯。新 pin:viewer 行显示 Open 且绝不 delete/edit、缺 onOpen 不渲染控件、页面级的随行 pin 用真实 ChatProvider 验证 lastWorkspaceId 的 localStorage 写入(WorkspacesPage)、共享分支从无项目的宿主条目渲染且为空时隐藏(AppSidebarNav)、选中哨兵显示共享占位而过期哨兵退化、覆盖层门控矩阵将启用/禁用按分支加桌面与移动端占位钉死(AppNavOverlay)。
  • 工作区 "Accesso" 对话框(侧栏顶部按钮)不再溢出自己的边框(2026-10-05)。每个全宽块都越过对话框的圆角边框:UI 套件的 DialogContent 是 grid 且没有显式 grid-template-columns,子元素都落在一根隐式的 auto 轨上,而那根轨会长到最大项的 min-content — 一行名字为 36 字符不可拆分 UUID 的用户名的授权行就会占 503–549px,而内容盒只有 488px(sm:max-w-[520px]),轨被撑宽,所有全宽元素(标题、选择器行、勾选列表、授权 <li>)都被拉过边框。双层修复:套件基础层钉死一根零下限的显式列(grid-cols-[minmax(0,1fr)] — 整个 Dialog 家族把内容收缩到有上限的内容盒里,而不是把轨往外挤,全局生效),对话框站位处在插值标题上加 break-words、给授权/批量的行与每个授权 <li> 加 flex-wrap(nowrap 的按钮在 ~360px 视口以下会折到自己那一行)。Playwright 已验证:380→1280px 零溢出(授权名按设计截断出省略号,无越界);新的 WorkspaceAccessDialog.test.tsx 将两层钉死 — 174 个套件 / 1487 个前端测试绿灯(+1 套件,+4 测试)。
  • "Accesso" 的授权行重新显示用户的名字(Q6 的快速跟进,2026-10-05)。D-15 的服务端响应带 {userId, username} 但不带内嵌用户对象(在 T-189-11 时删掉,为停止泄露邮箱/角色),而对话框只从这个已死的可选 user 字段读名字 — 每一行被分配用户的行都落回 UUID。授权行现在依次解析:内嵌 user(旧形态)→ username → id;WorkspaceAccessGrant 增加了线上真正会送的 username 字段。新 pin:渲染用户名 / 从不泄露 id / username 为 null 的旧行保有 id 回退(+1 测试)。
  • Settings 的深层链接已恢复(2026-10-05)。自 R-5 "settings 改成对话框" 的改版以来,整页加载 /settings(书签、侧栏入口、旧的导航项)不再打开设置对话框:首次提交时 auth 查询还在飞行中(user = null),而声明在打开器之后、同一组件里的 close-on-logout 效应会在同一批里再关掉它(先开再关),加载后 pathname 已被规范化,也没东西再打开它。软导航(用户菜单)不受影响。打开器现在只在已 LOADED 的会话上生效,规范化会保留查询串,?tab= 的深层链接得以穿越对话框挂载;?tab= 书签现在也会进入目标区块(与点一下栏内条目同一分支:组页面打开,条目打上 data-active)。e2e/settings-left-menu-nav.spec.ts 重新变绿。
  • 又核实了一处"非缺陷":POST/DELETE /api/system/settings/branding/icon 是 ENTERPRISE 插件的路由(simmetric-enterprise/brandingRoutes,Phase 145,mountProtected + adminLicense("white_label"))— 社区版一律 404,UI 按文档化行为降级为升级提示(D-12 的 grep 守门 whiteLabelExtractionGuard);EE 测试包 37 套件 / 737 测试绿灯,社区守门绿灯。
  • Settings 面板不再把打开它的一般用户登出。设置路由器曾以 requireAdmin(admin:settings 权限)做端到端的门禁,而按角色的可见性网格(SETTINGS_TAB_PERMISSIONS / RoleSectionVisibility)却仍把区块 — 个人资料、外观、LLM 供应商、高级 — 渲染给普通用户。打开面板(或一个区块)会让 SettingsPage 的外壳去请求 GET /api/system/settings,收到 403,页面把 403 混同于 401:显示 "Session expired. Please log in again.",抹掉 token,3 秒后重定向到 /login — 仅因一个权限缺口就毁掉一个完全有效的会话。两段修复(2026-10-04 "settings-panel logout"): - 服务器:GET /api/system/settings 现在按权限给出载荷。管理员(持有 admin:settings)得到的完整控制台载荷与从前字节相同;其他认证用户只收到 NON_ADMIN_SETTINGS_READ_KEYS 子集(共享)— 非管理员可达表面消费的非敏感键(外观标签页 / 应用头部的 BRANDING_*、下载页的 ALLOW_NON_ADMIN_UPLOAD)— 每项 readOnly: true(反对失配规则:绝不要渲染服务器会 403 拒绝的表单)。机密(SMTP_PASS、LLM_API_KEY、JWT_SECRET、DATABASE_URL、VECTOR_DB_API_KEY…)永远不可能到达非管理员的响应。按组织的视图(?organizationId=)与全部写路由(PUT /、POST /mail-test)仍是仅管理员(fail-closed)。 - 前端:只有 401(认证失败)才销毁会话 — 在 SettingsPage 的 3 秒重定向分支与 App.tsx 的 useMe 错误分支。403 现在渲染一条单独的"无权查看这些设置"横幅(settings.errorNoPermission,8 种语言)且会话存活。
  • Collector 的摄入:以类文本扩展名(.txt .md .csv)上传、却带着 PDF 魔数的文件不再按裸 UTF-8 索引(2026-10-01 的 AI-ACT.txt 事件)。被改成 .txt 后按文本来读的 PDF,只把 PDF 的未压缩内脏(信息字典、XMP 元数据、对象字典、结构元素)喂进向量索引 — 3321 个 chunk,无一是规范文本 — 而 FlateDecode 压缩流里的正文降解为 U+FFFD 乱码。parseFile 现在对声明为文本的文件嗅探 %PDF- 头(前 1024 字节,从 ISO 32000 垃圾开始的窗口)并将其路由到 PDF 解析器;chunk 元数据带上 routedAsPdf 戳记,误报的嗅探(文本文件只在开头提到 "%PDF-")落到文本解析器,而不是让 ingest 失败。
v0.28.1
2026-10-02

Added

  • 连接器:既有连接器现在可以从管理界面编辑了(Q1-quick)——早就存在的 PUT /api/connectors/:id 分支终于浮出:卡片的溢出菜单增加 Edit(第一项),在可变表面上打开预填充对话框(名称、知识档案绑定、欢迎/兜底消息、兜底语言、限速 + 日会话上限 — 留空清为 null,0 = 路由的"不限"哨兵)。isEnabled 留在卡片的开关上,Telegram 的 pollMode 留在 webhook 按钮上,平台/botToken/workspace 按架构不可变。PUT 分支还获得审计事件 chat_connector.updated(与 DELETE 分支同格,D-21)。
  • Phase 221 (INSTLIC) — 实例许可证的粘贴槽(免 .env 的 Enterprise 升级):新的管理表面 GET/PUT/DELETE /api/license/instance(requireAdmin + 租户中间件;按路由叠加保持 GET /info 与 /diagnose 不变)以及 Settings → Security → License 面板。粘贴时以 probe-first 验证一张未被认领的 ENTERPRISE 级 RS256 JWT(被拒的密钥绝不持久化:封闭枚举 { error, reason } — invalid / expired / plugin_bound / community_tier),SystemConfig 里只存 AES-256-GCM 密文(键 INSTANCE_LICENSE — SMTP_PASS 的写缝纪律),令 D-07 的 Redis 配置缓存失效,并经与启动步骤相同的解析器应用到运行中的实例(setCachedLicense — 由加载器注入的限制覆盖存活;布尔旗标即刻生效;清空粘贴退回下次启动时的状态,启动语义一致)。优先序、启动与运行时相同:槽 → 已验证的插件行 → 环境变量 LICENSE_KEY → Community。批量 PUT /api/system/settings 拒绝该密钥(chat-保留的专用写路由学说),且设置列表从不出示密文。
  • Phase 219 (SETP-01/02) — 每个设置区块的按角色 可见 + 可修改 旗标:增量的 RoleSectionVisibility.canModify(从 SETTINGS_SECTION_MODIFY_PERMISSIONS 兜底中减取 — 绝不授予,admin:settings 终端)、GET /api/roles/me/menu-sections 里的 settingsModify、区块级别的变更一律服务端门槛返回 403 { error, section }(/api/providers 的变更 → llm;POST /api/agency/users → team)、可见但不可变区块的只读渲染、以及角色可见性编辑器里的修改列(PUT /api/roles/:id/visibility 按条目接受 canModify — 缺省不作改动)。

Fixed

  • 侧栏的删除确认对话框不再溢出(Q5-quick):聊天/文件夹的删除确认是 w-80 的盒子,而 UI 套件的按钮按设计是 whitespace-nowrap shrink-0 — 本地化的长文件夹动作文案("Mantieni N chat…" / "Elimina N chat permanentemente")横向越出了盒子,插值的长文件夹名也溢出标题。盒子改为 w-[22rem] max-w-[calc(100vw_-_2rem)](更宽 + 以视口为限),按钮行 flex-wrap,两条长动作文案覆盖 nowrap(whitespace-normal text-left),插值的标题/文本带 break-words。聊天删除对话框缺的三枚 i18n 键(sidebar.deleteChat / .deleteChatConfirm / .deleteChatButton)以全部 8 种语言交付 — 意大利语界面在那里不再显示英文的内联兜底。 Q5 跟进:第一版上限从未编译 — 任意值里的 calc(100vw-2rem) 是无效 CSS(Tailwind 要求空格用下划线写法 calc(100vw_-_2rem)),该规则从未被静默发出,352px 的盒子在窄视口上不受限。修正后的工具已进入构建产物(在 dist 包中验证 max-width:calc(100vw - 2rem))。
  • 模型解析:无显式 pin 时,聊天回合不再落到硬编码的 qwen2.5:3b 默认值(Q4-quick — 四次聊天事件的根因:新聊天服务的是小的本地模型而不是 workspace 的默认,每回合 34-98 秒)。两处修复:(1) Chat.model 与 WorkspaceAgentConfig.model 的架构默认值由 "qwen2.5:3b" 改为 "" 哨兵(增量迁移 — ALTER COLUMN … SET DEFAULT,既有行不动):falsy 链(model || chat.model || undefined,orchestrator:388/1059)像既有的 "default" 哨兵一样落到 workspace 的 agent-config 链;(2) workspace 级 localStorage 的模型偏好只认显式 — 调色板的选择写入 explicit: true,重播种子时的自动再持久化已消失(链上解析出的默认不是用户的选择,不应变黏),无标记的遗留值被清理一次且永不再作为候选。过期的偏好(如 qwen2.5:3b,注册表里依然有效但缓慢)无法再在每次新聊天里遮蔽 workspace 的默认。
  • Chat:"一条消息开着 N 个聊天" — 客户端只在接受 SSE done 事件时(工具多的回合可晚到 ~90 秒)采纳服务端的 chatId,窗口的重试会无 chatId 地重发,服务器按次新建聊天,而飞行中的回合以孤儿行在后台收尾(Q3-quick)。流式路由现在在回合开始(任何 token 之前)发出一个携带 chatId 的附加 chat 事件;客户端立即采纳(加保护 + 幂等 — 已带 chatId 的发送绝不重采纳)并即刻刷新侧栏。重试落在同一个聊天里。对 pre-Q3 客户端字节一致(未知 SSE 事件被忽略)。
  • 记忆召回:没有存储零条记忆的(用户,workspace)组合现在跳过整个 pre-LLM 召回钩子(Q2-quick)— 一条索引过的 Postgres 计数(@@index([userId, workspaceId]))在任何工作前为钩子设门:不再做查询嵌入、没有 collector 往返,也不再每回合出现 [vector-store] Table "ws_user_memory_…" not found 的警告 — LanceDB 表在首次记忆写入前合理地不存在(vectorStore 的 ensureTable 首次 ingest 时创建;搜索分支 fail-open 到 [])。计数上的数据库异常按钩子的尽力而为契约 fail-open 到设门前的召回 — 决不阻塞聊天。
  • shared:FEATURE_FLAGS 的回归守卫被连接器迁往 EE 时落下了(quick 260930-l4q)— pin 由 12 → 13 覆盖 connectors_enabled。

Fixed

  • 备份/恢复:干跑或恢复以 "Unable to decrypt (no key in chain matched)" 失败(目的地凭据是在上一个 ENCRYPTION_KEY 下写入的)现在返回带引导的错误,指出原因、恢复的抓手(LEGACY_PREVIOUS_ENCRYPTION_KEYS / 上一个 ENCRYPTION_KEY,然后重启)、尝试过的键数,以及 runbook docs/ENCRYPTION_KEY_ROTATION.md — 该文档新增了 Docker 的"备份恢复干跑失败"恢复章节。只披露键链长度,绝无键材料。
  • Phase 220 (SPLIT-01/02) — 代码库拆分的一致性:docs/REPO_BOUNDARIES.md(4 个仓库的模块→仓库审计矩阵,含 7 条标准的分类学说与既定的例外)加上仓库边界门槛:scripts/repo-boundary-check.cjs(以允许清单强制的 grep 门槛 — 核心仓库不得在两条加载 seam 与已文档化的契约点之外引用私有的插件包)+ 自测试 scripts/repo-boundary-check.test.cjs + 接进 build.needs 的 CI 任务 repo-boundary-check
v0.25.0
2026-09-09

Added

  • UI:外壳的完整改版(R-2 → R-5)。聊天的空状态显示优雅的单色字标(故障风青/品红的动画只留给 hacker 主题);侧栏成为极简的 Claude 风格栏(新聊天 + 聊天列表 + 页脚菜单),完整导航住进键盘友好的 cmdk 覆盖层(Cmd/Ctrl+/),保留完全一致的 RBAC 过滤;用户菜单从顶部栏移入一个专用对话框(账号、语言、主题、设置、退出),移动端为左侧抽屉;Settings 以大的主从对话框打开(左侧菜单 + 全区域详情,带栏切换与面包屑),不再是路由页面 — 每一页的主区域现在只有聊天本身。主题(亮色 "Pearl"、暗色、hacker、跟随系统)获得一致性打磨:弱化文字与聊天强调色的 WCAG-AA 对比修正、所有覆盖层的 prefers-reduced-motion 守卫、字标上感知白标的品牌呈现。
  • Docker:compose 栈加入可选的 searXNG 元搜索服务(纯基础设施 — 暂无依赖;固定版本标签、wget 型健康检查、由镜像入口点自动供给设置)。
  • Docker:私有的 @simmetric-chat/saas 插件像 enterprise 插件一样挂载进 compose 服务器(整个兄弟仓库的 bind mount;社区模式下 saasLoader 遇 MODULE_NOT_FOUND 即 no-op)。

Fixed

  • 种子:用户播种时的启动崩溃可能抹掉角色并把管理员锁在门外;种子现在能自愈 — 幂等授予在每次启动时把管理员角色重新应用到幸存的 admin 用户上(记录于 STATE.md 的延期项)。
  • 租户:POST /api/auth/register 现在在一条事务里提交用户创建、默认角色授予与默认组织成员关系,且所有播种/启动路径在其"用户已存在"分支上重复执行幂等的成员关系帮助器 — 启动/请求途中的崩溃不再能把人留在无成员状态(Phase 182 的评审 WR-01);墓碑复活现在应用调用者的 roleInOrg,删除即降权(WR-02)。
  • Enterprise 构建:Settings → Advanced → Backup 区块(以及 /sso 与 /logs 路由)在应用内登录或 SSO 登入后,不再被卡在 "requires Enterprise" 卡片之后。enterprise 模块清单的查询 (GET /api/enterprise/modules) 现在受认证门控并对会话 reactive:以前它只在应用启动时无认证取一次,然后缓存整个会话(retry: false + staleTime: Infinity),于是在登录屏上 401、Enterprise 功能要等到带着已存 token 的完整重载才出现。钩子现在订阅 useMe() 并在会话出现时重取清单(密码登录、SSO ?token= 交接),且完全不在无会话时探测该端点。
  • Docker:生产机密供给侧助手(docker/provision-encryption-key.sh)不再在根 .env 带着未填的 .env.example 模板占位符(<sostituire-con-valore-generato>)作为 API_KEY_HMAC_SECRET/ENCRYPTION_KEY 值部署时把服务器送进崩溃循环 — 占位符现在被识别为"未设置"(大声警告),供给回落到恢复/生成路径。其他任何非空的无效值仍在启动门槛上大声失败。
  • 安全:E2E 助手路由(/api/__tests__,无认证的启停回声 MCP 服务器)在 NODE_ENV=production 下不再挂载 — 生产启动在那里 404,执行该路由文档化的 dev/test-only 契约(生产环境不再有无认证的进程孵化端点)。
  • 安全:档案导入 collector-机密回调检查(PUT /api/archives/import/:jobId/callback)现在以常数时间比较 X-Collector-Secret 头(crypto.timingSafeEqual 带长度守卫),与文档状态回调及 collector 自身机密门槛已有的计时安全纪律对齐 — 封掉服务器上最后一处以明文比较机密。
  • 认证:POST /api/auth/login 现在在路由层用共享的 loginSchema(safeParse)验证请求体 — 无效或空体返回 400 { error, details },与其他认证路由一致,而不是带裸 Zod 消息的 401。凭据正确但不对时仍返回 401 "Invalid credentials"。
  • 认证:当用户不存在时,login 现在对一个固定的弃用哈希执行假的 bcrypt.compare,响应计时不再区分存在的与不存在的用户名(防枚举加固)。两条路径仍返回相同的 401 "Invalid credentials"。
  • 认证:POST /api/auth/admin-reset-password 现在用新的共享架构 adminResetPasswordSchema(userId UUID + 8-128 字符的 newPassword,safeParse → 400 { error, details })验证请求体 — 最后一条认证路由脱离即兴的解构式身体验证。
  • 认证:POST /api/auth/register 的"关闭注册"分支不再内联复制认证逻辑 — 使用静态导入的 verifyToken / isTokenRevoked / isAdmin / getCachedUserWithRoles(没有动态 await import、没有死的头部复查、缓存用户查找),同时保持每个响应形态与"先撤销后查找"的顺序。
  • 安全:widget 的缓存击落端点(POST /api/config/:widgetId/cache-bust)现在以常数时间比较 X-Api-Key 头(crypto.timingSafeEqual 带长度守卫),在预期键未设置时 fail closed,取代之前带 fail-open expected && 形态的明文比较 — widget 的键检查与服务器/collector 机密门槛的计时安全纪律一致。
  • 发布流水线:五张 Docker 镜像现在只产 amd64。被 QEMU 模拟的 linux/arm64 构建挂起达 ~5h(先 widget,后 pgvector 的全合一源码编译)并烧光私有仓库付费分钟数的跑者磁盘;移除 setup-qemu,矩阵每项发布 linux/amd64,构建矩阵改为 max-parallel: 2。arm64 用户可以本地自建(各 Dockerfile 对交叉构建是安全的)或接一台自托管 arm64 跑者 — 见 docs/DEPLOYMENT.md 的 arm64 注记。

Changed

  • 工具链:CI 里新增死代码门槛(knip),在允许清单之外发现未使用的文件、依赖或导出即失败;全仓库扫除约 3400 行死代码,widget API 密钥的生成移入标准管理流程之后,两个 i18n 脚本把"定义但从未使用的键"作为非阻塞的清理提示报告。
  • Widget:按 widget 的响应模型 pin — 每个 widget 的管理配置现在可选定服务该 widget 聊天回复的 LLM 供应商 + 模型(responseProviderId/responseModel,增量迁移 add_widget_response_model)。pin 在每次聊天请求时由服务端从 widget 的数据库行解析(绝不由客户端提供 — widget 代理与访客 UI 不受影响),优先于 workspace 默认,未设置的 widget 保留既有的 workspace/全局解析链。widget 聊天现在记录真正服务它们的模型。
v0.22.0
2026-08-30

Added

  • RAG:rag_search 的元数据过滤 — agent 现在可按文档类型(documentTypes:pdf/md/txt/csv/docx/xlsx)与摄入日期(dateFrom/dateTo,ISO;只带日期的上界包含整日)限制检索范围,修复"用 2023 年的文档回答 2025 年的问题"的失败模式。过滤器穿过混合检索进入两条腿:向量腿以真实预过滤器到达 collector(打在已标记的摄入元数据上:pgvector 的 JSONB 谓词 + Qdrant 负载的 must 子句;LanceDB 与 Chroma 记录警告并忽略新键;FTS 腿在文档表上获得参数化的 SQL 谓词)。服务端在检索后以权威的文档表做后备校验,保证每个供应商上的过滤正确性。从现在起摄入的文档总是可过滤的;遗留向量(未标记的)在过滤器激活时被排除,并可通过管理员的 re-embed 恢复可过滤性 — 无架构变更、不重嵌入既有文档、无过滤器时行为不变。
  • DLP:为常见欧洲(尤其意大利)标识符新增四个内置模式,由一条纯数据迁移幂等播种并镜像到掉库兜底集合 — it_vat_iva(Partita IVA;锚定标签,纯 11 位数字永不命中)、it_codice_fiscale(16 字符的税号;仅大写,"foschi" 一类散文词永不命中)、iban(国家前缀 + 长度 ≥ 15;紧凑与空格分组两种形态,线性时间/ReDoS 安全)、eu_phone(意/欧电话;播种为禁用以待管理员复核,因为任何结构化电话正则都会误中序号/参考数字串)。内置共 10 条;既有部署在 migrate deploy 时得到新行,并有 ON CONFLICT DO NOTHING 的重放安全。
  • 聊天:管理员现在可一次性对整段对话揭示或重新隐藏 DLP 打码的匹配文本 — 聊天输入的"更多操作"(+)菜单新增 "显示 DLP 文本"/"隐藏 DLP 文本"(Eye/EyeOff 图标,仅管理员可见,因为只有他们能展开 DLP 通知)。偏好按会话保存、默认隐藏、适用于每条通知(含新到消息);各通知上的独立眼形按钮仍可在两次全局切换之间做单条覆盖。
  • DLP 模式配置:六个 DLP 模式现在由数据库承载(dlp_patterns 表,迁移幂等播种,既有部署自动获得内置项),不再硬编码。管理员在 Settings → Advanced → DLP Patterns 管理:按行启停任意模式、重命名内置项(其正则冻结 — 修改尝试返回 400)、添加至多 50 条自定义模式(snake_case 名称、以编译校验的 regex 源、标志、替换),带实时测试视图显示匹配段与打码后的文本。能加载库里集合的扫描 — 过滤插件的进出口与响应末尾的最后一次清洗 — 经由 5 分钟 TTL 缓存与逐模式的编译正则缓存使用配置模式,库不可达时回落到内置集合;逐 token 的流式渐进清洗保留内置模式(v1 的已文档化限制)。整个面板在 8 种语言里本地化。
  • DLP 角色旁路:新的系统设置 DLP_BYPASS_ROLES(角色名 JSON 数组,默认 [])让管理员把选定角色豁免于全部 DLP 扫描与脱敏 — 过滤插件的进出与流式渐进清洗核心(包括 RAG 上下文扫描与思考内容的脱敏,JWT 聊天与 widget 服务账号一视同仁)全都遵守同一道门。每次被旁路的运行触发一个 fire-and-forget 的审计事件 dlp.bypassed,记录谁(相交的角色名)与来源表面 — 绝不记录被扫描的内容。管理 UI:Settings → Advanced → DLP 里的角色多选带曝光警告,DLP 匹配历史面板里加"Bypassed"动作标签,8 种语言本地化。
  • 审计中的 DLP 来源标注与过滤:每条 dlp.* 事件日志的元数据带 source: "chat" | "widget" — 共享的 SSE 流核心从 widget 的 X-Widget-Id 头导出标记,widget 对话与聊天一样被打码,且现在能在 DLP 匹配历史面板中分辨。面板获得按来源(全部/聊天/Widget)、聊天 ID 与用户的客户端过滤。
  • 上传草稿收割者的管理界面:Settings → Maintenance 里新的一节经 PUT /api/system/settings 暴露启用开关(upload_draft_reaper_enabled)与节奏 cron 表达式(upload_draft_reaper_cron),带客户端的五段 cron 形状预检(服务器对无效值保持警告并回退)、8 种语言本地化的成功/失败提示与描述。.env.example 将收割者键作为环境变量覆盖记录(upload_draft_reaper_enabled、upload_draft_reaper_cron、upload_draft_retention_days)。
  • 配置:环境配置一次定义于共享的 Zod 架构(@simmetric-chat/shared),有效优先序文档化为DB > ENV > 默认 并以测试固定(设置界面改动立即生效;8 个用例的矩阵),会被环境变量覆盖而被忽略的键在设置界面标出 envOverridden 徽标。
  • 配置:仓库根的 .env 现在是服务器、collector 与 widget 的唯一运行时配置 — 一个零依赖的新加载器(loadRootEnv())把它在每包 .env 覆盖之下合并(遗留),启动一次的弃用警告列出受影响的键名(无值);Docker 与 Tauri 对齐(compose 的 env_file 指向根文件,镜像不烤秘密)。
  • 配置:三个包的 .env.example 自各自 Zod 架构重新生成(83/15/6 键、分组、默认同步),并带"未来架构键未入文档则以失败"的 Jest 陷阱;保留在架构之外的裸 process.env 读取(HF/openai 怪癖、加密/HMAC 键、LOG_LEVEL、测试门)由 44 条行为守卫探针钉住。
  • UX:删除 workspace 现在走标准确认对话框(原来是内联的小确认);workspaces 支持多选批量删除,带确认对话框与计入跳过行的结果提示(无权限的行被跳过,从来不是哑失败);OCR 预览并排时把提取文本绑定到与图像面板相同的页切片上;synthesis 运行详情在尚不可批准的运行上显示批准按钮禁用并给出明确原因。
  • UX:workspace 的选择复选框与批量删除在 8 种语言中本地化。

Changed

  • 上传草稿收割者完全由系统设置配置:开/关切换(upload_draft_reaper_enabled,默认 "true")与节奏覆盖(upload_draft_reaper_cron,默认每日 03:00 UTC),都经 PUT /api/system/settings 管理。无效的 cron 值记录警告并回落到默认节奏,而不是让启动失败;默认行为不变。

Fixed

  • 修复了 Docker 生产启动的崩溃循环,服务端启动死于 api_keys.prefix 的唯一约束失败:widget API 密钥播种器现在从 HMAC 摘要推导展示前缀并容忍前缀冲突(以键哈希复查,改为警告,不再崩溃),密钥生成遇前缀冲突时换新键重试(最多 3 次)。
  • 聊天引用现在反映回答真正奠基之所,而非检索工具拿到的全部:引用去重后,基础过滤器把每份文档的引用按相关性截到前 2,并丢掉与最终答案没有实质词汇重叠的片段(chunk)引用(保守的门槛 — wiki/档案页、网页/记忆结果、无 chunk 文本的引用与很短的回答总是保留)。"没有更多信息"的回答不再以 ~1.5% 的相关性显示单份文档的 "Fonti (5)";"无支持证据"的声明同样在过滤集上判定(流式与非流式两路)。仅涉引用流水线 — 不改界面、i18n 或架构。
  • Docker:ollama 服务的健康检查以 ollama ls 探测取代 curl(ollama/ollama 镜像不带 curl/wget/node — 容器一直报 "unhealthy");qdrant 的健康检查以 bash /dev/tcp 套接字检查取代 wget 探测(镜像不带 wget/busybox)— 两个服务现在都报 healthy。单容器的 docker/entrypoint.sh 现在 process provision-encryption-key.sh(与拆分镜像一致):生产独立启动自动供给 ENCRYPTION_KEY + API_KEY_HMAC_SECRET,而不是在门槛上失败。五个 Dockerfile 在 hadolint 下全部干净(含已文档化的非固定依赖策略 + 占位 ENV 豁免)。
  • 当匹配内容在很早的流片段中到达时,DLP 审计时间线事件不再显示"无匹配细节":流式 dlp.output_match 事件的匹配类型与匹配文本清单现在从整次运行累计的匹配推导(所有渐进清洗加最终的尾部扫描),不再只看最后一个片段 — 历史行可能存了空的 matchTypes 数组而行上仍显示匹配类型徽标。匹配文本按 DLP 过滤插件的先例存储(dlp.input_match / 非流式 dlp.output_match 本就携带);审计面板仅管理员且受 Enterprise 限。面板的展开视图现在为记录了匹配类型而无匹配的事件以"仅类型"提示渲染检测到的类型清单(8 种语言本地化)— "无细节"的兜底只留给真正空的元数据。
  • 上传的文件保持可重试:collector 终态回调不再删除 storage/uploads/drafts/ 里已暂存的草稿文件(任何兄弟文档的回调都可能抹掉每次重试所依赖的文件),/retry//assign 在持久 OCR 副本(storage/ocr-sources/)存在时经之自动恢复,不再以"磁盘上已不存在"失败。
  • 删除失败且过期的上传草稿不再返回被阻挡(409)— DELETE /api/uploads/:id 将终态(completed 或 failed)的档腿视为可删除,只拒绝 genuinely 飞行中(启用非终止)的档腿;前端批量删除使用与筛选 chip 相同的在飞谓词,界面上可删的即服务器接受的。
  • 对暂存草稿文件消失的上传,RAG/KB 重试现在返回清晰的"请重新上传"错误(400),不再给假的成功提示;失败的 collector 档腿不再删除 storage/uploads/drafts/ 的暂存文件(未来重试仍可能 — 24h 收割者与 DELETE 路由仍是仅有的草稿文件删除者);重试提示反映每档腿的结局:HTTP 200 上被拒的档腿给出本地化的错误提示,批量重试把计入失败。
  • 生产 Docker 部署自动供给持久的 ENCRYPTION_KEY(服务器存储卷 /app/storage/.encryption-key),不再因硬默认而崩溃循环。服务器入口在任何 Prisma 步骤之前一次性生成键,每次重启/重建都恢复,偏好运营者提供值,并拒绝损坏的持久键。.env.example 与 docs/DEPLOYMENT.md 更新为"生产必填",见 docs/ENCRYPTION_KEY_ROTATION.md 的 Docker 注记。
  • 当模型引擎在完整转写后不带 done 标记关闭 Ollama 视觉流(glm-ocr 自定义引擎中出现)时,KB 的 OCR 页面不再失败:ocrPage 现在把积累的非空输出保存为页结果并带 truncated 标志与警告日志,而不是把真实转写丢在 "Did not receive done or success response in stream." 之后。无 done 且无内容的流仍会让页失败。
  • OCR 任务摘要对页面健康状况如实:以 [FAILED: 标记收尾的页会被计数并以 failedPages 存入任务结果 JSON,完成日志发出 [ocr] 任务完成,存在失败页 警告 — 带 FAILED 页的 COMPLETED 任务不再与干净的成品难以区分(仅 0 token + 底线质量分本有歧义)。
  • 对 Enterprise 部署记录并修复了一个失败模式:当 @simmetric-chat/shared 添加新源文件时,过期的 enterprise file: 依赖快照可在启动时以 "Cannot find module './env.schema'" 弄崩服务器 — pnpm 将 file: 包取进 node_modules/.pnpm 的快照在最后一次 pnpm install 之后创建的文件不会进入快照(硬链接只更新已有文件),快照的 schemas/index.js 因此 require 一个 enterprise 侧不存在的模块,加载器 fail-loud 的策略把插件注册失败变成启动崩溃循环。修复以文档形式交付:enterprise 仓库的重建 runbook 现在记录"重装-而非-重建"的差别(用 pnpm install 刷新快照,再 pnpm build),docs/ENTERPRISE_PLUGIN.md 的 air-gap 安装 runbook 增补对应的排错小节(症状、原因、修复、健康检查),没有私有仓库的运维者也能找到解方。
v0.21.0
2026-08-28

债务扫除 & 发布卫生(曾为: v1.5.0)

Added

  • 为双重发行 (community + enterprise) 进行许可模型分析。决策: AGPL-3.0 (社区) + 专有商业 (Enterprise)。
  • LICENSE 由 Apache-2.0 改为 GNU AGPL-3.0(社区仓库)。
  • LICENSE_EE.md — enterprise 插件的专有商业许可证(@simmetric-chat/enterprise)。
  • NOTICE — 仓库根部的双许可说明。
  • CLA.md — 贡献者许可协议 (v1.0) — 版权+专利授权,双重许可。所有外部贡献必填。
  • AGPL-3.0 的版权页眉添加于 5 个包中的所有源文件 (.ts/.tsx)。以 scripts/add-headers.cjs 应用(幂等)。
  • docs/ENTERPRISE_LICENSE_TERMS.md — enterprise 客户的运营商面对的商业/SLA 条款。
  • .github/workflows/release.yml — 推送标签时生成 GitHub-Release + GHCR 图像发布。
  • CHANGELOG.md(本文件) — Keep-a-Changelog 格式,自里程碑历史播种。
  • .planning/milestones/v1.5-debt-sweep-ROADMAP.md — 债务评估里程碑的范围 (E2E 结转、调试会话、快速任务分类、版本戳同步纪律)。

Changed

  • package.json 的版本提升自 0.17.0 至 1.4.0(与最新的 git tag v1.4 同步)。每个包的版本保持不变,位于 0.22.0/0.23.0(私有包,不发布至任何注册表)。
  • 将 license 字段放入 package.json,将所有 包名/package.json 设置为 AGPL-3.0-or-later。
  • README.md 的许可徽标从 Apache-2.0 更新至 AGPL-3.0;更新许可章节描述双重许可模型;添加带有 CLA 要求的贡献章节。
  • CONTRIBUTING.md 随 CLA 的签名说明而更新。
v0.20.1
2026-08-27

水平缩放 ( Redis 层完成) (曾为: v1.4)

目标: 关闭 4 个 SCALE 的延期项目 (CSW-19 + SCALE-02/03/04) 并进行实际的多实例集成测试证明水平扩展 — 通过负载均衡器实现安全的多实例服务器部署。

已交付: 7 个阶段 (161–167)、13 个计划、29 个任务。所有 22/22 要求。Git 标签:v1.4。

Added

  • SCALE-01 ( CSW-19 ): 剩余 3 个锁包装的调度器 → 然后所有 8 个迁移到 pg-boss cron 作业;setInterval + isRunning + withDistributedLock 从所有 8 个迁移的调度器中移除。
  • SCALE-02: 移除生产中的 scryptSync ENCRYPTION_KEY 回退 — 启动时的 fail-loud (logger.error + process.exit(1)) + 服务的"深度防御"式 throw;dev/测试保存 scrypt;更新了轮换手册。
  • SCALE-03: 检验 API 密钥的 bcrypt 循环 → keyed-HMAC O(1) findUnique({key_hash}) 消化器查找;破坏性 DROP COLUMN hashedKey + ADD COLUMN key_hash 迁移;环境变量 API_KEY_HMAC_SECRET; 运营商迁移手册 docs/API_KEY_MIGRATION.md。
  • SCALE-04: 8 计划器的 setInterval 迁移到 pg-boss(Postgres 支持的工作队列、air-gap 友好);2 个延迟敏感的 10 秒轮询 (OCR + 合成) 保留为 setInterval;带有优雅降级的 pg-boss 单例 (jobQueue.ts)。
  • SSE 多播验证: 关闭跨实例延迟 — 4 个基于模拟的跨实例测试,证明 Redis pub/sub SSE 中继 (A 发布 → B 中继、起点跳过、拆除、降级)。
  • 操作器文档: docs/SCALING.md (377 行) — 多实例部署拓扑、REDIS_URL 要求、pg-boss Postgres 依赖、SCALE-02/03 交叉引用、10 秒轮询、优雅降级。

中断变更 — 需要操作员采取行动

  • 生产环境中必须设置 `ENCRYPTION_KEY` ( base64、32字节),否则服务器拒绝启动。请查看 docs/ENCRYPTION_KEY_ROTATION.md。
  • API 密钥从 bcrypt (apiKeyHash) 切换到 HMAC-SHA256 (key_hash)。操作员必须重新签发 Widget/Collector 的 API 密钥。请查看 docs/API_KEY_MIGRATION.md。

基础设施

  • 新依赖: pg-boss v12.28.0 (服务器)。新的环境变量: API_KEY_HMAC_SECRET。启动顺序改变 — pg-boss 的初始化现在是第 7 个异步初始化步骤,具有关闭拆卸功能。
v0.20.0
2026-08-26

报告扫荡 (曾为: v1.3)

目标: 扫荡 2026-08-26 的 CONCERNS.md 审计 — 直接修复所有简单的胜利点、在重点阶段处理中等困难度的项目,并评估 (延迟或记录) 阻挡/大努力点。

已交付: 7 个阶段 (154–160)、14 个计划。Brownfield 加固里程碑;无面向用户的新功能。

Added

  • 文档 gitignore 修复、chat.ts 文件映射、lint 规则翻动。
  • 代码加固,容易实现的功能 (apiKeyMiddleware 上限 take:10、等等)。
  • 前端 lint 债务 (3 个计划,警告→错误翻转)。
  • ENCRYPTION_KEY 生产环境启动警告 ( CSW-11 ) + 批处理 标题+标签+后续 生成。
  • 模块拆分 ( 提供商解析器提取、能力注册表 )。
  • 测试覆盖 ( chatImportService、chatExportService、apiKeyService,其余 5 个服务; Collector 和 Widget 接入根 jest 配置 )。
  • 评估和记录 ( .env 的许可证 JWT 权限、许可证签名密钥轮换手册、过期的 AGENTS.md 需求修复 )。
v0.19.1
2026-08-25

修订 (曾为: v1.2)

目标: 品牌、AI 免责声明、维基百科图表算法的 TypeScript 重新实现、RAG 搜索修复、MCP 加固、设置向导的初次运行。

已交付: 6 个阶段 (149–153 + 153.1)。

Added

  • BRAND-01/02: SVG S-Monogram favicon、可重复使用的 Monogram 组件、每条助手消息下方的哑音本地化 AI 免责声明(7 种语言)。
  • MCP-01/02/03: MCP 加固 — 每秒 SSE 传输映射、MCP_API_KEY Bearer 身份验证门、list_workspaces IDOR 关闭、getMCPToolsForWorkspace 接入 resolveSkillsForChat。
  • RAG-01/02/03: FTS 本地化修复 (it/ru/de/es/fr/zh 的多语言标记化)、rag_search ↔ wiki_query 回退结果的重复数据删除、rag_search 与 wiki_query 的 LLM 亲和力提示词。
  • WIKI-01: Graphify wiki 算法的 TypeScript 重新实现 (社区检测 + 维基百科标记语言生成、Apache-2.0 清晰的重新实现,~280 LOC Python → TS)。
  • WIZ-01/02: 设置向导首次运行 UI (多步骤: 管理员帐户 + 提供者配置 + 矢量 DB、获取 /api/system/initialize);当向导处于活动状态时,SEED_BOOTSTRAP_ADMIN 禁用。
  • .1: v1.2 技术债务清理 ( React Compiler 警告→错误翻转 )。
v0.19.0
2026-08-19

Enterprise 插件架构 (曾为: v1.1)

目标: 将许可模型从 "公共数据源中的运行时函数标志" 重构到 "独立的私有 enterprise 插件":Enterprise 功能(SSO、审计日志、白标、备份)存在于提供给付费客户的私人包中;社区 repo 不包含 Enterprise 函数代码。商品功能在社区中始终开启。

已交付: 9 个阶段 (140–148)、19 个计划、26 个任务。

Added

  • EPA-01: 共享的 PluginContext/EnterprisePlugin 类型;装载机连接点 loadEnterprisePlugin(app) / shutdownEnterprisePlugin(),带两步 require.resolve→require ( MODULE_NOT_FOUND = 社区无操作、其他错误 = process.exit(1) 失败-响鸣 )。
  • EPA-02: 从 FEATURE_FLAGS 删了 9 个商品标志 ( 20→10 ): web_search、webhooks、push_notifications、memory、lead_export、widget_analytics、auto_title、synthesis_rate_limit。商品路线一直处于启用状态。
  • EPA-03: SSO ( SAML + OIDC + SCIM 2.0 ) 提出至 enterprise 插件。
  • EPA-04: 审计日志 ( 不可变的 event_log 表 + INSERT-仅数据库角色 ) 提取至 enterprise 插件。
  • EPA-05: 白标品牌 ( config 键验证器 IoC 钩子 ) 提取至 enterprise 插件。
  • EPA-06: 备份 (~1400 LOC: backupService、提供者注册表、Bree 调度器、恢复、路由、UI) 提取至 enterprise 插件。
  • EPA-07: overrideFeatureLimit 带反应性撤销的解析器; GET /api/enterprise/modules 显现;4 个 enterprise 面板的 React.lazy 条件加载。
  • EPA-08/09/10: 删除 priority_support; custom_agents = 数字限制 (community 3、enterprise Infinity);licensePayloadSchema 字节相同 ( RS256 )。
  • EPA-12: 文档/环境对齐 ( docs/ENTERPRISE_PLUGIN.md、docker-compose 音量安装、.env.example 分裂 )、air-gap CI 配置 ( grep 上许可证服务的 HTTP 原语的闸门 )。
v0.18.0
2026-08-12

公共发布候选 ( simmetric-chat ) (曾为: v1.0)

目标: 将项目作为干净的公共 repo 准备和发布,重命名为 simmetric-chat、无秘密/个人数据/本地路径,并附带依赖许可证验证、enterprise 密钥生成工具 ( 分离,未发布 )、以及完整的文档。

已交付: 8 个阶段 (132–139)、12 个计划、16 个任务。已发布公共 repo、重命名完成、许可证审计、enterprise Keygen ( HS256 → 在 v1.0 后迁移到 RS256 )、规范文档、CI GitHub 操作、新的 repo。

v0.20
2026-08-11

Widget UX、i18n 与可靠性

已交付: 7 个阶段 (125–131)、32 个计划、75 次任务、16/16 要求。

Added

  • WID-ADMIN: Widget 创建/编辑标签卡子页面 (/widgets/:id + /widgets/new)、即席预览窗格、脏检测、潜客标签页。
  • WID-I18N: JSON 块传送器 ( 消灭 %20 Bug )、访客→Widget-默认→en 回退链、7 个本地 Chrome+ 内容的本地化、本地化线程 Widget→Proxy→Server→Orchestrator。
  • WID-QUEST: 三进制状态建议问题 ( 默认/无/自定 )、7 个语言组、截断修复。
  • WID-CREDITS: 页脚制作者 + AI 徽章、白标门禁删除、验证 real-嵌入弹窗中继。
  • WID-BUGS: 44 像素输入按钮、主机 FAB 往返、SVG-仅图标合同、联系横幅、维基循环、潜客持久化、降级 RAG i18n。
v0.19
2026-08-08

诊断与债务

已交付: 5 个阶段 (120–124)。许可证诊断 ( 启动日志、仅管理员可见的诊断端点、脚本 pnpm license:check )、测试债务 ( ESM 漂移、LoginPage QueryClientProvider、固定形状规格 )、技术债务评估 ( Redis 缩放层尖峰 — 延迟到 v1.4 )。

v0.18
2026-08-04

稳定化

已交付: 4 个阶段 (116–119)。

v0.17
2026-08-02

企业与打磨

已交付: 8 个阶段 (108–115)。

早期里程碑 ( v0.1–v0.16 ) 记录于 .planning/PROJECT.md(里程碑历史章节)。阶段级完整历史位于 .planning/MILESTONES.md。