diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4faa0e49f..7489d9a9a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,8 +12,8 @@
| 工具 | 推荐版本 | 来源与用途 |
| :--- | :--- | :--- |
-| **Go** | 1.26+ | 核心控制平面、CLI、TUI、HTTP API 与 SQLite 状态机 |
-| **Node.js** | 24+ | 前端管理界面(`web/` 目录下的 React 18 + Vite) |
+| **Go** | 遵循 [go.mod](go.mod),当前 1.26.10 | 核心控制平面、CLI、TUI、HTTP API 与 SQLite 状态机 |
+| **Node.js** | 24+ | 前端管理界面(`web/` 目录下的 React 19 + Vite) |
| **Git** | 2.41+ | 源码版本管理 |
| **Rust** *(可选)* | 1.97.1+ | 仅在修改或验证 `analyzers/` 目录下的 WASI 代码分析器时需要 |
@@ -175,7 +175,7 @@ cd ..
- **React 前端** 负责用户交互呈现与视觉效果,不私自推导或伪造未经后端确认的状态。
3. **纯 CSS 现代性能标准**:
- 界面视觉效果优先使用现代纯 CSS 属性(如 `backdrop-filter`、多层 `box-shadow` 内阴影、`linear-gradient`)。
- - 避免引入高开销的 SVG 位移滤镜(如 `feTurbulence`)或全屏 WebGL Canvas,确保在展示大量代码和日志输出时保持 120fps 丝滑滚动。
+ - 涉及滚动和视觉效果的改动应在实际页面验证;性能结论应记录测量环境与结果。
4. **统一规范的产品命名**:
- 所有面向用户的标题、文案、关于页面与文档中,产品官方名称一律统一为 **`Universal-Code`**。
@@ -190,7 +190,7 @@ cd ..
git checkout -b feat/support-deepseek-r1
```
2. 保持 Commit 粒度清晰,提交信息简明扼要。
-3. 在本地完整运行并通过 `npm test` 和 `go test`。
+3. 运行覆盖本次改动的相关测试和必需检查,在 PR 中记录验证范围与结果。
4. 在 GitHub 上发起 Pull Request,简要说明:
- **改动背景**:解决了什么问题或增加了什么特性。
- **关键设计**:采用了什么样的架构思路。
diff --git a/README.en.md b/README.en.md
index 992312379..e545557d3 100644
--- a/README.en.md
+++ b/README.en.md
@@ -16,7 +16,7 @@
-
+
@@ -31,10 +31,10 @@
## Highlights
- **Built for Local Repositories**: Read, inspect, and edit local codebases with built-in line-by-line diff inspection and auditable checkpoints (Rewind).
-- **Transparent Permission Control**: Choose between read-only analysis, approval-gated runs, and automated Full Access. High-risk actions (file deletion, unlisted commands) always require manual confirmation.
+- **Transparent Permission Control**: Ask / Auto / Full authorization allows, requests approval for, or denies operations according to current permissions, verified effects, and safety policy. Full requires explicit activation in the current process.
- **Ready for Any Model**: Native support for OpenAI, Anthropic Claude, DeepSeek, Kimi, local Ollama models, and custom OpenAI-compatible endpoints with on-the-fly model switching. Also supports quick browser-based ChatGPT account sign-in.
- **Traceable Web Research**: Prioritizes provider-hosted native search, fetches GitHub Issues/PRs, Hacker News, and RSS/Atom feeds, and keeps source snapshots and citations.
-- **Modern Liquid Glass Aesthetic**: Crafted with pure CSS optical refraction (macOS / VisionOS acrylic style), supporting Dark, Light, and Mist frosted themes at a responsive 120fps.
+- **Switchable Interface Themes**: Dark, Light, and Mist themes use CSS blur, translucent backgrounds, gradients, and edge shadows for a glass-like appearance.
---
@@ -52,7 +52,7 @@ Packages retain their historical filenames; the product name shown in the applic
### Option 2: Build from Source
-Install **Go as pinned in [go.mod](go.mod), currently 1.26.9**, and **Node.js 24** (CI uses 24.16.0), then clone the repository:
+Install **Go as pinned in [go.mod](go.mod), currently 1.26.10**, and **Node.js 24** (CI uses 24.16.0), then clone the repository:
```bash
git clone https://github.com/CWNU-Open-Source-Community/Universal-Code.git
@@ -110,6 +110,7 @@ Switch between **System, Light, Dark, and Mist** under **Settings → Appearance
| :--- | :--- | :--- |
| **User Guide** | [Usage Guide](docs/usage.md) | Client walkthrough, CLI commands, and typical workflows |
| **Web Research** | [Search & Citations](docs/web-evidence.md) | Search setup, snapshot storage, URL provenance, and diagnostics |
+| **Chat Resources** | [Links, Files & Clipboard](docs/chat-resource-interactions.md) | Link previews, resource context menus, and file/image copy and paste |
| **Code Auditing** | [Workspace Checkpoints](docs/workspace-checkpoints.md) | Line-by-line diff review, snapshots, and safe Rewind |
| **Extensions** | [Extensions & MCP](docs/extensions.md) | Integrating Model Context Protocol (MCP) and custom skills |
| **Code Intelligence** | [LSP Support](docs/code-intelligence.md) | Symbol jump, type inference, and repository index |
diff --git a/README.md b/README.md
index 6c05fa64e..82d903ecf 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
-
+
@@ -31,10 +31,10 @@
## 核心特性
- **深度聚焦本地项目**:直接阅读、分析与修改本地代码库,内置 Diff 逐行审阅与可信撤销(Rewind / Checkpoints)能力。
-- **透明可控的安全策略**:支持只读分析、交互审批(Approval Mode)与全自动(Full Access)权限分级,高危操作(文件删除、非白名单命令)严格阻断并要求人工确认。
+- **透明可控的安全策略**:采用 Ask / Auto / Full 分级授权;操作按当前权限、实际影响和安全策略自动执行、请求审批或拒绝,Full 须在当前进程显式激活。
- **主流模型即开即用**:原生接入 OpenAI、Anthropic Claude、DeepSeek、Kimi、Ollama 本地模型与任何兼容 OpenAI 的第三方 API,支持单会话内即时热切,并支持通过系统浏览器快速完成 ChatGPT 账户授权登录。
- **真实来源的网络检索**:优先调用供应商原生搜索,支持直接抓取 GitHub PR/Issue、Hacker News 与 RSS/Atom 资讯,自动附带快照存证与引用出处。
-- **现代苹果液态玻璃美学**:采用纯 CSS 物理折射模型(macOS / VisionOS 亚克力液态玻璃风格),提供深色、浅色与雾透磨砂质感,兼具 120fps 极速渲染。
+- **可切换的界面主题**:提供深色、浅色与雾透主题,使用 CSS 模糊、半透明背景、渐变和边缘阴影呈现玻璃质感。
---
@@ -52,7 +52,7 @@
### 方式二:从源码构建运行
-安装 **Go(版本遵循 [go.mod](go.mod),当前为 1.26.9)** 与 **Node.js 24**(CI 使用 24.16.0),然后克隆仓库:
+安装 **Go(版本遵循 [go.mod](go.mod),当前为 1.26.10)** 与 **Node.js 24**(CI 使用 24.16.0),然后克隆仓库:
```bash
git clone https://github.com/CWNU-Open-Source-Community/Universal-Code.git
diff --git a/assets/branding/README.md b/assets/branding/README.md
index 301e0ff4c..9c9be79f0 100644
--- a/assets/branding/README.md
+++ b/assets/branding/README.md
@@ -8,10 +8,12 @@ a glass globe with a terminal prompt and a segmented navy ring on a soft white r
- SHA-256: `a97e71e5f373d5d48ce24e39360cd390be2beabfe731037ac114ec38f12407d6`
- Source treatment: preserved exactly as supplied; no generative redraw
-Web, Windows, and macOS assets are deterministic resize, rounded-alpha-mask,
-or container-format derivatives of this master. This includes the checked-in
-300×300 Partner Center listing icon. The artwork itself is not redrawn or
-simplified. Regenerate every tracked derivative with:
+Web, Windows, and macOS assets use a deterministic centered crop of the middle
+60% of this master, followed by resizing, rounded-alpha masking and platform
+container encoding. Removing the presentation padding makes the navy mark occupy
+about 75% of each icon, including the 16px and 32px taskbar/favicon sizes. This
+also applies to the checked-in 300×300 Partner Center listing icon. The master
+and the artwork remain unchanged. Regenerate every tracked derivative with:
```powershell
pwsh ./scripts/generate-brand-assets.ps1
diff --git a/cmd/cyberagent-desktop/traverse_board_windows_amd64.syso b/cmd/cyberagent-desktop/traverse_board_windows_amd64.syso
index 989731b2e..e70c8acb3 100644
Binary files a/cmd/cyberagent-desktop/traverse_board_windows_amd64.syso and b/cmd/cyberagent-desktop/traverse_board_windows_amd64.syso differ
diff --git a/cmd/cyberagent-desktop/traverse_board_windows_arm64.syso b/cmd/cyberagent-desktop/traverse_board_windows_arm64.syso
index 0ae873a4b..fd0c1e042 100644
Binary files a/cmd/cyberagent-desktop/traverse_board_windows_arm64.syso and b/cmd/cyberagent-desktop/traverse_board_windows_arm64.syso differ
diff --git a/docs/acceptance/icon-optical-size-20261021/README.md b/docs/acceptance/icon-optical-size-20261021/README.md
new file mode 100644
index 000000000..0de87f980
--- /dev/null
+++ b/docs/acceptance/icon-optical-size-20261021/README.md
@@ -0,0 +1,37 @@
+# 平台图标主体大小验证
+
+原图中的展示留白被整体缩入图标,32px 图标的深色主体只有 14×14px。
+生成脚本现在使用母版中心的 60% 区域,32px 图标主体为 24×23px,
+16px 图标主体为 12×11px。母版、终端图形和圆角透明遮罩保持原样。
+尺寸按不透明深蓝像素的边界计算,抗锯齿会使横纵边界相差一个像素。
+
+## 真实页面前后截图
+
+- 前:主线提交 `e578c225f517fa381f1321919330da8db5881856` 的原始资产。
+- 后:同一份代码,应用本次生成脚本及其派生资产。
+- 两者均使用锁文件安装依赖并执行 `npm --prefix web run build`,
+ 通过 Vite preview 打开实际连接页面;Chromium,1280×900 CSS px,2026-10-21。
+- 深色截图使用应用已有的 `prayu.theme=dark` 偏好。未替换页面 DOM 或绘制模拟界面。
+
+| 浅色:修改前 | 浅色:修改后 |
+| --- | --- |
+|  |  |
+
+| 深色:修改前 | 深色:修改后 |
+| --- | --- |
+|  |  |
+
+## 已运行验证
+
+- 新的小图标主体占比回归测试在旧资产上失败,在新资产上通过。
+- `go test -count=1 ./internal/releasegate` 通过,包含母版与派生件同步、
+ ICO/COFF 一致性、ICNS Retina 尺寸槽及 PNG 尺寸/透明度检查。
+- `go test -count=1 -tags 'desktop,wv2runtime.error' ./cmd/cyberagent-desktop -run TestDesktopWindowsResourcesPreserveDPIAndIcons`
+ 通过,确认 EXE 资源保留 PerMonitorV2、asInvoker 及正确图标。
+- Windows 视觉资产清单校验通过,53 个 PNG 满足尺寸、格式和文件大小要求。
+- 前端品牌组件 3 项测试、类型检查与生产构建通过。
+- 本机使用 `desktop,production,wv2runtime.error` 编译 Windows EXE 成功;
+ 从 EXE 提取的 32px 图标与生成的 PNG 逐像素一致。
+- 母版 SHA-256 仍为 `a97e71e5f373d5d48ce24e39360cd390be2beabfe731037ac114ec38f12407d6`。
+
+本次截图覆盖 Web 连接页面;macOS ICNS 已检查容器和尺寸,尚未在 macOS Dock 中目视验证。
diff --git a/docs/acceptance/icon-optical-size-20261021/icon-size-after-dark.png b/docs/acceptance/icon-optical-size-20261021/icon-size-after-dark.png
new file mode 100644
index 000000000..ef3081c12
Binary files /dev/null and b/docs/acceptance/icon-optical-size-20261021/icon-size-after-dark.png differ
diff --git a/docs/acceptance/icon-optical-size-20261021/icon-size-after-light.png b/docs/acceptance/icon-optical-size-20261021/icon-size-after-light.png
new file mode 100644
index 000000000..d02d9deea
Binary files /dev/null and b/docs/acceptance/icon-optical-size-20261021/icon-size-after-light.png differ
diff --git a/docs/acceptance/icon-optical-size-20261021/icon-size-before-dark.png b/docs/acceptance/icon-optical-size-20261021/icon-size-before-dark.png
new file mode 100644
index 000000000..7b65e9762
Binary files /dev/null and b/docs/acceptance/icon-optical-size-20261021/icon-size-before-dark.png differ
diff --git a/docs/acceptance/icon-optical-size-20261021/icon-size-before-light.png b/docs/acceptance/icon-optical-size-20261021/icon-size-before-light.png
new file mode 100644
index 000000000..59cb8dd3a
Binary files /dev/null and b/docs/acceptance/icon-optical-size-20261021/icon-size-before-light.png differ
diff --git a/docs/architecture.md b/docs/architecture.md
index 4c442b8f0..5344b3917 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -157,7 +157,7 @@ The production React bundle is built by Vite but hosted only when Go receives an
## Core Domain
-`Run` is a domain aggregate, not a programming language, operating-system process, or replacement for Go/TypeScript/Rust. Go creates and owns Run lifecycle state; TypeScript may display and control it through Go APIs. Rust never owns a Run. The current analyzer fixture has no Run field or product bridge; a future Go adapter may bind a validated analyzer operation to a Run outside the Rust wire contract.
+`Run` is a domain aggregate, not a programming language, operating-system process, or replacement for Go/TypeScript/Rust. Go creates and owns Run lifecycle state; TypeScript may display and control it through Go APIs. Rust never owns a Run. Go application services bind the fixed embedded digest analyzer to a Run and commit its execution evidence. The Rust wire contract remains free of Run identity and authority. See the [Analyzer README](../analyzers/README.md) for the current execution boundary.
Budget, events, sandbox sessions, and reports are Run-scoped rather than embedded into one large object. Their modules remain independent and persist references by `run_id`; `AgentRunner` coordinates their lifecycle.
@@ -1041,7 +1041,7 @@ internal/store/ SQLite stores and migrations
internal/session/ Compatibility conversation service
internal/tui/ Bubble Tea adapter
internal/httpapi/ Loopback-only read/control API, OpenAPI contract, and bounded Run-event SSE
-internal/analyzer/ Go-owned analyzer protocol, validation, and future bridge boundary
+internal/analyzer/ Go-owned analyzer protocol, validation, and embedded WASI execution boundary
```
Packages split when a concrete implementation establishes different ownership. New tools and integrations reuse these services and add focused behavior tests for their contracts.
@@ -1334,6 +1334,9 @@ records these bounds.
## Go-Owned Analyzer Protocol And Rust Fixture
+The following P10-A/B sections record the initial implementation stages. For the
+current product execution path, see the [Analyzer README](../analyzers/README.md).
+
P10-A1 establishes `internal/analyzer` as a pure Go protocol owner rather than a process
launcher. `analyzer_protocol.v1` accepts one canonical inline Base64 payload plus explicit
limits and four capability declarations that must all be false. Strict decoding rejects
diff --git a/docs/architecture/bounded-project-tests.md b/docs/architecture/bounded-project-tests.md
index 552623f96..70c434581 100644
--- a/docs/architecture/bounded-project-tests.md
+++ b/docs/architecture/bounded-project-tests.md
@@ -1,11 +1,18 @@
# Bounded project tests
-`run_tests` is the root Supervisor's project test entry point in Code Plan and
-Deliver. Review is a workflow state, not an execution phase: the domain has
+`run_tests` is the project test entry point in Code Plan and Deliver for the root
+Supervisor and recorded `specialist_runner` workers with matching WorkerAuthority.
+Review is a workflow state, not an execution phase: the domain has
only `plan` and `deliver` phases.
-The request contains `version: "run-tests.v1"`, a relative directory `target`,
-and `timeout_milliseconds` from 1 through 25000. The host resolves the pinned
+The request contains `version: "run-tests.v1"`. `action: "run"` (the default when
+omitted) takes a relative directory `target` and `timeout_milliseconds` from 1
+through 25000. Longer tests use `action: "start"` with a timeout up to 1800000 ms,
+then `action: "wait"` with the original `job_id`, returned `cursor`, `max_bytes`
+(4–32768), and `wait_milliseconds` (1–5000). Wait observes the existing Job without
+a new target or timeout; repeating start is not a polling operation.
+
+The host resolves the pinned
project test ID or detects a supported project through the existing project
action registry. It chooses the executable, literal arguments, environment,
closed stdin, disabled network intent, output bounds and empty credentials.
@@ -21,10 +28,11 @@ fall back to the host. Dependency installation retains its sandbox-only gate.
The prepared command is pinned in the durable tool authority and regenerated
before approval and dispatch. The existing command authorizer, Run lease,
root attempt, gateway tool budget, command budget, timeout, cancellation and
-Job/output receipts remain authoritative. A test runs as one synchronous Job;
-failed or timed-out Jobs are returned as test evidence. A completed call is
-replayed without starting another Job. Specialist calls retain their existing
-bounded validation tools and child budget; `run_tests` grants no child access.
+Job/output receipts remain authoritative. Each run/start creates one Job; wait
+returns bounded output and status for that Job, retaining its original actor and
+authority. Failed or timed-out Jobs are returned as test evidence. A completed
+call is replayed without starting another Job. Worker execution retains its
+recorded assignment, WorkerAuthority, and child budget checks.
General `command_runtime` remains Deliver-only. Plan Job insertion additionally
requires a pending `run_tests` call with matching actor, command fingerprint,
diff --git a/docs/branding/README.md b/docs/branding/README.md
index 118d5dd12..73efc6392 100644
--- a/docs/branding/README.md
+++ b/docs/branding/README.md
@@ -29,14 +29,16 @@ canonical repository URLs while preserving compatibility identifiers.
尺寸为 1254×1254,保留原始白色圆角承托;SHA-256 为
`a97e71e5f373d5d48ce24e39360cd390be2beabfe731037ac114ec38f12407d6`。
-Web、Windows 与 macOS 资产只允许从该母版进行确定性的缩放、圆角透明遮罩和容器
-格式转换,不得用字母 `T`、旧 `P` 标志或生成式重绘替代。小尺寸界面使用图形标志
+Web、Windows 与 macOS 资产从母版中心的 60% 区域进行确定性裁切,再缩放、添加圆角
+透明遮罩并转换容器格式。这样去掉展示留白,让图形主体占图标约 75%,在 16px、32px
+任务栏与 favicon 中保持可辨认;母版本身不变。不得用字母 `T`、旧 `P` 标志或生成式重绘替代。小尺寸界面使用图形标志
本身,并由周围的可访问名称提供完整产品名。运行
`pwsh ./scripts/generate-brand-assets.ps1` 可一次重建所有受版本控制的平台派生件。
The maintainer-supplied glass globe, terminal prompt, and segmented navy ring are the official Universal-Code icon.
-Platform assets are deterministic resize, rounded-alpha-mask, and container derivatives of
-the exact supplied master above; generative redraws and letter placeholders are not canonical.
+Platform assets use a centered crop of the middle 60% of the exact supplied master, followed
+by resizing, rounded-alpha masking, and container conversion. The mark occupies about 75%
+of the output while the master remains unchanged; generative redraws and letter placeholders are not canonical.
| Surface | Derived assets |
|---|---|
diff --git a/docs/ci.md b/docs/ci.md
index de71f87c4..1ecb8b46d 100644
--- a/docs/ci.md
+++ b/docs/ci.md
@@ -30,11 +30,11 @@ successful aggregate result. Unclassified build inputs select full checks.
## Go security baseline
-The supported build toolchain is Go **1.26.9**, pinned by `go.mod`. Both central
+The supported build toolchain is Go **1.26.10**, pinned by `go.mod`. Both central
CI and release builders read that exact version with `go-version-file`.
This avoids selecting an older patch while the setup-go version catalog lags
behind a new Go security release. Local desktop builders require the same
-version; set `GOTOOLCHAIN=go1.26.9` when another Go toolchain is installed.
+version; set `GOTOOLCHAIN=go1.26.10` when another Go toolchain is installed.
The module's minimum version alone cannot exclude vulnerable newer release
branches, such as Go 1.27.0 and 1.27.1; a larger version is not security evidence.
The Windows fixed-toolchain fixture must match the exact version selected by
diff --git a/docs/evidence/markdown-current-state/after.png b/docs/evidence/markdown-current-state/after.png
new file mode 100644
index 000000000..728e4730c
Binary files /dev/null and b/docs/evidence/markdown-current-state/after.png differ
diff --git a/docs/evidence/markdown-current-state/before.png b/docs/evidence/markdown-current-state/before.png
new file mode 100644
index 000000000..bf3522de9
Binary files /dev/null and b/docs/evidence/markdown-current-state/before.png differ
diff --git a/docs/extensions.md b/docs/extensions.md
index 85b1fe3ba..e9340c038 100644
--- a/docs/extensions.md
+++ b/docs/extensions.md
@@ -105,10 +105,11 @@ Supervisor 首次最多展示 32 条已安装 Skill 摘要。更多摘要通过
组件与内容版本的绑定;停用、撤销或版本改变后,旧选择不能恢复权限。手动选择沿用每任务
最多 4 项、合计最多 4096 Token 的边界,超出预算会拒绝而不会截断正文。
-网页的“Skill 包”入口目前上传版本化 ZIP,新导入内容在扩展页审查与启用;历史 Skill
-登记列表用于兼容旧安装和移除。原生目录可通过上述 `skill import-dir` 导入后,在任务
-设置中手动选择。网页直接导入原生目录、URL/Git 导入和旧 Skill Catalog 发布者信任的
-GUI 入口仍由 #334 跟踪。
+网页支持安装版本化 Skill ZIP,并上传原生 `SKILL.md` 目录;原生目录暂存后,
+在扩展页审查与启用。
+Plugin 来源导入支持 URL/Git,由对应后端 capability 控制;高级设置提供旧 Skill Catalog
+发布者信任管理。历史 Skill 登记列表保留旧安装和移除操作。具体步骤见
+[使用指南](usage.md)。
同一来源的新 revision 使用新的 operation key 导入。如果旧 revision 仍启用,命令会报告 `another plugin version is enabled`;此时新 revision 已保留为 `approved`,旧 revision 继续启用。通过 `plugin list`、`plugin show` 核对两者的来源和 revision,并从各自最新输出取得 ID、`package_fingerprint` 与 `generation`,然后显式切换:
diff --git a/docs/macos-release.md b/docs/macos-release.md
index 8fe7d5567..b55f2519f 100644
--- a/docs/macos-release.md
+++ b/docs/macos-release.md
@@ -37,7 +37,7 @@ Extract the Actions transport ZIP, then the product ZIP inside it, and open `Tra
## 构建与核验
-本机源码构建需要 Xcode Command Line Tools、[go.mod](../go.mod) 固定的 Go(当前 1.26.9)、Node.js 24(CI 为 24.16.0)和 CGO。在仓库根目录运行:
+本机源码构建需要 Xcode Command Line Tools、[go.mod](../go.mod) 固定的 Go(当前 1.26.10)、Node.js 24(CI 为 24.16.0)和 CGO。在仓库根目录运行:
```sh
CGO_ENABLED=1 bash scripts/build-desktop-darwin.sh -Version v1.0.0-dev
diff --git a/docs/releases/v1.0.0.md b/docs/releases/v1.0.0.md
index 1e02b039b..2346f23d2 100644
--- a/docs/releases/v1.0.0.md
+++ b/docs/releases/v1.0.0.md
@@ -4,7 +4,12 @@
本版本以主线为基础,整合了 Full Access 权限分级、主流大模型接入、网络搜索取证、工作区检查点快照与苹果液态玻璃视觉系统。产品官方全称已统一为 **Universal-Code**。
-## 当前下载与验证状态(2026-09-21)
+> 本页保留首版整合时的范围与验证记录。当前预览包见
+> [v1.0.0-preview.20261020](https://github.com/CWNU-Open-Source-Community/Universal-Code/releases/tag/v1.0.0-preview.20261020);
+> 安装与启动说明见 [Windows](../windows-release.md) / [macOS](../macos-release.md)。
+> 预览包不等同于正式 `v1.0.0` 发布。
+
+## 首版整合时的下载与验证状态(2026-09-21)
- 首版源码 [PR #203](https://github.com/CWNU-Open-Source-Community/Traverse-Board/pull/203) 已合并;尚未公开 `v1.0.0` GitHub Release。现有 [Releases](https://github.com/CWNU-Open-Source-Community/Traverse-Board/releases) 是历史预览,不能据此判断是否包含最新源码功能。
- Mac 双架构候选与发布接线 [PR #204](https://github.com/CWNU-Open-Source-Community/Traverse-Board/pull/204) 已合并,下载和启动步骤见 [Mac 说明](../macos-release.md)。Windows 下载与签名/Store 状态见 [Windows 说明](../windows-release.md)。
diff --git a/docs/sandbox-environments.md b/docs/sandbox-environments.md
index 4921ae2eb..5dddc8423 100644
--- a/docs/sandbox-environments.md
+++ b/docs/sandbox-environments.md
@@ -28,8 +28,8 @@
## 准备项目依赖
-开始编码后,在「审阅改动 → 检查与交付 → 准备项目」检测 npm 或 Go
-项目,核对锁文件和命令,选择本次允许下载的包源,再在所选沙箱中
+开始编码后,在「审阅改动 → 检查与交付 → 准备项目」检测受支持的
+项目(当前范围见[准备项目依赖](project-preparation.md)),核对锁文件和命令,选择本次允许下载的包源,再在所选沙箱中
离线安装。依赖下载与任务的网页搜索分别授权,npm 安装脚本另行选择。
工具链应提前安装在本机环境、固定镜像或模板中。
diff --git a/docs/schema-baseline.md b/docs/schema-baseline.md
index 81dfedcb7..73c503f1c 100644
--- a/docs/schema-baseline.md
+++ b/docs/schema-baseline.md
@@ -1,6 +1,6 @@
# SQLite clean-install baseline / SQLite 全新安装基线
-Traverse Board keeps two database initialization paths:
+Universal-Code keeps two database initialization paths:
- a generated consolidated schema baseline for a database that is proven empty; and
- the complete, append-only migration history for every existing or uncertain database.
@@ -40,13 +40,15 @@ transaction commit identifies completion. A statement error, cancellation, disk-
condition, or process exit therefore leaves either the complete latest database or
no baseline schema; it cannot leave a partial baseline marked as current.
-The current v136 artifact contains 360 tables, 245 explicit indexes, 912 triggers,
-and 2 views. Clean install has no application seed rows; the 136 canonical migration
-ledger rows are the only generated data.
+The current version and fingerprints are recorded in
+[`clean_install_baseline_generated.go`](../internal/store/clean_install_baseline_generated.go)
+and checked against `LatestSchemaVersion` in
+[`migrations.go`](../internal/store/migrations.go). Clean install has no application
+seed rows; the canonical migration ledger rows are the only generated data.
## Backup and restore / 备份与恢复
-Before launching a newer binary against a non-empty profile, stop all Traverse Board
+Before launching a newer binary against a non-empty profile, stop all Universal-Code
Desktop, CLI, and API processes and make an offline copy of `cyberagent.db`. Keep any
remaining SQLite journal/WAL sidecar files with the diagnostic copy. A filesystem copy
while a writer is active is not a supported backup.
@@ -58,9 +60,9 @@ that selects safe readers and migrations.
## Binary rollback boundary / 旧二进制回滚边界
-A baseline-created v136 database has the same schema objects and canonical migration
-ledger as a v1-to-v136 historical creation. A pre-baseline binary that already knows
-the identical v1-to-v136 plan can therefore open it. A binary whose
+A baseline-created database has the same schema objects and canonical migration
+ledger as historical creation through the same version. A binary that knows
+the identical migration plan can therefore open it. A binary whose
`LatestSchemaVersion` is below the database version cannot open it and is not made safe
by this baseline. Use the offline pre-upgrade backup for that downgrade; do not delete
newer ledger rows.
@@ -75,8 +77,8 @@ install safely returns to historical replay.
2. Fix the underlying storage condition (free space, permissions, device health), then
restart. A rolled-back empty database is eligible for a fresh atomic attempt.
3. If a deterministic baseline defect affects a genuinely empty new profile, use the
- last known-good binary that already supports schema v136. That binary retains the
- historical creation path and produces the same latest schema.
+ known-good binary that supports the target schema and retains the historical
+ creation path. It must produce the same target schema.
4. If the database contains any user object or durable data, do not try to make it
appear empty. Diagnose the historical migration error or restore the offline backup.
5. Preserve the failing copy for diagnosis. Unknown migration history intentionally
diff --git a/docs/technical-reference.en.md b/docs/technical-reference.en.md
index f603575cd..d902c29fe 100644
--- a/docs/technical-reference.en.md
+++ b/docs/technical-reference.en.md
@@ -62,7 +62,7 @@ Schema v115 introduces `agent-code-tools.v1`, allowing the root Supervisor to co
| Mode | Available tools |
|---|---|
-| Code / Plan / root | `workspace_list`, `workspace_read`, `workspace_glob`, and `workspace_grep` |
+| Code / Plan / root | `workspace_list`, `workspace_read`, `workspace_glob`, and `workspace_grep`; `github_review_evidence_list/read` when current-Run GitHub evidence is available |
| Code / Deliver / root with Code or Script Profile | The read tools plus `workspace_change`, `workspace_apply`, and `workspace_delete` |
| Code / Deliver / root with Review or Learn Profile | Read tools only |
| Cyber Surface or Specialist | No `agent-code-tools.v1` tools are advertised; the capability snapshot records the refusal reason |
diff --git a/docs/usage.md b/docs/usage.md
index 583465a6c..d420e70ae 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -636,26 +636,26 @@ cyberagent skill candidate reject --candidate-fingerprint --candidate-fingerprint --operation-key --confirm-untrusted-skill
```
-The embedded read-only `skill.v1` Registry exposes twelve bounded workflows: the `code`, `review`, `learn`, `script`, and `plan-delivery` foundations plus `doctor`, `debug`, `run-verify`, `focused-checks`, `simplify`, `security-review`, and `run-skill-generator`. Each current manifest declares a compatibility tuple: `profiles` identifies the work intent (`code|review|learn|script`), `surfaces` the product execution surface (`code|cyber`), `phases` the active Run phase (`plan|deliver`), and `roles` the receiving Agent (`root|specialist`). All four dimensions must match before content is delivered.
-
-Invocation policy is separate from delivery compatibility. `user_invocable` permits an operator-origin selection; `model_invocable` permits a future controlled model-origin selector to propose the Skill but does not itself auto-select anything; `explicit_only` requires the operator to name and pin the Skill and therefore requires `user_invocable=true` and `model_invocable=false`. These fields never grant tools, network, files, processes, Provider access, or a new model-selection capability. The current product selection path remains operator-only.
-
-| Built-in | Version | Profiles | Surfaces | Phases | Roles | Invocation |
-| --- | --- | --- | --- | --- | --- | --- |
-| `code` | 1.2.0 | code | code | plan, deliver | root, specialist | user + model eligible |
-| `review` | 1.5.0 | code, review | code | plan, deliver | root, specialist | user + model eligible |
-| `learn` | 1.2.0 | learn | code | plan, deliver | root, specialist | user + model eligible |
-| `script` | 1.2.0 | script | code, cyber | plan, deliver | root, specialist | user + model eligible |
-| `plan-delivery` | 1.2.0 | all | code, cyber | plan, deliver | root | explicit user only |
-| `doctor` | 1.0.0 | all | code, cyber | plan | root | user + model eligible |
-| `debug` | 1.0.0 | all | code, cyber | plan, deliver | root | user + model eligible |
-| `run-verify` | 1.1.0 | code, script | code, cyber | deliver | root | user + model eligible |
-| `focused-checks` | 1.1.0 | code, review, script | code, cyber | deliver | root | user + model eligible |
-| `simplify` | 1.0.0 | code | code | deliver | root | user + model eligible |
-| `security-review` | 1.0.0 | code, review, script | code, cyber | plan, deliver | root | user + model eligible |
-| `run-skill-generator` | 1.0.0 | code | code | deliver | root | explicit user only |
-
-`doctor` reports provider, harness, workspace, sandbox, network-scope, tool, and Skill compatibility without repairing it. `debug` builds a bounded evidence timeline and permits repair only in Deliver. `run-verify@1.1.0` requires an exact `ui-evidence.v1` source/recipe/runtime binding, real interaction assertions, content-addressed artifact inventory, cleanup receipt, focused-check mapping, and a PR-ready verification receipt; `not_run` and missing matrix cells must remain explicit non-passes. It does not grant browser, process, network, Profile, or credential authority, and on Cyber remains restricted by guidance to an admitted local sandbox. `review@1.5.0` and `focused-checks@1.2.0` consume current `code-intel-lsp.v1` plus GitHub PR/CI evidence when available, preserve source and freshness bindings, and keep partial, stale, unavailable, or not-run evidence explicit instead of treating it as proof. `simplify` requires call-site evidence before deletion, and `security-review` remains read-only unless a separate Deliver authorization exists.
+The embedded read-only `skill.v1` Registry currently exposes fourteen workflows.
+The maintained catalog and invocation rules are in [Built-in skills](builtin-skills.md);
+exact current versions are defined by the [embedded manifests](../internal/skills/builtins/).
+Each manifest declares compatible `profiles`, `surfaces`, `phases`, and receiving
+`roles`. All four dimensions must match before content is delivered.
+
+Invocation policy is separate from delivery compatibility. `user_invocable` permits
+operator selection; `model_invocable` permits compatible model-origin reads through
+`skill_read`. `explicit_only` requires operator selection, with `user_invocable=true`
+and `model_invocable=false`. The three explicit workflows are `plan-delivery`,
+`loop-monitor`, and `run-skill-generator`. Existing operator-pinned versions take
+precedence. Reading a skill does not grant tools, network, files, processes, Provider
+access, or approval authority.
+
+`doctor` provides read-only Plan diagnostics. `debug` diagnoses reproducible faults
+and permits repair only in Deliver;
+`run-verify` verifies actual program behavior, and `focused-checks` selects tests by
+change and risk. `run-verify` treats `ui-evidence.v1` as an optional operator workflow,
+not a requirement for every task. `simplify` requires call-site evidence before
+deletion, and `security-review` remains read-only unless separate Deliver authorization exists.
`run-skill-generator` does not create a trusted Skill. When it is explicitly selected and actually delivered to a Code/Deliver root turn, Go exposes `skill_candidate_propose`; otherwise the tool is omitted and forged calls are rejected. A successful call stores at most 4096 bytes of validated, secret-screened Markdown as an inert `proposed` candidate bound to the real tool invocation, Run/Session/Workspace/root, deterministic package, and exact fingerprints. The ordinary tool result and candidate list never expose the body; `skill candidate show --show-content` uses explicit untrusted-content delimiters for human inspection.
@@ -1940,8 +1940,9 @@ These test actions do not install packages or enable network access. Missing
tools or dependencies produce an unavailable action or the actual failed Job.
The current SBX template does not support these Python/Rust presets.
-The same panel can detect npm and Go tests from project files when no test action
-is pinned. Use **Prepare project** first to review the lockfile, authorize exact
+The same panel can detect supported project tests when no test action is pinned;
+see the current support table in [Project preparation](project-preparation.md).
+Use **Prepare project** first to review the lockfile, authorize exact
package sources, and install dependencies offline in the selected sandbox.
Downloads, install scripts, cache reuse, cancellation, and interrupted requests
are described in [Prepare project dependencies](project-preparation.md).
diff --git a/docs/windows-release.md b/docs/windows-release.md
index 7baf7e161..21eae82d1 100644
--- a/docs/windows-release.md
+++ b/docs/windows-release.md
@@ -30,7 +30,7 @@ If this artifact has expired, select a newer successful [Desktop release run](ht
### 从源码构建与启动 / Build and launch from source
-在仓库根目录使用 PowerShell、[go.mod](../go.mod) 固定的 Go(当前 1.26.9)与 Node.js 24(CI 为 24.16.0):
+在仓库根目录使用 PowerShell、[go.mod](../go.mod) 固定的 Go(当前 1.26.10)与 Node.js 24(CI 为 24.16.0):
```powershell
.\scripts\build-desktop.ps1 -Version v1.0.0-dev
diff --git a/go.mod b/go.mod
index d12646dbf..b4c37fbb6 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module cyberagent-workbench
-go 1.26.9
+go 1.26.10
require (
github.com/Microsoft/go-winio v0.6.2
diff --git a/internal/browserruntime/agent_browser_cleanup_test.go b/internal/browserruntime/agent_browser_cleanup_test.go
index 03a702edb..2ccfa71fc 100644
--- a/internal/browserruntime/agent_browser_cleanup_test.go
+++ b/internal/browserruntime/agent_browser_cleanup_test.go
@@ -1,6 +1,78 @@
package browserruntime
-import "testing"
+import (
+ "context"
+ "errors"
+ "os"
+ "testing"
+ "time"
+)
+
+// Model a native process observation that cannot prove cleanup on its first
+// attempt, then verifies the owned process scope on the explicit retry.
+type retryableAgentBrowserTestProcess struct {
+ attempts int
+ done chan struct{}
+ exit BrowserProcessExit
+}
+
+func (p *retryableAgentBrowserTestProcess) PID() int { return 1 }
+func (p *retryableAgentBrowserTestProcess) Done() <-chan struct{} { return p.done }
+func (p *retryableAgentBrowserTestProcess) Exit() (BrowserProcessExit, bool) {
+ return p.exit, true
+}
+func (p *retryableAgentBrowserTestProcess) Stop(context.Context) error {
+ p.attempts++
+ if p.attempts == 1 {
+ close(p.done)
+ return errors.New("process observation unavailable")
+ }
+ p.exit.ProcessScopeVerified = true
+ return nil
+}
+
+func TestAgentBrowserDoneAllowsExplicitCleanupRetry(t *testing.T) {
+ profile, err := prepareAgentBrowserProfile(t.TempDir(), AgentBrowserAuthority{SessionID: "retry"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ life, cancel := context.WithCancel(t.Context())
+ defer cancel()
+ process := &retryableAgentBrowserTestProcess{
+ done: make(chan struct{}), exit: BrowserProcessExit{ProcessScope: DarwinAgentBrowserProcessScope},
+ }
+ r := &AgentBrowserRuntime{
+ request: AgentBrowserStartRequest{Authority: AgentBrowserAuthority{SessionID: "retry"}},
+ profile: profile, process: process, life: life, cancel: cancel,
+ done: make(chan struct{}), state: "ready",
+ }
+ go r.reapOnInvalidation()
+ r.Cancel()
+ select {
+ case <-r.Done():
+ case <-time.After(2 * time.Second):
+ t.Fatal("first cleanup attempt did not finish")
+ }
+ if status := r.Status(); status.State != "cleanup_pending" {
+ t.Fatalf("unverified cleanup reported as complete: %+v", status)
+ }
+ if _, err := os.Stat(profile.path); err != nil {
+ t.Fatalf("profile removed before process cleanup was verified: %v", err)
+ }
+ if _, err := r.Snapshot(t.Context()); !errors.Is(err, ErrAgentBrowserClosed) {
+ t.Fatalf("pending cleanup allowed another action: %v", err)
+ }
+ result, err := r.Close(t.Context())
+ if err != nil || !result.ProcessesStopped() || !result.ProfileRemoved || result.CleanupPending {
+ t.Fatalf("explicit cleanup retry: %+v %v", result, err)
+ }
+ if status := r.Status(); status.State != "closed" || process.attempts != 2 {
+ t.Fatalf("cleanup retry did not complete: status=%+v attempts=%d", status, process.attempts)
+ }
+ if _, err := os.Stat(profile.path); !errors.Is(err, os.ErrNotExist) {
+ t.Fatalf("profile survived verified cleanup: %v", err)
+ }
+}
func TestAgentBrowserCleanupRequiresItsActualProcessScope(t *testing.T) {
cases := []struct {
diff --git a/internal/browserruntime/agent_browser_platform_darwin.go b/internal/browserruntime/agent_browser_platform_darwin.go
index a339e6f58..1da77f12f 100644
--- a/internal/browserruntime/agent_browser_platform_darwin.go
+++ b/internal/browserruntime/agent_browser_platform_darwin.go
@@ -181,7 +181,10 @@ func LaunchAgentBrowser(ctx context.Context, request AgentBrowserStartRequest) (
return nil, err
}
go owned.monitor()
- if err := r.connect(ctx, filepath.Join(profile.path, "Data")); err != nil {
+ // Native Chrome startup can exceed the Windows 30s connection budget on
+ // macOS. Keep the supervisor's 3m launch bound; a shorter caller deadline
+ // or runtime cancellation still stops endpoint discovery immediately.
+ if err := r.connect(ctx, filepath.Join(profile.path, "Data"), 3*time.Minute); err != nil {
return nil, err
}
handlers, err := darwinChromeCrashpad(profile.path)
diff --git a/internal/browserruntime/agent_browser_runtime_test.go b/internal/browserruntime/agent_browser_runtime_test.go
index 03349ef7a..0b7687947 100644
--- a/internal/browserruntime/agent_browser_runtime_test.go
+++ b/internal/browserruntime/agent_browser_runtime_test.go
@@ -307,9 +307,22 @@ func TestAgentBrowserRealCancelDoesNotWaitForAuthorityLookup(t *testing.T) {
case <-time.After(10 * time.Second):
t.Fatal("Cancel waited behind authority callback")
}
+ // Done ends the first cleanup attempt; a transient native process
+ // observation failure can leave cleanup retryable. Verify completion while
+ // the authority callback is still blocked, before releasing it on return.
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ if _, err := r.Snapshot(ctx); !errors.Is(err, ErrAgentBrowserClosed) {
+ t.Fatalf("cancelled session accepted action: %v", err)
+ }
+ result, err := r.Close(ctx)
+ if err != nil || !result.ProcessesStopped() || !result.ProfileRemoved || result.CleanupPending {
+ t.Fatalf("cleanup while authority callback blocked: %+v %v", result, err)
+ }
if status := r.Status(); status.State != "closed" {
t.Fatalf("cleanup incomplete: %+v", status)
}
+ t.Logf("owned cleanup while authority callback blocked: %+v", result)
}
func agentBrowserWaitSnapshot(t *testing.T, r *AgentBrowserRuntime, predicate func(AgentBrowserSnapshot) bool) AgentBrowserSnapshot {
diff --git a/internal/browserruntime/agent_browser_start.go b/internal/browserruntime/agent_browser_start.go
index bcf890ba4..b6d7ef9ca 100644
--- a/internal/browserruntime/agent_browser_start.go
+++ b/internal/browserruntime/agent_browser_start.go
@@ -153,14 +153,14 @@ func launchPEAgentBrowser(ctx context.Context, request AgentBrowserStartRequest)
return nil, err
}
r.process = &BrowserProcess{spec: spec, platform: platform}
- if err := r.connect(ctx, profile.path); err != nil {
+ if err := r.connect(ctx, profile.path, 30*time.Second); err != nil {
return nil, err
}
return r, nil
}
-func (r *AgentBrowserRuntime) connect(ctx context.Context, profilePath string) error {
- startCtx, startCancel := context.WithTimeout(ctx, 30*time.Second)
+func (r *AgentBrowserRuntime) connect(ctx context.Context, profilePath string, startupTimeout time.Duration) error {
+ startCtx, startCancel := context.WithTimeout(ctx, startupTimeout)
defer startCancel()
stopLife := context.AfterFunc(r.life, startCancel)
defer stopLife()
diff --git a/internal/browserruntime/agent_browser_start_deadline_test.go b/internal/browserruntime/agent_browser_start_deadline_test.go
new file mode 100644
index 000000000..5e12e914a
--- /dev/null
+++ b/internal/browserruntime/agent_browser_start_deadline_test.go
@@ -0,0 +1,49 @@
+package browserruntime
+
+import (
+ "context"
+ "errors"
+ "testing"
+ "testing/synctest"
+ "time"
+)
+
+func TestAgentBrowserConnectStartupDeadlineAndCancellation(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ budget time.Duration
+ parent time.Duration
+ cancelLife time.Duration
+ want time.Duration
+ wantErr error
+ }{
+ {"windows_budget", 30 * time.Second, 5 * time.Minute, 0, 30 * time.Second, context.DeadlineExceeded},
+ {"macos_budget", 3 * time.Minute, 5 * time.Minute, 0, 3 * time.Minute, context.DeadlineExceeded},
+ {"shorter_parent", 3 * time.Minute, 45 * time.Second, 0, 45 * time.Second, context.DeadlineExceeded},
+ {"runtime_cancel", 3 * time.Minute, 5 * time.Minute, 2 * time.Second, 2 * time.Second, context.Canceled},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ // No endpoint is published: exercise the real discovery wait with
+ // virtual time rather than sleeping for the native startup budget.
+ profile := t.TempDir()
+ synctest.Test(t, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(t.Context(), tc.parent)
+ defer cancel()
+ life, cancelLife := context.WithCancel(t.Context())
+ defer cancelLife()
+ if tc.cancelLife > 0 {
+ time.AfterFunc(tc.cancelLife, cancelLife)
+ }
+ r := &AgentBrowserRuntime{
+ life: life,
+ process: &retryableAgentBrowserTestProcess{done: make(chan struct{})},
+ }
+ started := time.Now()
+ err := r.connect(ctx, profile, tc.budget)
+ if !errors.Is(err, tc.wantErr) || time.Since(started) != tc.want {
+ t.Fatalf("endpoint discovery ended after %s with %v; want %s and %v", time.Since(started), err, tc.want, tc.wantErr)
+ }
+ })
+ })
+ }
+}
diff --git a/internal/browserruntime/restricted_cdp_handshake_test.go b/internal/browserruntime/restricted_cdp_handshake_test.go
new file mode 100644
index 000000000..3edfc3475
--- /dev/null
+++ b/internal/browserruntime/restricted_cdp_handshake_test.go
@@ -0,0 +1,89 @@
+package browserruntime
+
+import (
+ "context"
+ "errors"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+func TestRestrictedCDPHandshakeUsesRemainingStartupBudget(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ select {
+ case <-time.After(RestrictedCDPOperationTimeout + 200*time.Millisecond):
+ case <-r.Context().Done():
+ return
+ }
+ upgrader := websocket.Upgrader{}
+ connection, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ return
+ }
+ defer connection.Close()
+ _, _, _ = connection.ReadMessage()
+ }))
+ defer server.Close()
+ endpoint, _ := url.Parse("ws" + strings.TrimPrefix(server.URL, "http") + "/devtools/browser/owned")
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
+ defer cancel()
+ connection, err := dialRestrictedCDP(ctx, endpoint)
+ if err != nil {
+ t.Fatalf("handshake inside startup deadline was rejected: %v", err)
+ }
+ defer connection.Close()
+ // Canceling the completed handshake must not close the live CDP session.
+ cancel()
+ if err := connection.WriteMessage(websocket.TextMessage, []byte("ready")); err != nil {
+ t.Fatalf("completed handshake retained cancellation hook: %v", err)
+ }
+}
+
+func TestRestrictedCDPHandshakeStopsOnCancellationOrDeadline(t *testing.T) {
+ for _, earlyCancel := range []bool{false, true} {
+ t.Run(map[bool]string{false: "deadline", true: "cancellation"}[earlyCancel], func(t *testing.T) {
+ entered := make(chan struct{})
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ close(entered)
+ <-r.Context().Done()
+ }))
+ defer server.Close()
+ endpoint, _ := url.Parse("ws" + strings.TrimPrefix(server.URL, "http") + "/devtools/browser/owned")
+ budget := 100 * time.Millisecond
+ if earlyCancel {
+ budget = time.Minute
+ }
+ ctx, cancel := context.WithTimeout(t.Context(), budget)
+ defer cancel()
+ if earlyCancel {
+ go func() {
+ select {
+ case <-entered:
+ cancel()
+ case <-ctx.Done():
+ }
+ }()
+ }
+ started := time.Now()
+ connection, err := dialRestrictedCDP(ctx, endpoint)
+ if connection != nil {
+ connection.Close()
+ t.Fatal("unfinished handshake returned a connection")
+ }
+ var timeout net.Error
+ if earlyCancel && !errors.Is(err, context.Canceled) ||
+ !earlyCancel && !errors.Is(err, context.DeadlineExceeded) && !(errors.As(err, &timeout) && timeout.Timeout()) {
+ t.Fatalf("handshake lost its cancellation/timeout cause: %v", err)
+ }
+ if time.Since(started) > 3*time.Second {
+ t.Fatal("canceled handshake waited for startup budget")
+ }
+ })
+ }
+}
diff --git a/internal/browserruntime/restricted_cdp_transport.go b/internal/browserruntime/restricted_cdp_transport.go
index 12c4784e3..1a5811ee6 100644
--- a/internal/browserruntime/restricted_cdp_transport.go
+++ b/internal/browserruntime/restricted_cdp_transport.go
@@ -911,23 +911,46 @@ func dialRestrictedCDP(ctx context.Context, endpoint *url.URL) (*websocket.Conn,
return nil, ErrBrowserRuntimeBoundary
}
expectedAddress := endpoint.Host
+ // Production callers already bound startup to the session/authority
+ // deadline. Do not shorten a cold browser's handshake to the operation
+ // budget; callers without a deadline still get the usual five-second cap.
+ if _, bounded := ctx.Deadline(); !bounded {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, RestrictedCDPOperationTimeout)
+ defer cancel()
+ }
+ var stopHandshakeCancel func() bool
dialer := websocket.Dialer{
- HandshakeTimeout: RestrictedCDPOperationTimeout,
NetDialContext: func(ctx context.Context, network string,
address string,
) (net.Conn, error) {
if network != "tcp" || address != expectedAddress {
return nil, ErrBrowserRuntimeBoundary
}
- return (&net.Dialer{}).DialContext(ctx, network, address)
+ connection, err := (&net.Dialer{}).DialContext(ctx, network, address)
+ if err == nil {
+ // Gorilla applies the deadline to the HTTP upgrade, but context
+ // cancellation alone does not interrupt that socket read.
+ stopHandshakeCancel = context.AfterFunc(ctx, func() { _ = connection.Close() })
+ }
+ return connection, err
},
}
connection, response, err := dialer.DialContext(ctx, endpoint.String(), nil)
+ if stopHandshakeCancel != nil {
+ stopHandshakeCancel()
+ }
+ if ctx.Err() != nil {
+ if connection != nil {
+ _ = connection.Close()
+ }
+ err = ctx.Err()
+ }
if err != nil {
if response != nil && response.Body != nil {
_ = response.Body.Close()
}
- return nil, errors.New("connect to restricted chromium DevTools endpoint failed")
+ return nil, fmt.Errorf("connect to restricted chromium DevTools endpoint failed: %w", err)
}
remote, ok := connection.UnderlyingConn().RemoteAddr().(*net.TCPAddr)
if !ok || remote == nil || !remote.IP.IsLoopback() ||
diff --git a/internal/releasegate/brand_assets_test.go b/internal/releasegate/brand_assets_test.go
index fd6ecaf62..8f06ecd51 100644
--- a/internal/releasegate/brand_assets_test.go
+++ b/internal/releasegate/brand_assets_test.go
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
+ "image"
"image/color"
"image/png"
"io"
@@ -19,7 +20,7 @@ const approvedBrandMasterSHA256 = "a97e71e5f373d5d48ce24e39360cd390be2beabfe7310
// Generated from the approved globe-and-terminal master, including both COFF
// resources with the shared PerMonitorV2/asInvoker manifest.
-const approvedBrandDerivativeSetSHA256 = "98e5cc53e87c82c0b6557608b6a40cac40046bf91223c42d8d2af1803806068c"
+const approvedBrandDerivativeSetSHA256 = "4db9b74f26d493f64af2b6e21b39db10cdbe08fa935122eebba489f23106206f"
func TestApprovedBrandMasterAndPlatformAssetsStaySynchronized(t *testing.T) {
t.Parallel()
@@ -168,6 +169,42 @@ func TestWindowsIconContainerAndExecutableResourcesStaySynchronized(t *testing.T
}
}
+func TestSmallPlatformIconsKeepReadableArtwork(t *testing.T) {
+ t.Parallel()
+ root := filepath.Clean(filepath.Join("..", ".."))
+ for _, relative := range []string{
+ "web/public/traverse-board-favicon-32.png",
+ "packaging/windows/Assets/Square44x44Logo.targetsize-16.png",
+ "packaging/windows/Assets/Square44x44Logo.targetsize-32.png",
+ "packaging/windows/Assets/Square44x44Logo.png",
+ } {
+ t.Run(relative, func(t *testing.T) {
+ bitmap, err := png.Decode(bytes.NewReader(readTestFile(t, filepath.Join(root, relative))))
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The approved artwork has a navy ring. Its visible bounds measure
+ // legibility independently of the white tile and transparent corners.
+ var artwork image.Rectangle
+ for y := bitmap.Bounds().Min.Y; y < bitmap.Bounds().Max.Y; y++ {
+ for x := bitmap.Bounds().Min.X; x < bitmap.Bounds().Max.X; x++ {
+ pixel := color.NRGBAModel.Convert(bitmap.At(x, y)).(color.NRGBA)
+ if pixel.A >= 240 && pixel.R < 96 && pixel.G < 112 && pixel.B < 144 {
+ artwork = artwork.Union(image.Rect(x, y, x+1, y+1))
+ }
+ }
+ }
+ size := bitmap.Bounds().Dx()
+ // Round to whole pixels: 70% of a 16px icon is 11.2px.
+ minimum, maximum := size*70/100, (size*85+99)/100
+ if artwork.Dx() < minimum || artwork.Dy() < minimum ||
+ artwork.Dx() > maximum || artwork.Dy() > maximum {
+ t.Fatalf("visible artwork is %dx%d inside %dpx icon; want 70–85%% occupancy", artwork.Dx(), artwork.Dy(), size)
+ }
+ })
+ }
+}
+
func TestMacOSIconContainsCompleteRetinaSlotSet(t *testing.T) {
t.Parallel()
root := filepath.Clean(filepath.Join("..", ".."))
diff --git a/packaging/macos/LOCAL-TEST-GUIDE.txt b/packaging/macos/LOCAL-TEST-GUIDE.txt
index 8a850081f..d55eb89af 100644
--- a/packaging/macos/LOCAL-TEST-GUIDE.txt
+++ b/packaging/macos/LOCAL-TEST-GUIDE.txt
@@ -30,7 +30,7 @@ Start-Prayu-Operator-Preview.command 只是同一默认入口的兼容快捷方
Node 版本不符时脚本会在前端检查前明确报错;如果 web/dist 已经构建好,
也可以用 -SkipFrontend 跳过前端步骤。构建还需要 Xcode 命令行工具
-(codesign)和 Go 1.26.9。
+(codesign)和 Go 1.26.10。
首次接入真实模型
----------------
@@ -127,7 +127,7 @@ then run:
A mismatched Node version fails fast before the frontend checks; use
-SkipFrontend when web/dist is already built. Building also requires the
-Xcode command line tools (codesign) and Go 1.26.9.
+Xcode command line tools (codesign) and Go 1.26.10.
Connect a real model
--------------------
diff --git a/packaging/macos/TraverseBoard.icns b/packaging/macos/TraverseBoard.icns
index d8d61cf59..27c839dcd 100644
Binary files a/packaging/macos/TraverseBoard.icns and b/packaging/macos/TraverseBoard.icns differ
diff --git a/packaging/windows/Assets/Square150x150Logo.png b/packaging/windows/Assets/Square150x150Logo.png
index a9405b378..c06e970ee 100644
Binary files a/packaging/windows/Assets/Square150x150Logo.png and b/packaging/windows/Assets/Square150x150Logo.png differ
diff --git a/packaging/windows/Assets/Square150x150Logo.scale-200.png b/packaging/windows/Assets/Square150x150Logo.scale-200.png
index 7045ad936..ce3cea091 100644
Binary files a/packaging/windows/Assets/Square150x150Logo.scale-200.png and b/packaging/windows/Assets/Square150x150Logo.scale-200.png differ
diff --git a/packaging/windows/Assets/Square150x150Logo.scale-400.png b/packaging/windows/Assets/Square150x150Logo.scale-400.png
index a193ac949..9b7b9c014 100644
Binary files a/packaging/windows/Assets/Square150x150Logo.scale-400.png and b/packaging/windows/Assets/Square150x150Logo.scale-400.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.png b/packaging/windows/Assets/Square44x44Logo.png
index f9652fdcb..bff1d5b27 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.png and b/packaging/windows/Assets/Square44x44Logo.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.scale-200.png b/packaging/windows/Assets/Square44x44Logo.scale-200.png
index 770a8aa8f..c45ca9efb 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.scale-200.png and b/packaging/windows/Assets/Square44x44Logo.scale-200.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.scale-400.png b/packaging/windows/Assets/Square44x44Logo.scale-400.png
index 0b94786a8..4f77ab65e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.scale-400.png and b/packaging/windows/Assets/Square44x44Logo.scale-400.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-16.png b/packaging/windows/Assets/Square44x44Logo.targetsize-16.png
index b96d9cae5..c68fe23f7 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-16.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-16.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-lightunplated.png
index b96d9cae5..c68fe23f7 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-unplated.png
index b96d9cae5..c68fe23f7 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-16_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-20.png b/packaging/windows/Assets/Square44x44Logo.targetsize-20.png
index 73ceaab06..546d3246f 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-20.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-20.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-lightunplated.png
index 73ceaab06..546d3246f 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-unplated.png
index 73ceaab06..546d3246f 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-20_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-24.png b/packaging/windows/Assets/Square44x44Logo.targetsize-24.png
index eb0ab1c29..5eb5c4ecd 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-24.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-24.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-lightunplated.png
index eb0ab1c29..5eb5c4ecd 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
index eb0ab1c29..5eb5c4ecd 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-256.png b/packaging/windows/Assets/Square44x44Logo.targetsize-256.png
index bdf26b9a3..e863af34f 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-256.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-256.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-lightunplated.png
index bdf26b9a3..e863af34f 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-unplated.png
index bdf26b9a3..e863af34f 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-256_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-30.png b/packaging/windows/Assets/Square44x44Logo.targetsize-30.png
index 21913756e..293b814f2 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-30.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-30.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-lightunplated.png
index 21913756e..293b814f2 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-unplated.png
index 21913756e..293b814f2 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-30_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-32.png b/packaging/windows/Assets/Square44x44Logo.targetsize-32.png
index f56ffa2a2..b4acb6fe8 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-32.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-32.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-lightunplated.png
index f56ffa2a2..b4acb6fe8 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-unplated.png
index f56ffa2a2..b4acb6fe8 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-32_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-36.png b/packaging/windows/Assets/Square44x44Logo.targetsize-36.png
index 82d65896d..08caddf4e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-36.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-36.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-lightunplated.png
index 82d65896d..08caddf4e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-unplated.png
index 82d65896d..08caddf4e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-36_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-40.png b/packaging/windows/Assets/Square44x44Logo.targetsize-40.png
index 68c938a5b..7c1077736 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-40.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-40.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-lightunplated.png
index 68c938a5b..7c1077736 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-unplated.png
index 68c938a5b..7c1077736 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-40_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-48.png b/packaging/windows/Assets/Square44x44Logo.targetsize-48.png
index 8e3917fc0..caf3291e1 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-48.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-48.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-lightunplated.png
index 8e3917fc0..caf3291e1 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-unplated.png
index 8e3917fc0..caf3291e1 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-48_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-60.png b/packaging/windows/Assets/Square44x44Logo.targetsize-60.png
index 1835457b1..c1413a40e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-60.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-60.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-lightunplated.png
index 1835457b1..c1413a40e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-unplated.png
index 1835457b1..c1413a40e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-60_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-64.png b/packaging/windows/Assets/Square44x44Logo.targetsize-64.png
index b80785568..2a6319ce3 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-64.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-64.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-lightunplated.png
index b80785568..2a6319ce3 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-unplated.png
index b80785568..2a6319ce3 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-64_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-72.png b/packaging/windows/Assets/Square44x44Logo.targetsize-72.png
index 6c15881ab..bb396f59e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-72.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-72.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-lightunplated.png
index 6c15881ab..bb396f59e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-unplated.png
index 6c15881ab..bb396f59e 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-72_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-80.png b/packaging/windows/Assets/Square44x44Logo.targetsize-80.png
index e47c23d1b..7493e6e5a 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-80.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-80.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-lightunplated.png
index e47c23d1b..7493e6e5a 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-unplated.png
index e47c23d1b..7493e6e5a 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-80_altform-unplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-96.png b/packaging/windows/Assets/Square44x44Logo.targetsize-96.png
index f1092787b..c53247ee1 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-96.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-96.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-lightunplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-lightunplated.png
index f1092787b..c53247ee1 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-lightunplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-lightunplated.png differ
diff --git a/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-unplated.png b/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-unplated.png
index f1092787b..c53247ee1 100644
Binary files a/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-unplated.png and b/packaging/windows/Assets/Square44x44Logo.targetsize-96_altform-unplated.png differ
diff --git a/packaging/windows/Assets/StoreLogo.png b/packaging/windows/Assets/StoreLogo.png
index 9b813aa51..a05b4486c 100644
Binary files a/packaging/windows/Assets/StoreLogo.png and b/packaging/windows/Assets/StoreLogo.png differ
diff --git a/packaging/windows/Assets/StoreLogo.scale-125.png b/packaging/windows/Assets/StoreLogo.scale-125.png
index d37f27d86..49f631c2e 100644
Binary files a/packaging/windows/Assets/StoreLogo.scale-125.png and b/packaging/windows/Assets/StoreLogo.scale-125.png differ
diff --git a/packaging/windows/Assets/StoreLogo.scale-150.png b/packaging/windows/Assets/StoreLogo.scale-150.png
index 85b8a1322..7060f87f5 100644
Binary files a/packaging/windows/Assets/StoreLogo.scale-150.png and b/packaging/windows/Assets/StoreLogo.scale-150.png differ
diff --git a/packaging/windows/Assets/StoreLogo.scale-200.png b/packaging/windows/Assets/StoreLogo.scale-200.png
index af9f4dcca..b03c50cfa 100644
Binary files a/packaging/windows/Assets/StoreLogo.scale-200.png and b/packaging/windows/Assets/StoreLogo.scale-200.png differ
diff --git a/packaging/windows/Assets/StoreLogo.scale-400.png b/packaging/windows/Assets/StoreLogo.scale-400.png
index 34cba68ab..e9e921ec9 100644
Binary files a/packaging/windows/Assets/StoreLogo.scale-400.png and b/packaging/windows/Assets/StoreLogo.scale-400.png differ
diff --git a/packaging/windows/StoreListingIcon.png b/packaging/windows/StoreListingIcon.png
index 7045ad936..ce3cea091 100644
Binary files a/packaging/windows/StoreListingIcon.png and b/packaging/windows/StoreListingIcon.png differ
diff --git a/packaging/windows/TraverseBoard.ico b/packaging/windows/TraverseBoard.ico
index c124bc4fc..19d4108b9 100644
Binary files a/packaging/windows/TraverseBoard.ico and b/packaging/windows/TraverseBoard.ico differ
diff --git a/scripts/generate-brand-assets.ps1 b/scripts/generate-brand-assets.ps1
index 103ebed73..2cd59ab97 100644
--- a/scripts/generate-brand-assets.ps1
+++ b/scripts/generate-brand-assets.ps1
@@ -76,7 +76,17 @@ function New-PlatformIconBitmap {
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
$clip = New-RoundedRectanglePath -Size $workingSize
$graphics.SetClip($clip)
- $graphics.DrawImage($Source, 0, 0, $workingSize, $workingSize)
+ # The master includes presentation padding: its navy mark occupies only
+ # 45% of the canvas. Use the central 60% for platform icons so the same
+ # artwork occupies about 75%, while keeping a clear inset on every side.
+ $sourceInset = [single]($Source.Width * 0.20)
+ $sourceBounds = [System.Drawing.RectangleF]::new(
+ $sourceInset, $sourceInset,
+ [single]($Source.Width - 2 * $sourceInset),
+ [single]($Source.Height - 2 * $sourceInset)
+ )
+ $targetBounds = [System.Drawing.RectangleF]::new(0, 0, $workingSize, $workingSize)
+ $graphics.DrawImage($Source, $targetBounds, $sourceBounds, [System.Drawing.GraphicsUnit]::Pixel)
} finally {
if ($null -ne $clip) {
$clip.Dispose()
diff --git a/scripts/package-desktop-dmg.py b/scripts/package-desktop-dmg.py
index 623d9ea72..36cb7c103 100644
--- a/scripts/package-desktop-dmg.py
+++ b/scripts/package-desktop-dmg.py
@@ -14,6 +14,7 @@
import subprocess
import sys
import tempfile
+import time
# Importing the ZIP verifier must not add scripts/__pycache__ to an otherwise
@@ -60,6 +61,31 @@ def detach(mountpoint):
release.require(result.returncode == 0, "could not detach the temporary DMG mount")
+def create_image(stage, image, volume):
+ command = [HDIUTIL, "create", "-srcfolder", str(stage), "-volname", volume,
+ "-fs", "HFS+", "-format", "UDZO", str(image)]
+ for attempt in range(3):
+ # hdiutil create reports EBUSY as exit 1, shared with permanent errors.
+ # Keep its diagnostic and retry only the explicit transient failure.
+ # Use a file, not a pipe inherited by diskimages-helper.
+ with tempfile.TemporaryFile(mode="w+t", encoding="utf-8") as log:
+ result = subprocess.run(command, stdout=log, stderr=subprocess.STDOUT,
+ env=dict(os.environ, LC_ALL="C"), timeout=600)
+ log.seek(0)
+ diagnostic = log.read()
+ print(diagnostic, end="", flush=True)
+ if result.returncode == 0:
+ return
+ if result.returncode != 1 or "Resource busy" not in diagnostic or attempt == 2:
+ result.check_returncode()
+ # This image is inside this invocation's private staging directory;
+ # a failed attempt may leave a partial file that cannot be reused.
+ image.unlink(missing_ok=True)
+ delay = 2 ** (attempt + 1)
+ print("macos_dmg: resource busy; retrying creation in %ss" % delay, flush=True)
+ time.sleep(delay)
+
+
def package(version, revision, arch, source, output):
release.require(sys.platform == "darwin", "DMG packaging requires macOS")
source = source.resolve()
@@ -90,9 +116,7 @@ def package(version, revision, arch, source, output):
subprocess.run(["/usr/bin/ditto", "-x", "-k", str(source / archive_name), str(stage)], check=True)
verify_payload(stage, manifest["files"], exact=True)
(stage / "Applications").symlink_to("/Applications")
- subprocess.run([HDIUTIL, "create", "-srcfolder", str(stage), "-volname",
- "Universal-Code " + version + " " + arch, "-fs", "HFS+",
- "-format", "UDZO", str(temporary_image)], check=True, timeout=600)
+ create_image(stage, temporary_image, "Universal-Code " + version + " " + arch)
subprocess.run([HDIUTIL, "verify", str(temporary_image)], check=True, timeout=120)
attached = subprocess.run([HDIUTIL, "attach", "-readonly", "-nobrowse", "-noautoopen",
"-mountpoint", str(mountpoint), "-plist", str(temporary_image)],
diff --git a/scripts/test_macos_dmg.py b/scripts/test_macos_dmg.py
index 0c416e1ba..46dff3449 100644
--- a/scripts/test_macos_dmg.py
+++ b/scripts/test_macos_dmg.py
@@ -137,6 +137,51 @@ def test_detach_retries_owned_mount_on_failure_or_timeout(self):
self.assertEqual(run.call_args_list[1].args[0],
[dmg.HDIUTIL, "detach", "-force", str(self.root / "mounted")])
+ def test_busy_create_retries_after_removing_partial_image(self):
+ calls = 0
+ image = self.root / "private.dmg"
+
+ def busy_once(command, **kwargs):
+ nonlocal calls
+ calls += 1
+ self.assertEqual(command[:2], [dmg.HDIUTIL, "create"])
+ self.assertFalse(image.exists())
+ if calls == 1:
+ image.write_bytes(b"incomplete")
+ kwargs["stdout"].write("hdiutil: create failed - Resource busy\n")
+ return subprocess.CompletedProcess(command, 1)
+ image.write_bytes(b"disk-image-fixture")
+ return subprocess.CompletedProcess(command, 0)
+
+ with mock.patch.object(dmg.time, "sleep") as sleep, \
+ mock.patch.object(dmg.subprocess, "run", side_effect=busy_once):
+ dmg.create_image(self.payload, image, "Preview")
+ self.assertEqual(calls, 2)
+ sleep.assert_called_once_with(2)
+ self.assertEqual(image.read_bytes(), b"disk-image-fixture")
+
+ def test_create_stops_on_permanent_error_or_exhausted_busy_retries(self):
+ for diagnostic, expected_attempts in (("Permission denied", 1), ("Resource busy", 3)):
+ with self.subTest(diagnostic=diagnostic):
+ def fail_create(command, **kwargs):
+ kwargs["stdout"].write("hdiutil: create failed - " + diagnostic + "\n")
+ return subprocess.CompletedProcess(command, 1)
+
+ with mock.patch.object(dmg.time, "sleep") as sleep, \
+ mock.patch.object(dmg.subprocess, "run", side_effect=fail_create) as run:
+ with self.assertRaises(subprocess.CalledProcessError):
+ dmg.create_image(self.payload, self.root / "private.dmg", "Preview")
+ self.assertEqual(run.call_count, expected_attempts)
+ self.assertEqual(sleep.call_count, expected_attempts - 1)
+
+ def test_timed_out_create_is_not_retried(self):
+ with mock.patch.object(dmg.time, "sleep") as sleep, \
+ mock.patch.object(dmg.subprocess, "run", side_effect=subprocess.TimeoutExpired([], 600)) as run:
+ with self.assertRaises(subprocess.TimeoutExpired):
+ dmg.create_image(self.payload, self.root / "private.dmg", "Preview")
+ sleep.assert_not_called()
+ self.assertEqual(run.call_count, 1)
+
if __name__ == "__main__":
unittest.main()
diff --git a/web/README.md b/web/README.md
index e92b939ac..59f92a93f 100644
--- a/web/README.md
+++ b/web/README.md
@@ -1,12 +1,12 @@
# Universal-Code Web Console
-本目录包含 **Universal-Code** 的前端桌面与 Web 用户界面,基于 React 18、TypeScript 与 Vite 构建。
+本目录包含 **Universal-Code** 的前端桌面与 Web 用户界面,基于 React 19、TypeScript 与 Vite 构建。
---
## 技术栈 (Tech Stack)
-- **核心框架**:React 18 + TypeScript + Vite
+- **核心框架**:React 19 + TypeScript + Vite
- **代码与 Diff 编辑器**:Monaco Editor (本地离线打包,支持按需加载与 Proposal 恢复)
- **视觉与样式系统**:纯 CSS 苹果液态玻璃(Apple Liquid Glass Acrylic)+ Tailwind CSS 工具类
- **测试框架**:Vitest + React Testing Library
@@ -46,7 +46,7 @@ cd ..
go run ./cmd/cyberagent api serve --listen 127.0.0.1:8765 --ui-dir web/dist
```
-访问 `http://127.0.0.1:8765` 即可使用。如配置了 API Token,需在首次加载时输入对应凭证。
+访问 `http://127.0.0.1:8765`,首次连接时输入后端打印的临时只读 token,或 `CYBERAGENT_API_TOKEN` 配置的 token。控制操作还需 control token 与对应 capability,见 [HTTP API](../docs/http-api.md)。
---
@@ -57,9 +57,9 @@ go run ./cmd/cyberagent api serve --listen 127.0.0.1:8765 --ui-dir web/dist
| **会话舞台 (Stage)** | `src/v2/components/conversation.tsx` | 连续任务对话流、消息卡片、工具调用状态呈现与响应流式打字机。 |
| **伴侣停靠坞 (Companion Dock)** | `src/v2/styles.css` (`.v2-companion-dock`) | 智能对齐右侧的工作流面板,支持检查点、代码审查与工具箱原生平移停靠。 |
| **任务输入舱 (Composer)** | `src/v2/components/composer.tsx` | 任务指令输入、文件/图片附件上传、模型选择器与权限档位切换。 |
-| **代码 Diff 与编辑** | `src/components/unified-diff.tsx` / `monaco/` | 逐行差异对比、待审提案(Proposal)接受/驳回与本地 Monaco 编辑。 |
-| **网络检索存证** | `src/components/safe-markdown.tsx` / `evidence/` | 网页快照渲染、GitHub/HN/RSS 来源标注与点击溯源。 |
-| **系统设置与外观** | `src/v2/components/provider-settings.tsx` | 多模型凭据配置、系统/浅色/深色/雾透磨砂玻璃外观实时切换。 |
+| **代码 Diff 与编辑** | `src/components/unified-diff.ts` / `src/lib/monaco-local.ts` | 逐行差异对比、待审提案(Proposal)接受/驳回与本地 Monaco 编辑。 |
+| **网页证据来源** | `src/components/thread-transcript.tsx` / `src/v2/components/activity-detail.tsx` | 展示网页来源、URL、获取时间、摘要标识以及可引用、部分内容和过期状态。 |
+| **系统设置与外观** | `src/v2/components/settings.tsx` / `src/v2/components/provider-settings.tsx` | 多模型凭据配置、系统/浅色/深色/雾透磨砂玻璃外观实时切换。 |
---
@@ -99,15 +99,14 @@ web/
## 纯 CSS 视觉与设计规范 (Design Language)
-- **纯 CSS 物理光学折射**:采用由 GreyD097 纯 CSS 方案启发的双向对角内阴影 (`inset 1.5px -1.5px` 与 `inset -1.5px 1.5px`),在按钮和面板边缘模拟凸透镜全反射效果。
-- **环境底光网格**:注入多点离散光场 (`--prayu-ambient-radial`) 搭配 `backdrop-filter: blur(34px) saturate(190%) contrast(102%)`,确保在深色模式下呈现透亮亚克力质感,杜绝普通黑底导致的色彩发灰坍塌。
-- **极致渲染性能**:绝不使用高开销的 SVG 滤镜(`feTurbulence`)或全屏 WebGL Canvas,确保承载数千行代码与终端滚动输出时维持 120fps 丝滑帧率。
+- **主题样式**:使用 CSS 渐变、半透明背景、`backdrop-filter` 和边缘阴影呈现各主题的视觉效果,修改应限定在对应主题内。
+- **实际页面验证**:检查文字清晰度、长内容滚动、窄屏布局和交互状态;性能结论应记录测量环境与结果。
---
## English Overview
-Universal-Code Web Console is the desktop and web operator interface for Universal-Code, built with **React 18**, **TypeScript**, and **Vite**.
+Universal-Code Web Console is the desktop and web operator interface for Universal-Code, built with **React 19**, **TypeScript**, and **Vite**.
### Quick Start
1. Run backend: `go run ./cmd/cyberagent api serve --listen 127.0.0.1:8765`
@@ -117,4 +116,4 @@ Universal-Code Web Console is the desktop and web operator interface for Univers
### Key Features
- **Fluid Companion Dock**: A docked assistant panel alongside the main conversation stage without overlapping content.
- **Monaco Diff Viewer**: Line-by-line inspection and auditable rollback.
-- **Pure CSS Apple Liquid Glass**: Acrylic blur and specular rim sheen with zero WebGL/SVG performance overhead.
+- **CSS Themes**: Translucent surfaces, backdrop blur, gradients, and edge shadows.
diff --git a/web/e2e/agent-browser.spec.ts b/web/e2e/agent-browser.spec.ts
index cee845ef8..f376964b0 100644
--- a/web/e2e/agent-browser.spec.ts
+++ b/web/e2e/agent-browser.spec.ts
@@ -55,7 +55,8 @@ test("native Agent Browser uses the production API and visible workbench", async
} else {
await expect.poll(async () => {
const status = await readStatus();
- return status.state === "failed" || status.last_action === "browser_screenshot";
+ return status.state === "failed" || status.unavailable_code === "run_unavailable"
+ || status.last_action === "browser_screenshot";
}, { timeout: nativeMac ? 240_000 : 60_000 }).toBe(true);
expect(await readStatus(), "browser journey must reach screenshot capture")
.toMatchObject({ last_action: "browser_screenshot" });
diff --git a/web/public/apple-touch-icon.png b/web/public/apple-touch-icon.png
index 74dcb89a2..177a54c84 100644
Binary files a/web/public/apple-touch-icon.png and b/web/public/apple-touch-icon.png differ
diff --git a/web/public/traverse-board-favicon-32.png b/web/public/traverse-board-favicon-32.png
index f56ffa2a2..b4acb6fe8 100644
Binary files a/web/public/traverse-board-favicon-32.png and b/web/public/traverse-board-favicon-32.png differ
diff --git a/web/src/assets/traverse-board-mark.png b/web/src/assets/traverse-board-mark.png
index 383fcd7a7..00b372b82 100644
Binary files a/web/src/assets/traverse-board-mark.png and b/web/src/assets/traverse-board-mark.png differ