Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/fast-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,17 @@ jobs:
with:
node-version: 24
cache: npm
cache-dependency-path: |
package-lock.json
website/package-lock.json

- name: Install dependencies
run: npm ci

- name: Install website dependencies
working-directory: website
run: npm ci

- name: Run WebUI and script tests
run: npm run test:webui

Expand Down
4 changes: 4 additions & 0 deletions docs/changelog/en.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

### [Unreleased] Unreleased

- Fast Checks now installs website dependencies so download-page tests can run on clean CI runners.
- Release tests now use a local calendar date so changelog checks pass in both UTC CI and developer time zones.
- Windows build-argument tests use their own version fixture so normal release version bumps no longer break CI.
- Set a custom model ID for the experimental Jev plugin in Agent settings; leave it blank to keep using `jev-latest` for Agent and MCP browser tasks.
- Allow up to ten minutes to verify uploaded desktop release files so slower downloads do not abort publication after a successful upload.
- The website starts downloads automatically only on macOS; other devices show a manual download page with the configured platform installers instead of downloading a potentially incompatible file.

Expand Down
4 changes: 4 additions & 0 deletions docs/changelog/zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

### [Unreleased] 未发布

- Fast Checks 现在会安装官网依赖,下载页测试可在全新的 CI 环境中正常运行。
- 发布测试改用本地日历日期,更新日志检查在 UTC 的 CI 环境和开发者本地时区均可通过。
- Windows 构建参数测试使用独立的版本数据,正常升级发布版本不再导致 CI 失败。
- 可在 Agent 设置中为实验性 Jev 插件填写自定义模型 ID;留空时,Agent 和 MCP 浏览器任务继续使用 `jev-latest`。
- 桌面发布文件上传后的校验最多等待十分钟,避免下载较慢时,上传成功却因校验超时中断发布。
- 官网仅在 macOS 上自动开始下载;其他设备显示手动下载页面,可选择已配置的各平台安装包,避免自动下载不适用的文件。

Expand Down
2 changes: 1 addition & 1 deletion docs/feature-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ Flagship feature. C++ services + `dao://dao-agent` WebUI + vendor runtime.
| ☐ | Agent WebUI host allowed to make network requests (LLM API) | `webui/chrome_web_ui_controller_factory.cc.patch` (`origin.host()=="agent"`) | 🟢 | `dao://agent` reaches external LLM endpoints |
| ☐ | Named, collapsible Agent tool calls | `resources/agent/dao_tool_renderer.ts`, `resources/agent/agent.css` | 🟢 | Run `dao_tool_renderer.test.ts`; verify regular, search, and fetch calls always show the stable tool name, keep parameters/output collapsed by default, and expand when requested |
| ☐ | Unified Profile-scoped Agent settings and legacy migration | `src/dao/.../agent/dao_agent_settings_handler.{h,cc}`, `resources/agent/agent_settings_{sync,native_bridge}.ts`, `resources/settings/dao_page/dao_agent_page*.patch`, `webui/settings/settings_ui.cc.patch` | 🟡 | Run `agent_settings_sync.test.ts`, `dao_agent_app.test.ts`, `DaoAgentPage`, and `DaoAgentSettingsHandlerTest`; verify legacy `dao://agent` local-storage values migrate once without overwriting Settings values, partial usage dictionaries receive validated defaults and derived totals, malformed/non-finite fields fail closed, canonical stored snapshots still require the complete schema, both WebUIs receive `dao-agent-settings-changed`, rapid tool toggles serialize cumulative disabled-tool arrays and resync after failure, the resume window defaults to 3 and accepts 0, and runtime-only state is not migrated. With no active Agent turn, the gear opens exactly one foreground `dao://settings/agent` tab through its fixed native command; success closes the sidebar and failure leaves it open |
| ☐ | Experimental shared Jev browser subtask plugin | `agent/dao_agent_plugins.h`, `resources/agent/agent_plugins.ts`, `automation/dao_jev_task.cc`, `automation/dao_page_tools.cc`, Settings Agent page patches | 🟡 | Run `jev.test.ts`, `compact_snapshot.test.ts`, `browser_tool_catalog.test.ts`, Agent bridge/settings/adapter tests, `npm run rebuild`, and `node scripts/checks/jev-plugin.mjs`; verify both switches default off, masked credentials and independent persistence, no settings-triggered requests, invalid configuration unavailable, underlying permissions enforced, no editable drafts or nested controls in compact snapshots (empty/true/plaintext-only, including uppercase), choices stay within 255 options at 150 fields and 20 inputs without dropping candidates, native input/ARIA button names and matching action guards, atomic refs and locally verified completion, fast/delayed form navigation without duplicate submissions, obsolete decisions discarded after navigation, task deadlines while navigation is pending, bounded recovery/no-progress/budget errors with partial evidence, cancellation on stop/config changes/target loss, rejection of late responses and credentialed redirects, reloading the Agent during a pending native wait preserves the browser and allows a new turn, MCP discovery hides `run_browser_task` when disabled/unconfigured and includes it only with valid configuration and permission, without requests or approval prompts, approved MCP tasks run with the Agent panel closed, MCP cancellation and revocation abort native requests, partial failures preserve structured progress, exact target remains pinned across focus changes, ordinary tools unaffected when unregistered, English/Chinese labels and keyboard access. With a configured compatible service, compare bilingual representative tasks against the ordinary Agent before claiming a speedup. |
| ☐ | Experimental shared Jev browser subtask plugin | `agent/dao_agent_plugins.h`, `resources/agent/agent_plugins.ts`, `automation/dao_jev_task.cc`, `automation/dao_page_tools.cc`, Settings Agent page patches | 🟡 | Run `jev.test.ts`, `compact_snapshot.test.ts`, `browser_tool_catalog.test.ts`, Agent bridge/settings/adapter tests, `npm run rebuild`, and `node scripts/checks/jev-plugin.mjs`; verify both switches default off, masked credentials and independent persistence, custom Jev model IDs surviving restart and reaching Agent/MCP requests with surrounding whitespace trimmed, empty model IDs falling back to `jev-latest`, model changes cancelling pending requests, no settings-triggered requests, invalid configuration unavailable, underlying permissions enforced, no editable drafts or nested controls in compact snapshots (empty/true/plaintext-only, including uppercase), choices stay within 255 options at 150 fields and 20 inputs without dropping candidates, native input/ARIA button names and matching action guards, atomic refs and locally verified completion, fast/delayed form navigation without duplicate submissions, obsolete decisions discarded after navigation, task deadlines while navigation is pending, bounded recovery/no-progress/budget errors with partial evidence, cancellation on stop/config changes/target loss, rejection of late responses and credentialed redirects, reloading the Agent during a pending native wait preserves the browser and allows a new turn, MCP discovery hides `run_browser_task` when disabled/unconfigured and includes it only with valid configuration and permission, without requests or approval prompts, approved MCP tasks run with the Agent panel closed, MCP cancellation and revocation abort native requests, partial failures preserve structured progress, exact target remains pinned across focus changes, ordinary tools unaffected when unregistered, English/Chinese labels and keyboard access. With a configured compatible service, compare bilingual representative tasks against the ordinary Agent before claiming a speedup. |
| ☐ | Settings Agent management summaries and limited native facade | `src/dao/.../agent/dao_agent_settings_handler.{h,cc}`, `resources/settings/dao_page/dao_agent_page*.patch`, `resources/agent/agent_settings_native_bridge.ts` | 🟡 | Verify legacy `dao_agent_stats` migration preserves existing counters once; persona reset removes the override and restores the runtime default; manual Dream generation remains disabled until Memory and Dream analysis are enabled, then reports success and failure without duplicate submissions; a Settings-side usage reset updates an already-open Agent WebUI; Memory clear requires confirmation and refreshes aggregate counts; Workspace reveal works without registering workspace mutation messages; configuration loading/error/retry is independent, including when `getSettings()` fails; Memory, Workspace, and Usage remain visible, independently loadable inset cards with 16px desktop spacing, 12px narrow spacing, compact rows, the shared content inset, and visible keyboard focus in light and dark themes; and the existing Skills, Memory, and Dream secondary links still open |
| ☐ | 7 Dao WebUI configs registered (agent, dream, index, memory, sidebar, skills, welcome) | `webui/chrome_web_ui_configs.cc.patch` | 🟡 | All 7 pages load; confirm upstream `SkillsUIConfig` still exists (dao takes over `dao://skills`) |
| ☐ | Agent tab helpers, guarded cursor overlay, lock banner | `ui/tab_helpers.cc.patch` + `src/dao/.../agent/`, `dao_agent_cursor_view.*`, `dao_agent_*_view.*` | 🟢 | Run `DaoAgentCursorViewBrowserTest.*` and the focused cursor cases in `DaoMcpPageToolsBrowserTest`; verify the cursor uses a black fill, white outline, blue glow, direction-aware tilt/compression, damped arrival wobble, and the existing Dao ripple; verify visuals render only for the pinned target when it is the active tab of a visible, non-minimized, active window, background moves succeed without visuals, background clicks skip animation and remain pinned, changing tabs during a move cannot leak a ripple, short foreground moves are direct, long moves are bounded curves, and hiding a move completes its callback |
Expand Down
8 changes: 5 additions & 3 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,10 @@ The stack includes: **LLM tool calling**, **long-term memory** (SQLite + FTS5),
**Unified Agent settings** (`dao://settings/agent`, overview entry `#agent`)
- The optional bundled Jev plugin has an experimental, default-off connection
checkbox after the main provider settings and a separate default-off global
tool permission. Its full HTTP(S) API URL and masked Bearer token persist in
Profile settings independently of the main model. Opening or editing settings
tool permission. Its full HTTP(S) API URL, masked Bearer token and optional
model ID persist in Profile settings independently of the main model. The model
ID is trimmed for requests; empty or whitespace-only values use `jev-latest`.
Opening or editing settings
sends no inference request. Invalid or incomplete configuration is unavailable.
MCP discovery lists `run_browser_task` only when the connection and tool
permission are enabled with valid configuration; clients must refresh the tool
Expand All @@ -493,7 +495,7 @@ The stack includes: **LLM tool calling**, **long-term memory** (SQLite + FTS5),
through a shared native executor available to Dao Agent and external MCP.
Agent calls retain the existing turn and sequential scheduler; MCP calls
retain connection approval and the exact authorized tab lease, and work with
the Agent panel closed. Both enforce browser permissions. Jev uses the `jev-latest` choice
the Agent panel closed. Both enforce browser permissions. Jev uses the choice
protocol, selects fill targets and known inputs separately to keep every choice
below the service's 255-option limit, accepts only offered actions and known
non-sensitive inputs, and verifies all completion predicates locally. Compact viewport snapshots and
Expand Down
24 changes: 21 additions & 3 deletions scripts/checks/jev-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const profile = await realpath(await mkdtemp(join(tmpdir(), 'dao-jev-check-')));
const sockets = [];
let child, helper, port, stderr = '', mode = 'normal', requests = 0, disconnected = 0;
let pendingResponse, pendingBody, redirectedRequests = 0, submissions = 0, navigationDelay = 0;
let expectedModel = 'custom-jev-model';
const choice = (question, selected) => ({choice: selected, confidence: 1,
probabilities: Object.fromEntries(Object.keys(question.criteria).map(key => [key, Number(key === selected)]))});
function normalAnswer(body) {
Expand Down Expand Up @@ -54,7 +55,7 @@ const server = createServer(async (req, res) => {
assert(!raw.includes('local-test-token'), 'Token is excluded from model input');
assert(!raw.includes('private-editor-'), 'Editable drafts and nested controls are excluded');
const body = JSON.parse(raw);
assert.equal(body.model, 'jev-latest');
assert.equal(body.model, expectedModel);
for (const question of Object.values(body.questions)) {
assert(Object.keys(question.criteria).length <= 255, 'Every choice respects the service limit');
}
Expand Down Expand Up @@ -123,6 +124,7 @@ const targets = async () => (await fetch(`http://127.0.0.1:${port}/json/list`)).
const enabled = '[data-setting="dao_plugin_jev_enabled"]';
const url = '[data-setting="dao_plugin_jev_url"]';
const token = '[data-setting="dao_plugin_jev_token"]';
const model = '[data-setting="dao_plugin_jev_model"]';
const permission = '[data-setting="dao_plugin_jev_permission"]';
async function start() {
await rm(join(profile, 'DevToolsActivePort'), {force: true});
Expand Down Expand Up @@ -217,6 +219,9 @@ try {
};
await setField(url, `${origin}/jev`);
await setField(token, 'local-test-token');
assert.equal(await ui(`return find('${model}').value;`), '');
assert.equal(await ui(`return find('${model}').placeholder;`), 'jev-latest');
await setField(model, ' custom-jev-model ');
await until(() => ui(`return !find('${permission}').disabled;`), 'valid connection');
assert.equal(await ui(`return find('${token}').type;`), 'password');
assert.equal(await ui(`return find('${permission}').checked;`), false);
Expand All @@ -239,6 +244,7 @@ try {
await until(() => ui(`return find('settings-dao-agent-page').agentPlugins_[0].effective;`), 'restart persistence');
assert.equal(await ui(`return find('${url}').value;`), `${origin}/jev`);
assert.equal(await ui(`return find('${token}').value;`), 'local-test-token');
assert.equal(await ui(`return find('${model}').value;`), ' custom-jev-model ');
assert.equal(requests, 0);
console.log('PASS settings: defaults, expansion, validation, masking, two badges, explicit permission, collapse and restart persistence; zero inference requests');

Expand Down Expand Up @@ -413,6 +419,14 @@ try {
await until(() => evaluate(formSend, `!!document.querySelector('input') && !document.querySelector('input').value`), 'fresh MCP form');
};
const runMcp = (args = task) => mcp.call('tools/call', {name: 'run_browser_task', arguments: args});
for (const value of ['', ' ', ' another-jev-model ']) {
await resetForm();
expectedModel = value.trim() || 'jev-latest';
await setField(model, value);
const result = await runMcp().result;
assert.equal(result.structuredContent.status, 'completed', JSON.stringify(result));
}
console.log('PASS custom model IDs reach Agent and MCP requests; empty or whitespace-only IDs use jev-latest');
for (let i = 0; i < 3; i++) {
await checkNavigation(async args => (await runMcp(args).result).structuredContent, i);
}
Expand Down Expand Up @@ -473,17 +487,21 @@ try {
assert.equal(bounded.structuredContent.status, 'step_limit');
console.log('PASS native argument bounds and split field/input choices remain within the Jev choice limit');

for (const cancellation of ['timeout', 'client', 'permission']) {
for (const cancellation of ['timeout', 'client', 'model', 'permission']) {
await resetForm(); mode = 'hold'; pendingResponse = null;
const beforeDisconnect = disconnected;
const running = runMcp({...task, timeout_ms: cancellation === 'timeout' ? 1000 : 60000});
await until(() => pendingResponse, 'pending external Jev request');
if (cancellation === 'client') mcp.notify('notifications/cancelled', {requestId: running.id, reason: 'Integration cancellation'});
if (cancellation === 'model') {
expectedModel = 'changed-jev-model';
await setField(model, expectedModel);
}
if (cancellation === 'permission') await ui(`find('${permission}').click();`);
// MCP cancellation deliberately suppresses the cancelled request's reply.
if (cancellation !== 'client') {
const result = await running.result;
const expected = cancellation === 'permission' ? 'configuration_changed' : 'timeout';
const expected = cancellation === 'timeout' ? 'timeout' : 'configuration_changed';
assert.equal(result.structuredContent.status, expected, JSON.stringify(result));
assert.equal(result.isError, true);
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/commands/__tests__/release.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function releaseFixture(): ReleaseFixture {
CLOUDFLARE_API_TOKEN: 'token',
R2_BUCKET: 'releases',
},
now: () => new Date('2026-07-12T00:00:00+08:00'),
now: () => new Date(2026, 6, 12),
head: () => 'a'.repeat(40),
tagState: () => ({exists: false}),
createTag: (tagName, commit) => {
Expand Down
11 changes: 11 additions & 0 deletions scripts/commands/__tests__/settings_redesign_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ function preservedContractToken(line: string): string | null {
}

describe("settings continuous overview contract", () => {
it("provides an optional model ID field for bundled Agent plugins", () => {
const html = addedPayload(readPatch("dao_page/dao_agent_page.html.patch"));
const modelInput = html.match(/<cr-input\b[^>]*data-setting\$="\[\[plugin\.modelKey\]\]"[^>]*>/)?.[0];
expect(modelInput).toBeDefined();
expect(modelInput).toContain('label="$i18n{daoAgentModelLabel}"');
expect(modelInput).toContain('value="[[plugin.model]]"');
expect(modelInput).toContain('placeholder="[[plugin.defaultModel]]"');
expect(modelInput).toContain('on-input="onAgentTextSettingChange_"');
expect(modelInput).not.toContain('required');
});

it("provides an integrated table of contents and overview controller", () => {
const requiredPatches = [
"settings_ui/settings_ui.html.patch",
Expand Down
3 changes: 2 additions & 1 deletion scripts/commands/__tests__/windows_development.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ describe('native development target', () => {

it('keeps Windows lld and macOS development linker behavior', () => {
const config = utils.loadConfig();
config.version.display = '2.3.4';
const common = readFileSync(path.join(utils.CONFIGS_DIR, 'common.gn'), 'utf8');
const args = build.createBuildArgs(
config, {os: 'win', cpu: 'x64'}, true, common, 'use_lld = true\n');
Expand All @@ -76,7 +77,7 @@ describe('native development target', () => {
expect(args).not.toContain('use_lld = false');
expect(args).toContain('is_component_build = true');
expect(args).toContain('is_official_build = false');
expect(args).toContain('dao_display_version = "1.0.108.0"');
expect(args).toContain('dao_display_version = "2.3.4.0"');
expect(build.createBuildArgs(
config, {os: 'mac', cpu: 'arm64'}, true, common, 'use_lld = true\n'))
.toContain('use_lld = false');
Expand Down
Loading
Loading