Skip to content

v2026.9.23 - #775

Merged
jamon-bodhi merged 87 commits into
mainfrom
dev
Sep 23, 2026
Merged

jamon-bodhi merged 87 commits into
mainfrom
dev

Conversation

@stephamie7

Copy link
Copy Markdown
Contributor

No description provided.

chenjie-booker and others added 30 commits September 6, 2026 21:27
- 侧栏:删除「场景工作区」分组,SOC 工作区升为一级分组,设备接入移入智能体工作室;AI 工作台与工作区分组做成手风琴,同一时刻只展开一个,进入对应页面自动切换并持久化
- 二级菜单:AI 工作台、SOC 工作区、智能体工作室都支持拖拽和 Alt+方向键排序,按分组存 localStorage
- 顶部标签栏:打开过的侧栏入口成为标签,可切换、关闭、拖动排序,记住最后停留的子路径;「+」菜单按分组列出未打开页面
- keep-alive:路由子表抽到 routes/contentRoutes.tsx,Layout 为每个打开过的标签保留面板,非当前面板 visibility:hidden + inert,状态与滚动位置保留;刷新后恢复的标签懒挂载
- 工作区宿主页:根路径直接跳默认页,去掉页内页签,修复 t 进依赖导致的重复拉取
- SOC 态势大屏插件:三处 100vh 改为按容器高度,soc_ui 升到 1.1.6
顶栏只保留 Agent、SOC工作区、系统设置三个分区,左侧只显示当前分区的菜单。每个访问过的
菜单项各自保活,切走再切回来页面还是原样(滚动位置、未提交的输入都在),分区之间也各自
记住上次停留的页面。

- 系统设置改为在布局内渲染,不再是带「返回」的独立整屏页
- 场景工作区的页面平铺成同一级菜单,不做分组
- 场景套件的安装、停用、卸载统一放到场景工作区,插件广场不再列出场景套件
- Hub 插件增加 edition 字段:Pro 专属套件(代码审计、AI 红队)仍然列出但拒绝安装,
  直接访问它们的路由会说明需要 Flocks Pro
- 场景工作区可以停用而不卸载套件,停用后旧页面链接给出提示
- 安装/更新套件时会带上版本落后的子件,避免套件与它的页面版本脱节

Claude-Session: https://claude.ai/code/session_019toMtTXUWkgQBBRfUCy2PJ
长 write / edit 的参数流被上游切断后,累加器会把残缺 JSON 补齐再执行,结果是文件只写了一半却报成功。
三处根因一起修:

- 累加器不再用补齐后的 JSON 兜底执行。JSON 解析失败时先判断是不是「补上未闭合的引号和括号就能解析」,
  是就走截断路径:发 ToolInputErrorEvent、标记失败、抛 StreamToolArgumentsTruncatedError,不执行工具。
  真正格式错误的参数仍走原来的 invalid-tool 路径
- Anthropic 适配层保留 stop_reason:原来 message_stop 一律上报 "stop",max_tokens 截断和正常结束
  在下游无法区分。现在 max_tokens 映射为 length、tool_use 映射为 tool_calls
- openai_base 改为参数片段边到边转发:原来把整段参数 JSON 攒到 finish_reason 才发,
  长调用期间适配层整段静默,把 runner 的流中途 chunk 看门狗饿死了。末尾那个 chunk 只带还没转发的尾巴,
  避免累加器重复追加

回归测试都验过有效性:三个源文件各自还原到修改前,对应用例都会红。

Claude-Session: https://claude.ai/code/session_019toMtTXUWkgQBBRfUCy2PJ
装了不止一个场景工作区时,侧栏底部的「自定义页面」「自定义标题」两个按钮会消失。

根因是 `LayoutNavSection.workspace` 一个字段同时承担了两件事:决定拖动排序存到哪个工作区的 key 下,
以及决定工作区动作挂在谁身上。多场景共用一条平铺菜单时前者必须为空——否则会把别的场景的页面顺序
写进 SOC 的排序 key——结果把后者一起关掉了。

拆成两个字段:`workspace` 继续只管排序归属,新增 `actionsWorkspace` 固定指向 SOC 工作区,
有几个场景都不受影响。

菜单本身维持一级平铺、分区名仍为 SOC工作区,这次不改。

新增回归测试 keeps every scene in one flat menu and keeps the SOC actions when several are
installed,已验有效性:把 `actionsWorkspace` 改回旧写法后该用例报找不到 workspace.customPage 按钮。

Claude-Session: https://claude.ai/code/session_019toMtTXUWkgQBBRfUCy2PJ
装了多个场景工作区(SOC / 代码审计 / AI 红队)时,它们的页面原来全部并进同一条侧栏菜单,
既没有切换入口也分不清某一页属于哪个场景。

顶栏右侧加场景切换器,侧栏仍是一级平铺、不做分组,但只显示当前场景的页面;分区名保持
「SOC工作区」不变。当前场景由路由推导,路由里没有时取上次选过的、再没有就取第一个;
只装了一个场景时切换器不显示。工作区动作(自定义页面 / 自定义标题)只在 SOC 是当前场景时出现,
不跨场景跟随。

顺带修两处:

- 顶栏改成单一横向滚动容器。原来三个 tablist 各自 overflow-x-auto 各自裁剪,400px 宽下
  遮挡检测报出 3 个控件点不到(「SOC工作区」「系统设置」中心命中顶栏容器本身、「代码审计」
  中心命中「场景套件」图标),复扫后降为 0
- 删掉账号弹窗里的「设置」入口。系统设置已经是顶栏分区,这是条重复路径

新增三条回归测试,都验过有效性:场景切换与隔离、单场景时不显示切换器、多场景时保留 SOC 动作。

Claude-Session: https://claude.ai/code/session_019toMtTXUWkgQBBRfUCy2PJ
远端 029bd9a 是本地 ad34665 的组成部分——我接手时那批改动正躺在暂存区,
它被单独提交并推送,我则把它连同后续的三分区改造一起提交,于是两边分叉。

已核实内容包含关系:029bd9aa 新增的 7 个文件在本地全部存在,其中
KeepAlivePanes.tsx / OpenTabsBar.tsx / layoutTabs.ts / useLayoutOpenTabs.ts
与远端版本逐字节相同;把冲突全部取本地后的树与本地 HEAD 完全一致,
即合并不引入也不丢失任何内容。故用 -s ours 记录合并,远端提交作为父提交保留。
对着 xlsx 逐条核对 feat/202609 的代码,查出两处报告称已完成、实际没进代码的缺口。

TC-02 要求「5个入口完整保留在对应分组下」,代码里 AI 工作台组只有工作台、文件目录、
任务中心、工作流 4 项,首页是独立的一节。对照 TC-03「7个入口完整保留在智能体工作室
分组下」——那一组代码里确实是 7 项,口径一致,所以本组应为 5 项。现把首页并入该组,
与 D01「首页归 Agent」一致。

规划表「Hub 场景入口」行要求目录、计数、版本、安装进度和工作区注册使用同一状态,
前四项已具备,安装进度缺失:SceneSuites 用的是普通 install 接口,装套件时只有按钮转圈。
改为走流式 installStream,复用 Hub 原有的 SuiteInstallProgressPanel 逐组件显示进度,
失败时落到 failSuiteInstallProgress 而不是只弹一个 toast。

两条回归测试都验过有效性:把源码分别还原后,
reorders AI workbench entries by dragging 与
shows per-component install progress while a suite installs 各自变红。

Claude-Session: https://claude.ai/code/session_019toMtTXUWkgQBBRfUCy2PJ
- 告警态势所在 section 带 themeOverride: dark,宿主靠卸载时的清理撤销临时主题;
  上了 KeepAlive 后页面不再卸载,override 一直挂着,切到哪个页签都是深色。
  新增 PaneActiveContext,KeepAlivePanes 告诉子树自己是否在前台,宿主只在
  自己的 pane 可见时持有 override,切走即由 effect cleanup 释放;inactive 的
  pane 不再主动写 null,避免覆盖当前 pane 刚设的值。
- ThemeContext 加 mode: light | dark | system,默认 system,监听
  prefers-color-scheme 的 change 事件实时跟随。旧实现一挂载就把 light 写进
  localStorage.flocks_theme,从此再也不跟系统;改为只在用户显式选择时落盘,
  换新键 flocks_theme_mode,旧键读时忽略、写时顺手删掉。index.html 的防闪烁
  脚本同步改读新键,判定规则与 ThemeContext 一致。
- 偏好页主题从两项改为三项:跟随系统 / 浅色 / 深色;右列放宽到 20rem 并对
  选项加 whitespace-nowrap,三项一行放下。
- 测试:ThemeContext 6 项按新行为重写(含 OS 实时切换与旧键忽略);宿主新增
  「pane 隐藏时释放 override、重新可见时取回」回归用例;Settings 新增跟随系统
  选项用例。

Claude-Session: https://claude.ai/code/session_013YByVmNxkWwDs1kMpwkeN7
每个已启用的场景工作区各成一个一级分组(标题为场景名),页面是二级;
分组接入现有的 primary 手风琴,同一时间只展开一个场景,进入某场景的路由
自动展开它,展开状态沿用 flocks_layout_expanded_primary_nav_section
(id 为 workspace:<id>)。自定义页面 / 自定义标题两个动作仍只挂在 SOC 分组下。

上一版「一级平铺 + 顶栏场景切换器」按最新要求撤掉:PartitionTopBar 去掉
scenes / activeSceneId / onSelectScene 与对应渲染,Layout 删除 activeSceneId
及 selectScene 派生逻辑,nav.json 去掉只给切换器用的 scenes 键。

Layout 测试改为断言场景标题、aria-expanded 互斥与直开链接自动展开,新增
「直接打开另一场景页面时展开对应分组」「单场景也有一级标题」两条。

Claude-Session: https://claude.ai/code/session_013YByVmNxkWwDs1kMpwkeN7
…n-7993a4bb426b

chore: sync main into dev after main-7993a4bb426b
Bind output resources to their writer scope and restrict attachment persistence.
Protect staged uploads through submission, queueing, retries, and cleanup.
Paginate Context resources and directory reads with safe merging and single-flight refreshes.
Require explicit project bindings, preserve preview navigation, and add boundary regression coverage.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Include the user workspace scope in Context output paths while preserving file locations and access checks.
Cover bound and legacy output sources, global scope, and panel rendering with regression tests.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Align execution, security, and model menus with consistent sizing and viewport-aware positioning. Keep labels and descriptions in single-line columns with full-text hover hints and regression coverage.

Co-Authored-By: Claude Code <noreply@anthropic.com>
fix: require edits array for edit tool
fix(session): unify composer selector menu layout
解决冲突:soc_ui 插件版本两边分别抬到 1.1.6 / 1.1.7,合并后统一为 1.1.8,
index.json、manifest.json、workspace.json 与 tests/hub/test_hub_catalog.py 四处一致。

Layout.test.tsx 的 onboarding 用例把同步 getByRole 改为 await findByRole:合并后
Layout 多了 useTokenPolicyNotice,标题出现那一刻 getStatus 尚未落地,原断言存在竞态。
探针确认 getStatus 在合并树与两个父提交上均恰好调用 2 次,无重挂载,非产品回归。

验证:validate_flockshub 800 插件通过;pytest tests/hub tests/scripts 229 passed;
webui lint / build 通过;vitest 1094 passed,剩余 2 个失败(chunkLoadRecovery、Home)
在两个父提交上同样失败,为历史遗留。
Storage.mutate_many 删 key 后按前缀根调用无参的 Session.invalidate_cache()
/ Message.invalidate_cache(),导致删一个会话(以及 Message.delete、
Message.clear)会清空所有会话的消息缓存、会话列表缓存、id 索引和每会话锁,
并 bump 全局 epoch 打断其它会话正在进行的加载。

- storage: _invalidate_runtime_caches 改为从 key 形状解析出 session_id,
  session:{project}:{sid}、message:{sid}、message_parts:{sid}[:{mid}] 与前缀
  message_parts:{sid}: 只驱逐该会话;None 与根/项目级前缀仍走全局重置
- storage: mutate_many 新增 invalidate_caches 参数,缓存所有者
  (Message._persist_indexed_state / Message.clear)在锁内已自行维护缓存,
  跳过存储层驱逐,避免删单条消息后整会话从 SQLite 重读
- session: Session.invalidate_cache 支持按 session_id 只摘掉一个会话
- message: Message.invalidate_cache 新增 discard_lock,存储层安全网不再丢弃
  调用方可能仍持有的会话锁
- tests: 新增删会话/删消息/清历史不影响其它会话、存储层按 key 定范围、
  所有者可跳过驱逐的回归用例;放宽两处 mock 签名;
  test_clear_tolerates_cache_invalidation_during_full_parts_load 自 792d97c
  起 clear() 已不加载 parts,其断言仅靠全局清空副作用成立,改为断言真实状态
Preserve skill failures and refresh Context on skill state changes. Register user and global outputs using their actual scope, including trusted legacy writes, while retaining session and path checks. Add regression coverage and close the test channel-binding database.

Co-Authored-By: Claude Code <noreply@anthropic.com>
新增 packaging/linux:makeself 自解压单文件包,内含 Python 3.12、Node 22、uv、
按 /opt/flocks 预建的 venv、构建好的 WebUI 和可选的 Pro bundle;安装脚本无交互、
幂等(旧目录改名留存,数据与授权不动),注册 systemd(oneshot + RemainAfterExit,
与 restart_handoff 的重启方式兼容)、firewalld 放行、restorecon、健康检查后打印访问地址。
build-in-docker.sh 一键在 CentOS Stream 9 容器里出 x86_64 / aarch64 包;CI 工作流按
两种架构的原生 runner 矩阵构建并挂到 Release。附 smoke-test.sh(无网络容器)与
verify-install.sh(真机验收)。

updater:
- Pro bundle 认 manifest 的 prebuilt: true,允许不带 flocks/ 源码
- FLOCKS_PRO_BUNDLE_DIR 指向本地 bundle 时直接从盘安装,不下载;console manifest
  仅尽力拉取,版本一致才合并 release 身份
- prebuilt 模式跳过 uv sync 与前端构建,只 uv pip install --no-deps;带依赖 wheel 时
  跑 uv sync --frozen --offline --find-links;prebuilt 换 core 但无依赖 wheel 直接拒绝
- restart_handoff 透传 --prebuilt / --dependency-wheels-dir
- 新增 deploy mode offline:联网就地升级在动文件前拒绝(API/CLI/页面提示),本地
  Pro bundle 安装不受影响
- 心跳与升级申请带可选 channel(FLOCKS_UPDATE_CHANNEL)
- flocks update --pro-bundle [--no-restart]
- service_manager 缺 ps 时按"查不到"处理,不再让 flocks start 崩

以上新逻辑全部由离线包写入的环境变量 / manifest 字段 / 显式参数触发,在线安装的
升级路径不变。测试:tests/updater/test_updater_prebuilt_bundle.py 23 项、
tests/scripts/test_linux_offline_installer.py 28 项、CLI/心跳/申请/API/ps 各补用例,
WebUI UpdateModal 增加离线模式提示与测试。
packaging/linux/README.md 的同名 PDF(5 页,md2report_pdf 生成),方便不开仓库
直接查看一键打包、安装布局和离线升级说明。
jamon-bodhi and others added 28 commits September 22, 2026 10:26
fix(webui): 修复 /clear 后上下文文件残留及 404
Exclude device services from API totals while including disabled API services. Count MCP service identities from native server and catalog inventories instead of tool facets, including zero-tool services.

Synchronize tab totals after service updates and refreshes while preserving tool-level activation statistics and previous counts on failed reloads.

Co-Authored-By: Claude Code <noreply@anthropic.com>
给不能直连外网的客户内网机器 A 用:一条命令 `sudo bash flocks-egress-proxy-2.0.0-linux-x86_64.run`
装好后,A 上所有进程和本机 Docker bridge 容器访问公网自动经客户现成的 HTTP / HTTPS / SOCKS5 代理 B,
内网按 IP 直连;flocks 零改动,B 上什么都不装。

- setup-a.py(Python 3.9 标准库):install / reconfigure / check / status / rollback / uninstall;
  mihomo v1.19.31 透明代理 + nftables 两张表(常驻门禁 + 随引擎启停的分流),DNS_MODE=auto
  自动选 redir-host / fake-ip,CONTAINERS=auto 接管容器,ENGINE_DOWN 默认 direct 兜底
- 小白安装:自解压 .run(解压到 /opt 绕开 noexec 的 /tmp),代理地址可只写 IP:端口,不给就用
  dnf / 环境变量 / wgetrc 里现成的设置,再没有就在终端里问三句;help / 位置参数 / 子命令都认
- 装前真的经代理 CONNECT 一次,失败不改系统;装后自动验收逐项 PASS / FAIL
- tools/make-bundle.sh 出 x86_64 的 .tar.gz + .run 到 dist/(arm64 只给开发机测试,放 dist/dev-arm64/)
- tests:pytest 42 项;tests/e2e-centos9.sh 四容器 160 项(squid 认证 / https 自签 / socks5 夹具、
  容器流量、兜底、重启、rollback、终端提问经 pty_drive.py),x86_64 QEMU 与 arm64 容器均 160/160,
  日志与交付包校验和在 tests/results/
- 文档:客户 README(安装 / 使用 / 排障)、INTERNALS(机制与验证记录)、THIRD_PARTY_NOTICES,均配 PDF
- packaging/README.md 补 egress-proxy 一节
Keep JSON group metadata handling alongside regex uninstall validation. Consolidate onboarding tests while preserving event assertions and role-change coverage.
e2e / pytest 的 *.log 是跑测试时的产物,动辄几百行、每跑一次就变,留在仓库里只会让
diff 变噪音(仓库根 .gitignore 本来就忽略 *.log,之前特意加了反忽略把它们放进来)。

- packaging/egress-proxy/.gitignore:去掉 !tests/results/*.log,改成显式忽略
- 从版本库里移除已提交的 6 份日志(文件还在本机,git rm --cached)
- tests/results/README.txt 改写成「跑了什么、结果如何」的记录:每轮的命令、通过数、
  交付包校验和、以及自己跑时会踩的几个坑;要日志就照命令重跑一遍
- INTERNALS.md 指向改成 README.txt,并说明原始日志不进仓库
CR 指出两处:
- tests/results/*.log 只管一层,子目录里的日志靠仓库根规则兜着,看不出是有意为之;改成 **/*.log
- 记录里写的 uv run pytest 在 packaging/egress-proxy/ 下跑不起来(这个目录没有 pyproject.toml,
  uv 会去用仓库根那个项目);改成仓库根 .venv 的 python -m pytest,并说明命令的工作目录
- 顶部「SOC 工作区」页签只在有启用的场景套件时显示;没有启用场景时,独立自定义界面和场景配置页的侧栏改挂 Agent 菜单;导航从未加载成功前不猜分区、不写分区记忆
- 系统设置下拉改为 createPortal 挂到 body(z-[55]),不再被页面内容压住;指针在齿轮和菜单之间来回不会关闭,Enter/Space 打开时焦点进入菜单
- 「+ 自定义页面」移到侧栏菜单底部并改成虚线样式;删掉侧栏「自定义标题」入口、弹窗和 6 个不再使用的 i18n key
- soc_ui 升到 1.1.10:日志调查筛选按容器宽度自适应列数(插件页不跑 Tailwind,改用内联样式),关键词与查询、重置同行,靠右的下拉面板自动右对齐;告警态势页头新增「页面设置」修改大标题
- 补充 Layout、PartitionTopBar、navPartitions 和告警态势页的测试
…5f4f0

feat(packaging): 新增 egress-proxy 出网接管交付包(CentOS 9 x86_64,客户已有代理)
…ation

Fix process timing during model activity and parallel tools
…eedback-b0382b

fix(webui): 按 9-20 评审反馈调整场景页签、设置菜单和 SOC 页面
fix(webui): restore default model when resetting blank drafts
…e-20260923

feat(notifications): 添加双节福利通知与申请二维码
@jamon-bodhi
jamon-bodhi merged commit dde8faa into main Sep 23, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants