diff --git a/.agents/skills/dsh-prose-standard/SKILL.md b/.agents/skills/dsh-prose-standard/SKILL.md index bb8347823b..338ff29a21 100644 --- a/.agents/skills/dsh-prose-standard/SKILL.md +++ b/.agents/skills/dsh-prose-standard/SKILL.md @@ -55,7 +55,7 @@ This is not a one-way shortening pass. Add or restore prose when code, types, an - **Postmortems:** retain the incident sequence, evidence, causal chain, impact, and prevention. Remove repeated persuasion or implementation detail that does not establish causality. - **Skills and agent instructions:** state behavioral guardrails and explicit scope limitations such as “guidance, not a script/checklist.” Keep the workflow concise and link its source of truth. - **Examples and configuration comments:** explain access limits, non-obvious wiring or load order, security stance, replay behavior, exceptions, and likely misuse. Do not narrate entries that the configuration already shows. -- **Prompts and visible strings:** treat wording as behavior. Client UI copy belongs in typed locale dictionaries and reaches Cordis-free primitives as explicit localized props; inspect text, accessibility names, tooltips, placeholders, and format templates together, then run `verify-client-ui-i18n`. Update the owning runnable snapshot for model-visible text and repository-required GUI evidence. If the authorized scope has no owning scenario, leave the wording unchanged and report the deferral; do not silently fold it into a prose-only edit. +- **Prompts and visible strings:** treat wording as behavior. Client UI copy belongs in typed locale dictionaries and reaches Cordis-free primitives as explicit localized props; inspect text, accessibility names, tooltips, placeholders, and format templates together, then run `verify-client-ui-i18n`. English UI copy, including plugin `meta.title` and `meta.description`, uses sentence case: capitalize only the first word, proper nouns, and product names such as `Chrome DevTools`, `Git`, or `MCP` (`Git worktrees`, not `Git Worktrees`). Chinese copy keeps its own conventions. Update the owning runnable snapshot for model-visible text and repository-required GUI evidence. If the authorized scope has no owning scenario, leave the wording unchanged and report the deferral; do not silently fold it into a prose-only edit. - **Diagnostics:** name the failing subject or path, violated rule, and correction when it is non-obvious. Remove internal execution narration. Preserve searchable mechanism names and meaningful modal, temporal, or negative emphasis. Normalize decorative emphasis only. diff --git a/AGENTS.md b/AGENTS.md index 1f88aa1293..69dbd7d69e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -155,7 +155,7 @@ Real-API tests/demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`, and - **Tests describe behavior, not correctness.** Change obsolete behavior with its tests; explain why in the PR. - **Create Agent Notes only for durable decision rationale;** mechanical/local edits are exempt, including local UI changes ([scope](.agents/notes/README.md#when-to-write-one)). Archived notes are frozen: never edit or treat them as current authority ([archive policy](.agents/notes/README.md#archiving-and-deletion)). - **Official plugin artwork:** use [dsh-official-plugin-artwork](.agents/skills/dsh-official-plugin-artwork/SKILL.md) for official and official experimental plugin icons; obtain designer approval before landing new glyphs. -- **Client UI copy is locale-owned.** Route product text through typed dictionaries and `t` or localized primitive props; `verify-client-ui-i18n` rejects hardcoded copy ([decision](.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.md)). +- **Client UI copy is locale-owned.** Route product text through typed dictionaries and `t` or localized primitive props; `verify-client-ui-i18n` rejects hardcoded copy ([decision](.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.md)). English copy, including plugin titles, uses sentence case ([rule](.agents/skills/dsh-prose-standard/SKILL.md#required-coverage-by-prose-location)). - **Testing policy** — [docs/testing.md](docs/testing.md). Every non-trivial model- or product-user-visible change updates a keyless recorded-session snapshot; [snapshot ownership](snapshots/AGENTS.md) reserves the top-level tree for session-driven cases and keeps other expected output owner-local. Fixtures replay on macOS/Linux; fix fixtures, not normalizers. - **Design each tool's UI presentation up front.** Host presenters stay pure; Web cards derive from raw events and persisted result metadata ([cookbook](docs/cookbook/adding-a-tool.md)). - **Plan unit, e2e, and snapshot coverage** for capability seams, lifecycle paths, and transcript output; include missing snapshot-harness support in the same change. diff --git a/apps/web/tests/agent-team-panel.e2e.ts b/apps/web/tests/agent-team-panel.e2e.ts index bd7dcff720..d5f699a288 100644 --- a/apps/web/tests/agent-team-panel.e2e.ts +++ b/apps/web/tests/agent-team-panel.e2e.ts @@ -85,8 +85,8 @@ describe('web e2e: Agent Teams panel', () => { onTestFailed(() => saveFailureShot(page, 'web-e2e-agent-team-panel')) const projectionReads = vi.spyOn(scaffold.ctx.sessionController, 'projections') onTestFinished(() => { projectionReads.mockRestore() }) - await page.locator('[data-team-action]').getByRole('button', { name: /Agent Team/iu }).click() - const action = page.getByRole('dialog', { name: 'Agent Team', exact: true }) + await page.locator('[data-team-action]').getByRole('button', { name: 'Agent team', exact: true }).click() + const action = page.getByRole('dialog', { name: 'Agent team', exact: true }) await action.getByText('No shared tasks yet. Create them through the conversation.').waitFor() await action.getByText('lead').waitFor() expect(projectionReads).not.toHaveBeenCalled() @@ -119,8 +119,8 @@ describe('web e2e: Agent Teams panel', () => { it('keeps the panel inside the viewport and closes on outside click or Escape', async () => { onTestFailed(() => saveFailureShot(page, 'web-e2e-agent-team-panel-keyboard')) - const panel = page.getByRole('dialog', { name: 'Agent Team', exact: true }) - const trigger = page.locator('[data-team-action]').getByRole('button', { name: /Agent Team/iu }) + const panel = page.getByRole('dialog', { name: 'Agent team', exact: true }) + const trigger = page.locator('[data-team-action]').getByRole('button', { name: 'Agent team', exact: true }) const viewport = page.viewportSize()! await trigger.click() await panel.getByText('lead', { exact: true }).waitFor() @@ -162,8 +162,8 @@ describe('web e2e: Agent Teams panel', () => { }) it('keeps the hovered panel open when the pointer returns directly to its trigger', async () => { - const trigger = page.locator('[data-team-action]').getByRole('button', { name: 'Agent Team', exact: true }) - const panel = page.getByRole('dialog', { name: 'Agent Team', exact: true }) + const trigger = page.locator('[data-team-action]').getByRole('button', { name: 'Agent team', exact: true }) + const panel = page.getByRole('dialog', { name: 'Agent team', exact: true }) await trigger.hover() await panel.waitFor() await panel.hover() @@ -182,8 +182,8 @@ describe('web e2e: Agent Teams panel', () => { writeScopes: [], }) const viewport = page.viewportSize()! - const trigger = page.locator('[data-team-action]').getByRole('button', { name: 'Agent Team', exact: true }) - const panel = page.getByRole('dialog', { name: 'Agent Team', exact: true }) + const trigger = page.locator('[data-team-action]').getByRole('button', { name: 'Agent team', exact: true }) + const panel = page.getByRole('dialog', { name: 'Agent team', exact: true }) const card = panel.locator('article').filter({ hasText: 'Responsive task' }) await trigger.click() await card.waitFor() diff --git a/apps/web/tests/cua-permissions.e2e.ts b/apps/web/tests/cua-permissions.e2e.ts index 8b1309c725..43ba40e4e0 100644 --- a/apps/web/tests/cua-permissions.e2e.ts +++ b/apps/web/tests/cua-permissions.e2e.ts @@ -80,15 +80,15 @@ it.skipIf(process.platform !== 'darwin')('keeps system requests pending until fr expect(await card.getByRole('button', { name: /Accessibility permission|Screen Recording permission/ }).count()).toBe(0) await page.setViewportSize({ width: 1680, height: 600 }) const plugins = page.locator('[data-plugin-panel]') - const openDetails = page.getByRole('button', { name: 'View Computer Use', exact: true }) + const openDetails = page.getByRole('button', { name: 'View Computer use', exact: true }) await openDetails.scrollIntoViewIfNeeded() expect(await plugins.evaluate(element => element.scrollTop)).toBeGreaterThan(0) await openDetails.click() const panel = page.locator('[data-cua-settings]') const accessibility = panel.getByRole('group', { name: 'Accessibility', exact: true }) await accessibility.getByRole('button', { name: 'Request permission', exact: true }).waitFor() - expect(await panel.getByRole('region', { name: 'Authorize Computer Use', exact: true }).getByRole('group').count()).toBe(4) - expect(await panel.getByRole('region', { name: 'Manage applications Computer Use can access', exact: true }).count()).toBe(1) + expect(await panel.getByRole('region', { name: 'Authorize computer use', exact: true }).getByRole('group').count()).toBe(4) + expect(await panel.getByRole('region', { name: 'Manage applications computer use can access', exact: true }).count()).toBe(1) await expect.poll(() => panel.locator('li img').evaluate((image: HTMLImageElement) => image.naturalWidth)).toBe(1) expect(await panel.getByText('/fixture/app', { exact: true }).count()).toBe(0) expect(await accessibility.getByText('Visual Studio Code.app', { exact: true }).count()).toBe(1) @@ -186,7 +186,7 @@ it.skipIf(process.platform !== 'darwin')('keeps system requests pending until fr return { errors, layouts, documents, stop: () => { observer.disconnect(); cancelAnimationFrame(frame) } } }) try { - const toggle = page.locator('[data-plugin-panel]').getByRole('switch', { name: 'Enable Computer Use', exact: true }) + const toggle = page.locator('[data-plugin-panel]').getByRole('switch', { name: 'Enable Computer use', exact: true }) for (const width of [1680, 390]) { await page.setViewportSize({ width, height: 1000 }) if (width === 390) await page.locator('[data-sidebar-collapsed]').waitFor({ state: 'attached' }) diff --git a/apps/web/tests/expected/cua-permissions/application-approval.expected.md b/apps/web/tests/expected/cua-permissions/application-approval.expected.md index 96f5eb74e3..fc9007a938 100644 --- a/apps/web/tests/expected/cua-permissions/application-approval.expected.md +++ b/apps/web/tests/expected/cua-permissions/application-approval.expected.md @@ -1,8 +1,8 @@ - text: Waiting for approval - group "Approval details": - strong: Fixture - - text: Allow Computer Use to launch, read, capture and control this application? - - paragraph: Session access lasts until DSH restarts or Computer Use is disabled. + - text: Allow computer use to launch, read, capture and control this application? + - paragraph: Session access lasts until DSH restarts or computer use is disabled. - button "Reject" - button "Allow for this session" - button "Always allow" diff --git a/apps/web/tests/expected/cua-permissions/missing.expected.md b/apps/web/tests/expected/cua-permissions/missing.expected.md index 8dfba492a2..cb4ade57ac 100644 --- a/apps/web/tests/expected/cua-permissions/missing.expected.md +++ b/apps/web/tests/expected/cua-permissions/missing.expected.md @@ -1,8 +1,8 @@ -- region "Authorize Computer Use": - - heading "Authorize Computer Use" [level=3] +- region "Authorize computer use": + - heading "Authorize computer use" [level=3] - group "Default mode for new sessions": - strong: Default mode for new sessions - - paragraph: Computer Use is available only in PTC mode. + - paragraph: Computer use is available only in PTC mode. - button "Set to PTC" - group "PTC execution mode": - strong: PTC execution mode @@ -17,8 +17,8 @@ - paragraph: In System Settings, allow Visual Studio Code.app to capture application windows. - button "Request permission" - paragraph: Manage permissions in System Settings for the application named by macOS. -- region "Manage applications Computer Use can access": - - heading "Manage applications Computer Use can access" [level=3] +- region "Manage applications computer use can access": + - heading "Manage applications computer use can access" [level=3] - heading "Always-allowed applications" [level=4] - list: - listitem: diff --git a/apps/web/tests/expected/shared-inspector/metadata-en.expected.md b/apps/web/tests/expected/shared-inspector/metadata-en.expected.md index 4953cbfc60..8a700d7178 100644 --- a/apps/web/tests/expected/shared-inspector/metadata-en.expected.md +++ b/apps/web/tests/expected/shared-inspector/metadata-en.expected.md @@ -1,9 +1,9 @@ -- bundle: Developer Tools +- bundle: Developer tools description: Inspect and debug raw session data, grouped chat messages, and the NodeJS backend. -- component: NodeJS Inspector +- component: NodeJS inspector description: Press Ctrl/Cmd+Shift+. to open Chrome DevTools in the bottom panel for development and debugging. -- component: Session Log +- component: Session log description: Enable analysis of the current session's raw data and grouped chat messages in the session sidebar. -- session sidebar: Session Log +- session sidebar: Session log description: Analyze raw logs and grouped chat messages for the current session in the sidebar. -- NodeJS bottom: NodeJS Inspector +- NodeJS bottom: NodeJS inspector diff --git a/apps/web/tests/inspector-late-enable.e2e.ts b/apps/web/tests/inspector-late-enable.e2e.ts index 9a7cda3eee..c8c7e6fff3 100644 --- a/apps/web/tests/inspector-late-enable.e2e.ts +++ b/apps/web/tests/inspector-late-enable.e2e.ts @@ -35,7 +35,7 @@ it('opens bottom DevTools after the Developer Tools bundle is enabled on an alre await page.getByRole('navigation', { name: 'Global panels' }).getByRole('button', { name: 'Plugins', exact: true }).click() const plugins = page.locator('[data-plugin-panel]') - const toggle = plugins.getByRole('switch', { name: 'Enable Developer Tools', exact: true }) + const toggle = plugins.getByRole('switch', { name: 'Enable Developer tools', exact: true }) await toggle.waitFor() expect(await toggle.getAttribute('aria-checked')).toBe('false') const panel = page.locator('[data-inspector-panel]') diff --git a/apps/web/tests/plugin-manager.e2e.ts b/apps/web/tests/plugin-manager.e2e.ts index 4523b55b3b..21826685e8 100644 --- a/apps/web/tests/plugin-manager.e2e.ts +++ b/apps/web/tests/plugin-manager.e2e.ts @@ -923,14 +923,14 @@ describe('web e2e: plugin manager', () => { expect(await child.getByText('为智能体提供成员协调、消息通信和共享任务管理工具。', { exact: true }).count()).toBe(1) try { await setLanguage('en') - await panel.getByRole('heading', { name: 'Agent Teams', exact: true }).waitFor() + await panel.getByRole('heading', { name: 'Agent teams', exact: true }).waitFor() expect(await packageName.textContent()).toBe('@deepseek-ai/dsh-experimental-agent-team-profile') expect(await panel.getByText('Enable team collaboration, team tools, the member roster, and the shared task board.').count()).toBe(1) - await child.getByText('Team Tools', { exact: true }).waitFor() + await child.getByText('Team tools', { exact: true }).waitFor() expect(await child.getByText('Give agents tools to coordinate members, exchange messages, and manage shared tasks.', { exact: true }).count()).toBe(1) await panel.getByRole('button', { name: 'Back to plugins' }).click() - await panel.getByRole('button', { name: 'View Agent Teams', exact: true }).waitFor() - expect(await panel.getByRole('switch', { name: 'Enable Agent Teams', exact: true }).count()).toBe(1) + await panel.getByRole('button', { name: 'View Agent teams', exact: true }).waitFor() + expect(await panel.getByRole('switch', { name: 'Enable Agent teams', exact: true }).count()).toBe(1) // Schedule ships in the delivered composition, so no bundle card carries it in either language. expect(await panel.getByRole('button', { name: 'View Automation tasks', exact: true }).count()).toBe(0) // The official configuration pages follow the language too, from their own dictionary. diff --git a/apps/web/tests/preview-boot.e2e.ts b/apps/web/tests/preview-boot.e2e.ts index d4d502e388..4cb8a8946f 100644 --- a/apps/web/tests/preview-boot.e2e.ts +++ b/apps/web/tests/preview-boot.e2e.ts @@ -776,7 +776,7 @@ async function openSessionInspector(page: Page): Promise { // The showcase opens with a file tab in the right sidebar; a new tab shows the guide entries. await page.getByRole('button', { name: 'New tab', exact: true }).click() await page.locator('[data-sidebar-right-guide-entry="session-inspector-log"]').click({ timeout: 30_000 }) - await page.getByRole('table', { name: 'Raw Log', exact: true }).waitFor({ timeout: 15_000 }) + await page.getByRole('table', { name: 'Raw log', exact: true }).waitFor({ timeout: 15_000 }) expect(await setBundle(page, INSPECTOR_BUNDLE, false)).toBe('applied') } diff --git a/apps/web/tests/session-inspector.e2e.ts b/apps/web/tests/session-inspector.e2e.ts index 2df97eeaa9..cba16b1571 100644 --- a/apps/web/tests/session-inspector.e2e.ts +++ b/apps/web/tests/session-inspector.e2e.ts @@ -46,12 +46,12 @@ it.skipIf(webSnapshotMode() === 'record')('inspects a replayed Session log and C await page.locator('[data-sidebar-right-guide-entry="session-inspector-log"]').click() const mode = page.getByRole('combobox', { name: 'Presentation', exact: true }) expect(await mode.inputValue()).toBe('session-log') - const raw = page.getByRole('table', { name: 'Raw Log', exact: true }) + const raw = page.getByRole('table', { name: 'Raw log', exact: true }) await raw.getByRole('button', { name: 'Filter types', exact: true }).click() await page.getByRole('combobox', { name: 'Type keyword', exact: true }).fill('user/message') await page.getByRole('option', { name: 'user/message', exact: true }).click() await raw.getByRole('button', { name: 'user/message', exact: true }).first().click() - const panel = page.locator('section[aria-label="Raw Log"]') + const panel = page.locator('section[aria-label="Raw log"]') const details = panel.getByRole('separator').locator('..').locator('ul').first() expect(await details.innerText()).toContain(prompts[0]) expect(await details.locator('details:not([open])').count()).toBe(0) @@ -64,14 +64,14 @@ it.skipIf(webSnapshotMode() === 'record')('inspects a replayed Session log and C const replacements = [...new Set(times)].filter(value => /^\d{2}:\d{2}:\d{2}\.\d{3}$/.test(value)) .map(value => [value, '{{time}}'] as const) await compareOrRefreshGolden(join(directory, 'log.expected.md'), - await captureStableAria(page, 'section[aria-label="Raw Log"] table', scaffold.workspaceCwd, { replacements }), webSnapshotMode()) + await captureStableAria(page, 'section[aria-label="Raw log"] table', scaffold.workspaceCwd, { replacements }), webSnapshotMode()) await mode.selectOption('chat-node') - const groups = page.getByRole('table', { name: 'Chat Group', exact: true }) + const groups = page.getByRole('table', { name: 'Chat group', exact: true }) expect(await groups.getByRole('columnheader', { name: 'Time (UTC)' }).count()).toBe(0) await groups.getByRole('button', { name: 'assistant-step', exact: true }).first().click() await page.getByRole('navigation', { name: 'Raw data navigation', exact: true }).waitFor() - const groupPanel = page.locator('section[aria-label="Chat Group"]') - await groupPanel.locator('summary').filter({ hasText: 'Node Data' }).click() + const groupPanel = page.locator('section[aria-label="Chat group"]') + await groupPanel.locator('summary').filter({ hasText: 'Node data' }).click() const blocks = groupPanel.getByText('blocks:', { exact: true }).locator('..') await blocks.click() await blocks.locator('..').getByText('Other properties:', { exact: true }).waitFor() @@ -104,7 +104,7 @@ it.skipIf(webSnapshotMode() === 'record')('highlights nearby Chat content when t await page.getByText('DONE', { exact: true }).waitFor() await page.getByRole('button', { name: 'Open right sidebar', exact: true }).click() await page.locator('[data-sidebar-right-guide-entry="session-inspector-log"]').click() - const table = page.getByRole('table', { name: 'Raw Log', exact: true }) + const table = page.getByRole('table', { name: 'Raw log', exact: true }) await table.getByRole('button', { name: 'Filter types', exact: true }).click() await page.getByRole('combobox', { name: 'Type keyword', exact: true }).fill('tool/call') await page.getByRole('button', { name: 'Apply filter', exact: true }).click() diff --git a/apps/web/tests/shared-inspector.e2e.ts b/apps/web/tests/shared-inspector.e2e.ts index 74a65d498b..d72ff60197 100644 --- a/apps/web/tests/shared-inspector.e2e.ts +++ b/apps/web/tests/shared-inspector.e2e.ts @@ -42,7 +42,7 @@ it('toggles bottom DevTools from the page and iframe without a selected Session' const close = panel.getByRole('button', { name: 'Collapse', exact: true }) expect(await close.textContent()).toBe('') expect(await close.locator('svg').count()).toBe(1) - const divider = panel.getByRole('separator', { name: 'Resize NodeJS Inspector panel' }) + const divider = panel.getByRole('separator', { name: 'Resize NodeJS inspector panel' }) const drag = async (deltaY: number): Promise => { const box = (await divider.boundingBox())! const x = box.x + box.width / 2 @@ -93,10 +93,10 @@ it.each([ openSidebar: '打开右侧边栏', sidebarDescription: '在侧边栏分析当前会话原始日志和聊天分组数据', }, { - locale: 'en-US', file: 'en', plugins: 'Plugins', open: 'View Developer Tools', bundle: 'Developer Tools', + locale: 'en-US', file: 'en', plugins: 'Plugins', open: 'View Developer tools', bundle: 'Developer tools', description: 'Inspect and debug raw session data, grouped chat messages, and the NodeJS backend.', - inspector: 'NodeJS Inspector', inspectorDescription: 'Press Ctrl/Cmd+Shift+. to open Chrome DevTools in the bottom panel for development and debugging.', - session: 'Session Log', sessionDescription: "Enable analysis of the current session's raw data and grouped chat messages in the session sidebar.", + inspector: 'NodeJS inspector', inspectorDescription: 'Press Ctrl/Cmd+Shift+. to open Chrome DevTools in the bottom panel for development and debugging.', + session: 'Session log', sessionDescription: "Enable analysis of the current session's raw data and grouped chat messages in the session sidebar.", openSidebar: 'Open right sidebar', sidebarDescription: 'Analyze raw logs and grouped chat messages for the current session in the sidebar.', }, ])('shows the Inspector bundle and component copy in $locale', async (copy) => { @@ -150,7 +150,7 @@ it.each([ ` description: ${await sessionGuide.getByText(copy.sidebarDescription, { exact: true }).innerText()}`, `- NodeJS bottom: ${await bottomTitle.innerText()}`) await sessionGuide.click() - await page.getByRole('table', { name: copy.locale === 'zh-CN' ? '原始数据' : 'Raw Log', exact: true }).waitFor() + await page.getByRole('table', { name: copy.locale === 'zh-CN' ? '原始数据' : 'Raw log', exact: true }).waitFor() await compareOrRefreshGolden(fileURLToPath(new URL(`./expected/shared-inspector/metadata-${copy.file}.expected.md`, import.meta.url)), snapshot.join('\n'), webSnapshotMode()) }) diff --git a/apps/web/tests/snapshots/agent-team-panel/resize.expected.md b/apps/web/tests/snapshots/agent-team-panel/resize.expected.md index 19f90be9a3..57fbab1f84 100644 --- a/apps/web/tests/snapshots/agent-team-panel/resize.expected.md +++ b/apps/web/tests/snapshots/agent-team-panel/resize.expected.md @@ -1,4 +1,4 @@ -- dialog "Agent Team": +- dialog "Agent team": - heading "Members" [level=3] - button "lead Current chat Inactive" [disabled] - heading "Shared tasks 2" [level=3] diff --git a/apps/web/tests/snapshots/agent-team-panel/task.expected.md b/apps/web/tests/snapshots/agent-team-panel/task.expected.md index 9e6f63532e..d5593a069b 100644 --- a/apps/web/tests/snapshots/agent-team-panel/task.expected.md +++ b/apps/web/tests/snapshots/agent-team-panel/task.expected.md @@ -1,4 +1,4 @@ -- dialog "Agent Team": +- dialog "Agent team": - heading "Members" [level=3] - button "lead Current chat Inactive" [disabled] - heading "Shared tasks 1" [level=3] diff --git a/apps/web/tests/worktree-opt-in.e2e.ts b/apps/web/tests/worktree-opt-in.e2e.ts index 40d4e58857..37b94a16ec 100644 --- a/apps/web/tests/worktree-opt-in.e2e.ts +++ b/apps/web/tests/worktree-opt-in.e2e.ts @@ -70,9 +70,9 @@ it('persists the Git Worktrees switch and removes its capability while retaining } let panel = await openPlugins() let card = panel.locator(`[data-plugin-group="more"] [data-plugin-package="${BUNDLE}"]`) - await card.getByRole('button', { name: 'View Git Worktrees', exact: true }).waitFor() + await card.getByRole('button', { name: 'View Git worktrees', exact: true }).waitFor() expect(await card.getByText('Experimental', { exact: true }).count()).toBe(1) - let toggle = card.getByRole('switch', { name: 'Enable Git Worktrees', exact: true }) + let toggle = card.getByRole('switch', { name: 'Enable Git worktrees', exact: true }) expect(await toggle.getAttribute('aria-checked')).toBe('false') expect(await selectedBundles()).not.toContain(BUNDLE) expect(rows()).toEqual([]) @@ -92,7 +92,7 @@ it('persists the Git Worktrees switch and removes its capability while retaining await page.reload({ waitUntil: 'load' }) panel = await openPlugins() card = panel.locator(`[data-plugin-group="more"] [data-plugin-package="${BUNDLE}"]`) - toggle = card.getByRole('switch', { name: 'Enable Git Worktrees', exact: true }) + toggle = card.getByRole('switch', { name: 'Enable Git worktrees', exact: true }) await expect.poll(() => toggle.getAttribute('aria-checked')).toBe('true') expect(tools()).toContain('create_worktree') await toggle.click() @@ -108,7 +108,7 @@ it('persists the Git Worktrees switch and removes its capability while retaining await page.reload({ waitUntil: 'load' }) panel = await openPlugins() - await expect.poll(() => panel.getByRole('switch', { name: 'Enable Git Worktrees', exact: true }) + await expect.poll(() => panel.getByRole('switch', { name: 'Enable Git worktrees', exact: true }) .getAttribute('aria-checked')).toBe('false') expect(tools()).not.toContain('create_worktree') expect(await selectedBundles()).not.toContain(BUNDLE) diff --git a/docs/cookbook/adding-a-package.i18n.yaml b/docs/cookbook/adding-a-package.i18n.yaml index 2fb8b9bd67..de5eb1d67b 100644 --- a/docs/cookbook/adding-a-package.i18n.yaml +++ b/docs/cookbook/adding-a-package.i18n.yaml @@ -21,8 +21,8 @@ en: 0894bd9f35430753 zh: 2d28090e71134edd /cookbook-adding-a-workspace-package/5-add-optional-plugin-display-metadata: - en: ed501a72ab8ba6e7 - zh: 8ad775b735887a10 + en: cb799f5d561dd917 + zh: 7739af36ef8ffb3f /cookbook-adding-a-workspace-package/6-verify: en: 80c7fb90b1175aae zh: 2ba37962dbc57299 diff --git a/docs/cookbook/adding-a-package.md b/docs/cookbook/adding-a-package.md index 1f86f1b9bd..d5dc6454a8 100644 --- a/docs/cookbook/adding-a-package.md +++ b/docs/cookbook/adding-a-package.md @@ -136,7 +136,7 @@ Merge these entries into `package.json`, retaining existing runtime exports and } ``` -Keep language files together in `locale/`, with `en.json` as the discovery entry. Fields are optional; present values must be non-empty strings. Missing files or fields permit fallback, while malformed JSON or invalid fields produce a per-plugin diagnostic. +Keep language files together in `locale/`, with `en.json` as the discovery entry. Fields are optional; present values must be non-empty strings. English `meta.title` and `meta.description` use sentence case: capitalize only the first word, proper nouns, and product names, as in `Git worktrees` ([rule](../../.agents/skills/dsh-prose-standard/SKILL.md#required-coverage-by-prose-location)). Missing files or fields permit fallback, while malformed JSON or invalid fields produce a per-plugin diagnostic. Fields fall back independently before view-specific name formatting, using the existing locale language chain first: diff --git a/docs/cookbook/adding-a-package.zh.md b/docs/cookbook/adding-a-package.zh.md index 3e13580fe9..4ef55c443a 100644 --- a/docs/cookbook/adding-a-package.zh.md +++ b/docs/cookbook/adding-a-package.zh.md @@ -136,7 +136,7 @@ Append-only, prefix-stable, replacing, or independent behavior, including the ex } ``` -语言文件统一放在 `locale/`,并保留 `en.json` 作为发现入口。字段可选,填写时必须是非空字符串。文件或字段缺失时允许回退,JSON 损坏或字段无效时显示单插件诊断。 +语言文件统一放在 `locale/`,并保留 `en.json` 作为发现入口。字段可选,填写时必须是非空字符串。英文 `meta.title` 和 `meta.description` 使用 sentence case:只大写首个单词、专有名词和产品名,例如 `Git worktrees`([rule](../../.agents/skills/dsh-prose-standard/SKILL.md#required-coverage-by-prose-location))。文件或字段缺失时允许回退,JSON 损坏或字段无效时显示单插件诊断。 各字段先独立回退,再按页面规则格式化技术名称;回退优先经过现有 locale 语言链: diff --git a/packages/client/ui-plugin-manager/src/client/locales.ts b/packages/client/ui-plugin-manager/src/client/locales.ts index e820c21322..fb4ec04104 100644 --- a/packages/client/ui-plugin-manager/src/client/locales.ts +++ b/packages/client/ui-plugin-manager/src/client/locales.ts @@ -380,7 +380,7 @@ export const en = { installGuideFill: 'Use example', installGuideFillAria: 'Use the example {example}', installGuideSafety: 'Install only plugins you trust: they run with your permissions and can damage DeepSeek Harness or leak your data.', - installUpgradeNotice: 'On-demand Official plugins offer Update when their version differs from DSH. To update other plugins, uninstall and reinstall them.', + installUpgradeNotice: 'On-demand official plugins offer Update when their version differs from DSH. To update other plugins, uninstall and reinstall them.', registryToggle: 'Registry', registryLegend: 'The npm registry the plugin is downloaded from', registryDefault: 'Default registry', diff --git a/packages/experimental/agent-team-profile/docs/DETAIL.md b/packages/experimental/agent-team-profile/docs/DETAIL.md index 2c45e46b94..62dd975f03 100644 --- a/packages/experimental/agent-team-profile/docs/DETAIL.md +++ b/packages/experimental/agent-team-profile/docs/DETAIL.md @@ -1,11 +1,11 @@ -![A conversation titled Add rate limiting with its Agent Team button, and the panel it opens: two members and two shared tasks](preview.svg) +![A conversation titled Add rate limiting with its Agent team button, and the panel it opens: two members and two shared tasks](preview.svg) Ask the agent you are talking to, the lead, to split a job. It creates named teammates, puts the work on a shared task board, waits for their results, and reports back. ## How to use -- Ask in plain words, for example: "Use Agent Teams to split this task between two teammates, wait, and summarize." -- Click **Agent Team** next to the conversation title to see **Members** and **Shared tasks**. Each task shows its status, **Owner**, **Write scopes**, and the tasks it is **Blocked by**. +- Ask in plain words, for example: "Use agent teams to split this task between two teammates, wait, and summarize." +- Click **Agent team** next to the conversation title to see **Members** and **Shared tasks**. Each task shows its status, **Owner**, **Write scopes**, and the tasks it is **Blocked by**. - Click a teammate to open its conversation and talk to it there. - The panel is read-only. To change tasks or teammates, ask the lead. diff --git a/packages/experimental/agent-team-profile/locale/en.json b/packages/experimental/agent-team-profile/locale/en.json index 7d7c505d0e..7086787e61 100644 --- a/packages/experimental/agent-team-profile/locale/en.json +++ b/packages/experimental/agent-team-profile/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Agent Teams", + "title": "Agent teams", "description": "Enable team collaboration, team tools, the member roster, and the shared task board.", "detail": "docs/DETAIL.md" } diff --git a/packages/experimental/agent-team/locale/en.json b/packages/experimental/agent-team/locale/en.json index 5d93c64811..5706ed2cc6 100644 --- a/packages/experimental/agent-team/locale/en.json +++ b/packages/experimental/agent-team/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Team Collaboration", + "title": "Team collaboration", "description": "Manage team members, member messages, and shared tasks." } } diff --git a/packages/experimental/auto-review/locale/en.json b/packages/experimental/auto-review/locale/en.json index ba43ff62fe..c051b5bcfc 100644 --- a/packages/experimental/auto-review/locale/en.json +++ b/packages/experimental/auto-review/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Auto Authorization Review", + "title": "Auto authorization review", "description": "Add an Auto review permission mode that uses the model to assess authorization before each tool call.", "detail": "docs/DETAIL.md" } diff --git a/packages/experimental/browser-use-chrome-devtools-mcp/locale/en.json b/packages/experimental/browser-use-chrome-devtools-mcp/locale/en.json index a2f98e1846..035febc4b7 100644 --- a/packages/experimental/browser-use-chrome-devtools-mcp/locale/en.json +++ b/packages/experimental/browser-use-chrome-devtools-mcp/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Browser Use (Chrome DevTools)", + "title": "Browser use (Chrome DevTools)", "description": "Let the model inspect and operate web pages through Chrome DevTools MCP in a launched Chrome." } } diff --git a/packages/experimental/browser-use-playwright-mcp/locale/en.json b/packages/experimental/browser-use-playwright-mcp/locale/en.json index ffc27dd81c..4b3c3aa18d 100644 --- a/packages/experimental/browser-use-playwright-mcp/locale/en.json +++ b/packages/experimental/browser-use-playwright-mcp/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Browser Use (Playwright)", + "title": "Browser use (Playwright)", "description": "Let the model browse and operate web pages through Playwright MCP in a launched Chromium." } } diff --git a/packages/experimental/client-ui-agent-team/locale/en.json b/packages/experimental/client-ui-agent-team/locale/en.json index ad6f1665e5..81e33d8236 100644 --- a/packages/experimental/client-ui-agent-team/locale/en.json +++ b/packages/experimental/client-ui-agent-team/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Team Panel", + "title": "Team panel", "description": "View team members and shared tasks, and navigate to member conversations." } } diff --git a/packages/experimental/client-ui-agent-team/src/client/locales.ts b/packages/experimental/client-ui-agent-team/src/client/locales.ts index fe4b853095..cc559c2a2a 100644 --- a/packages/experimental/client-ui-agent-team/src/client/locales.ts +++ b/packages/experimental/client-ui-agent-team/src/client/locales.ts @@ -37,10 +37,10 @@ export type TeamKey = keyof typeof zh /** English dictionary checked against the Chinese key set. */ export const en = { - trigger: 'Agent Team', - loading: 'Loading Team…', + trigger: 'Agent team', + loading: 'Loading team…', unavailable: 'Team is unavailable', - failure: 'Invalid persisted Team record: {message}', + failure: 'Invalid persisted team record: {message}', empty: 'No shared tasks yet. Create them through the conversation.', roster: 'Members', tasks: 'Shared tasks', diff --git a/packages/experimental/client-ui-agent-team/tests/browser-plugin.client.spec.ts b/packages/experimental/client-ui-agent-team/tests/browser-plugin.client.spec.ts index 23c5844747..22c0ca04bd 100644 --- a/packages/experimental/client-ui-agent-team/tests/browser-plugin.client.spec.ts +++ b/packages/experimental/client-ui-agent-team/tests/browser-plugin.client.spec.ts @@ -82,7 +82,7 @@ describe('ui-team browser plugin', () => { locale: 'agent-team', }) const t = b.ctx.locale.bind('agent-team') - expect(t('trigger')).toBe('Agent Team') + expect(t('trigger')).toBe('Agent team') expect(b.navigation).toEqual([]) diff --git a/packages/experimental/client-ui-cot-translation/src/client/locales.ts b/packages/experimental/client-ui-cot-translation/src/client/locales.ts index 88cc907e32..adfb1c629c 100644 --- a/packages/experimental/client-ui-cot-translation/src/client/locales.ts +++ b/packages/experimental/client-ui-cot-translation/src/client/locales.ts @@ -7,10 +7,10 @@ export const NS = 'cotTranslation' /** English copy and key source. */ export const en = { provider: 'Translation service', google: 'Google', bing: 'Bing', - deepseekAccount: 'DeepSeek Flash — Account (paid)', deepseekOfficial: 'DeepSeek Flash — Official API (paid)', + deepseekAccount: 'DeepSeek Flash — account (paid)', deepseekOfficial: 'DeepSeek Flash — official API (paid)', providerUnavailableOption: '{provider} — unavailable', - providerUnavailable: 'This provider is unavailable. Configure your DeepSeek account or Official API provider, or choose an available service.', - paidNotice: 'Each uncached fragment sends a separate paid DeepSeek Flash request through the selected account or Official API credentials. Reopening reasoning reuses saved results; changing the provider, language, source text, or request settings may send a new paid request.', + providerUnavailable: 'This provider is unavailable. Configure your DeepSeek account or official API provider, or choose an available service.', + paidNotice: 'Each uncached fragment sends a separate paid DeepSeek Flash request through the selected account or official API credentials. Reopening reasoning reuses saved results; changing the provider, language, source text, or request settings may send a new paid request.', anonymousNotice: 'Bing and Google need no translation login or API key. Their anonymous endpoints may reject or limit requests.', targetLanguage: 'Target language', targetLanguageHint: 'auto follows the UI language; language codes such as zh, en, and ja also work.', privacy: 'Expanded reasoning is sent to the selected service. It may contain private code or conversation details.', diff --git a/packages/experimental/computer-use-cua-driver-mcp/locale/en.json b/packages/experimental/computer-use-cua-driver-mcp/locale/en.json index 9ae3ab4ed1..99a93828da 100644 --- a/packages/experimental/computer-use-cua-driver-mcp/locale/en.json +++ b/packages/experimental/computer-use-cua-driver-mcp/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Computer Use (Cua Driver MCP)", + "title": "Computer use (Cua Driver MCP)", "description": "Let the model operate the desktop through a separately installed cua-driver executable." } } diff --git a/packages/experimental/computer-use-cua-driver-native/locale/en.json b/packages/experimental/computer-use-cua-driver-native/locale/en.json index 009e83af75..fa0b9a4d40 100644 --- a/packages/experimental/computer-use-cua-driver-native/locale/en.json +++ b/packages/experimental/computer-use-cua-driver-native/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Computer Use (Cua Driver)", + "title": "Computer use (Cua Driver)", "description": "Let the model operate the desktop with the bundled Cua Driver runtime; needs desktop permissions for the app that launches dsh." } } diff --git a/packages/experimental/cot-translation-bundle/docs/DETAIL.md b/packages/experimental/cot-translation-bundle/docs/DETAIL.md index b2e6a9696c..6128db4b72 100644 --- a/packages/experimental/cot-translation-bundle/docs/DETAIL.md +++ b/packages/experimental/cot-translation-bundle/docs/DETAIL.md @@ -12,7 +12,7 @@ Expand a **Think** row to read its reasoning in your interface language. Paragra - The reasoning you expand can contain your code and conversation details. - Bing is the default; Google is also available. Neither needs an account or API key, but neither guarantees availability or a free quota. -- With a DeepSeek account or Official API set up, **DeepSeek Flash — Account (paid)** and **DeepSeek Flash — Official API (paid)** also appear. Each text piece without a saved translation is then a paid request. +- With a DeepSeek account or official API set up, **DeepSeek Flash — account (paid)** and **DeepSeek Flash — official API (paid)** also appear. Each text piece without a saved translation is then a paid request. - Translations are saved in the session and reused when you reopen it. The original reasoning and the model's input stay unchanged. - Collapsed summaries stay in the original language. Translation can change Markdown formatting or code. - Turning the plugin off removes its buttons and cancels pending translations; saved translations stay in the session. diff --git a/packages/experimental/cua-bundle/locale/en.json b/packages/experimental/cua-bundle/locale/en.json index 5893fa41d8..3508df9450 100644 --- a/packages/experimental/cua-bundle/locale/en.json +++ b/packages/experimental/cua-bundle/locale/en.json @@ -1,7 +1,7 @@ { "meta": { - "title": "Computer Use", - "description": "Currently supports macOS; Windows support is in development. Loads Computer Use for existing and new PTC sessions. Switch to PTC mode and grant Accessibility and Screen Recording permissions to use it.", + "title": "Computer use", + "description": "Currently supports macOS; Windows support is in development. Loads computer use for existing and new PTC sessions. Switch to PTC mode and grant Accessibility and Screen Recording permissions to use it.", "detail": "docs/DETAIL.md" } } diff --git a/packages/experimental/cua-macos/src/native-build.ts b/packages/experimental/cua-macos/src/native-build.ts index 1d79c4b402..ab804bb0eb 100644 --- a/packages/experimental/cua-macos/src/native-build.ts +++ b/packages/experimental/cua-macos/src/native-build.ts @@ -17,7 +17,7 @@ export type NativeArchitecture = 'arm64' | 'x64' * @throws When the current host has no native implementation. */ export function nativeHostArchitecture(platform: string, architecture: string): NativeArchitecture { - if (platform !== 'darwin' || (architecture !== 'arm64' && architecture !== 'x64')) throw new Error('Computer Use currently requires macOS on Apple silicon or Intel. Windows support is in development') + if (platform !== 'darwin' || (architecture !== 'arm64' && architecture !== 'x64')) throw new Error('Computer use currently requires macOS on Apple silicon or Intel. Windows support is in development') return architecture } @@ -91,7 +91,7 @@ async function compileNative(spec: NativeBuildSpec, signal: AbortSignal, executa signal.throwIfAborted() if (outcome.exitCode !== 0 || outcome.signal !== null) { const diagnostics = `${handle.collected.stdout?.readFrom(0).text ?? ''}${handle.collected.stderr?.readFrom(0).text ?? ''}` - throw new Error(`Computer Use native preparation failed (${String(outcome.exitCode)}, ${String(outcome.signal)}): ${diagnostics}`) + throw new Error(`Computer use native preparation failed (${String(outcome.exitCode)}, ${String(outcome.signal)}): ${diagnostics}`) } const built = await readFile(join(scratch, 'release', 'dsh-cua-native')) let previous: Buffer | undefined @@ -107,7 +107,7 @@ async function compileNative(spec: NativeBuildSpec, signal: AbortSignal, executa try { retained = await readFile(runtime) } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error } - if (retained && !retained.equals(built)) throw new Error('Computer Use retained executable does not match its content digest') + if (retained && !retained.equals(built)) throw new Error('Computer use retained executable does not match its content digest') signal.throwIfAborted() if (!retained) await writeFileAtomic(runtime, built, { mode: 0o755 }) await access(runtime, constants.X_OK) diff --git a/packages/experimental/cua/README.i18n.yaml b/packages/experimental/cua/README.i18n.yaml index 3060f9f6f0..3ab7ae71fb 100644 --- a/packages/experimental/cua/README.i18n.yaml +++ b/packages/experimental/cua/README.i18n.yaml @@ -18,7 +18,7 @@ en: ceb54d964dc355b6 zh: 0892066fbceef7b9 /deepseek-ai-dsh-cua/use-this-package/permission-controls: - en: be53792edc381ea1 + en: 7315ac8d68cf5d02 zh: 037407055d9f34a0 /deepseek-ai-dsh-cua/understand-the-implementation: en: a32743fc5b545ab3 diff --git a/packages/experimental/cua/README.md b/packages/experimental/cua/README.md index 0c880c0e2a..89718a39b0 100644 --- a/packages/experimental/cua/README.md +++ b/packages/experimental/cua/README.md @@ -43,7 +43,7 @@ Actions return `dispatched:true, verified:false`; read fresh state to verify the ### Permission controls -The [Computer Use bundle](../cua-bundle/README.md) mounts this page with its native provider. **Authorize Computer Use** groups the new-session default, PTC execution mode, Accessibility, and Screen Recording inside one border, with dividers between the rows. Each system-permission row shows the system-reported responsible application’s `.app` bundle filename and icon together in a bordered label. **Manage applications Computer Use can access** places the application lists inside a separate bordered group. Choose **Request permission**, find that application in the macOS permission list, and enable its switch; if macOS names another application, follow that prompt. With no detected source, the page directs the user to the application named by macOS. Each system-permission row has one control: **Request permission** when access is missing, or an outlined **Allowed** button with a settings icon after authorization. Clicking it opens the corresponding privacy pane, where the user can revoke access; hovering shows **Open System Settings**. Unknown status uses the same button with an **Unknown** label; pending initial observations retain a single placeholder. The page refreshes on entry, when the window regains focus, and after permission actions. A failed query offers **Retry**. Follow any macOS quit-and-reopen prompt and restart DSH as needed. +The [Computer Use bundle](../cua-bundle/README.md) mounts this page with its native provider. **Authorize computer use** groups the new-session default, PTC execution mode, Accessibility, and Screen Recording inside one border, with dividers between the rows. Each system-permission row shows the system-reported responsible application’s `.app` bundle filename and icon together in a bordered label. **Manage applications computer use can access** places the application lists inside a separate bordered group. Choose **Request permission**, find that application in the macOS permission list, and enable its switch; if macOS names another application, follow that prompt. With no detected source, the page directs the user to the application named by macOS. Each system-permission row has one control: **Request permission** when access is missing, or an outlined **Allowed** button with a settings icon after authorization. Clicking it opens the corresponding privacy pane, where the user can revoke access; hovering shows **Open System Settings**. Unknown status uses the same button with an **Unknown** label; pending initial observations retain a single placeholder. The page refreshes on entry, when the window regains focus, and after permission actions. A failed query offers **Retry**. Follow any macOS quit-and-reopen prompt and restart DSH as needed. System-permission operations affect the computer running DSH. When accessing DSH remotely, complete authorization on that computer. diff --git a/packages/experimental/cua/src/application-access.ts b/packages/experimental/cua/src/application-access.ts index 82d7d2a74a..e5a957b97d 100644 --- a/packages/experimental/cua/src/application-access.ts +++ b/packages/experimental/cua/src/application-access.ts @@ -146,16 +146,16 @@ export class ApplicationAccess { let result: AskUserQuestionAnswer try { result = await questions.ask({ agent, signal, questions: [{ id: 'cua-app-access', - question: `Allow Computer Use to launch if needed, read, capture, and control ${identity.name}?`, - detail: 'Session access lasts until DSH restarts or Computer Use is disabled.', + question: `Allow computer use to launch if needed, read, capture, and control ${identity.name}?`, + detail: 'Session access lasts until DSH restarts or computer use is disabled.', options, intent: { kind: 'approval', approve: 'Allow for this session', reject: 'Deny', ...(identity.persistable && this.options.save ? { always: 'Always allow' } : {}), subject: { name: identity.name, ...(icon === undefined ? {} : { icon }) }, display: { - question: { en: 'Allow Computer Use to launch, read, capture and control this application?', + question: { en: 'Allow computer use to launch, read, capture and control this application?', zh: '允许 Computer Use 启动、读取、截图和控制此应用吗?' }, - detail: { en: 'Session access lasts until DSH restarts or Computer Use is disabled.', + detail: { en: 'Session access lasts until DSH restarts or computer use is disabled.', zh: '会话授权在 DSH 重启或关闭 Computer Use 后失效。' }, approve: { en: 'Allow for this session', zh: '本会话允许' }, }, diff --git a/packages/experimental/cua/src/client/locales.ts b/packages/experimental/cua/src/client/locales.ts index 7b4062f707..7e23488b28 100644 --- a/packages/experimental/cua/src/client/locales.ts +++ b/packages/experimental/cua/src/client/locales.ts @@ -2,13 +2,13 @@ export const NS = 'cuaPermissions' /** English permission-page dictionary. */ export const en = { - title: 'Authorize Computer Use', - applicationsTitle: 'Manage applications Computer Use can access', - setupTitle: 'Set up Computer Use before use', - setupBody: 'Computer Use is enabled. Before using it, switch to PTC mode and grant Accessibility and Screen Recording permissions. Open the plugin details to complete setup.', - setupQueryFailed: 'Computer Use is enabled, but system permissions could not be checked. Open the plugin details to see the error and retry.', + title: 'Authorize computer use', + applicationsTitle: 'Manage applications computer use can access', + setupTitle: 'Set up computer use before use', + setupBody: 'Computer use is enabled. Before using it, switch to PTC mode and grant Accessibility and Screen Recording permissions. Open the plugin details to complete setup.', + setupQueryFailed: 'Computer use is enabled, but system permissions could not be checked. Open the plugin details to see the error and retry.', defaultTitle: 'Default mode for new sessions', - defaultBody: 'Computer Use is available only in PTC mode.', + defaultBody: 'Computer use is available only in PTC mode.', defaultConfirm: 'Set to PTC', defaultSet: 'PTC is the default', defaultUnavailable: 'Could not read the default mode. Check it in Settings.', defaultReadOnly: 'Default-mode settings are read-only.', diff --git a/packages/experimental/cua/tests/application-access.spec.ts b/packages/experimental/cua/tests/application-access.spec.ts index a506da7728..ac98ad3fdb 100644 --- a/packages/experimental/cua/tests/application-access.spec.ts +++ b/packages/experimental/cua/tests/application-access.spec.ts @@ -75,13 +75,13 @@ it.each([ const question = request.questions[0]! expect(question).toMatchObject({ id: 'cua-app-access', - question: 'Allow Computer Use to launch if needed, read, capture, and control Test application?', - detail: 'Session access lasts until DSH restarts or Computer Use is disabled.', + question: 'Allow computer use to launch if needed, read, capture, and control Test application?', + detail: 'Session access lasts until DSH restarts or computer use is disabled.', intent: { kind: 'approval', approve: 'Allow for this session', reject: 'Deny', subject: { name: 'Test application' }, display: { - question: { en: 'Allow Computer Use to launch, read, capture and control this application?', + question: { en: 'Allow computer use to launch, read, capture and control this application?', zh: '允许 Computer Use 启动、读取、截图和控制此应用吗?' }, - detail: { en: 'Session access lasts until DSH restarts or Computer Use is disabled.', + detail: { en: 'Session access lasts until DSH restarts or computer use is disabled.', zh: '会话授权在 DSH 重启或关闭 Computer Use 后失效。' }, approve: { en: 'Allow for this session', zh: '本会话允许' }, }, diff --git a/packages/experimental/inspector-profile/README.i18n.yaml b/packages/experimental/inspector-profile/README.i18n.yaml index 5173e0b524..583ef4608e 100644 --- a/packages/experimental/inspector-profile/README.i18n.yaml +++ b/packages/experimental/inspector-profile/README.i18n.yaml @@ -9,16 +9,16 @@ en: a83fef9afaa2d5c3 zh: 95ff381e7be3afe6 /deepseek-ai-dsh-experimental-inspector-profile/summary: - en: 959f9bb18e5e99ad + en: 42e5984090888b60 zh: f0b00539b5ab40fd /deepseek-ai-dsh-experimental-inspector-profile/table-of-contents: en: 76368b6b1da79c80 zh: 52384aee991ac542 /deepseek-ai-dsh-experimental-inspector-profile/use-this-package: - en: 919c4baad08a10a6 + en: 25cfe986a0ff91e1 zh: 0f0c82abda2f3630 /deepseek-ai-dsh-experimental-inspector-profile/understand-the-implementation: - en: c9afee7b01eff431 + en: cc0adef84da59698 zh: c2d1209c14a42088 /deepseek-ai-dsh-experimental-inspector-profile/model-experience: en: 315bc90149039d2d diff --git a/packages/experimental/inspector-profile/README.md b/packages/experimental/inspector-profile/README.md index 1478378369..443718d9fb 100644 --- a/packages/experimental/inspector-profile/README.md +++ b/packages/experimental/inspector-profile/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Enable this optional bundle to inspect Session data in the Sidebar and open NodeJS Inspector in the bottom panel. It enables both inspectors and Host fetch capture; the bundle itself is off by default in Plugin Manager. +Enable this optional bundle to inspect Session data in the Sidebar and open NodeJS inspector in the bottom panel. It enables both inspectors and Host fetch capture; the bundle itself is off by default in Plugin Manager. ## Table of Contents @@ -24,11 +24,11 @@ Enable this optional bundle to inspect Session data in the Sidebar and open Node ## Use this package -Enable **Developer Tools** in Plugin Manager's official group. This selects `@deepseek-ai/dsh-experimental-inspector-profile` for the current Web profile. +Enable **Developer tools** in Plugin Manager's official group. This selects `@deepseek-ai/dsh-experimental-inspector-profile` for the current Web profile. -Open **Session Log** from the Sidebar's new-tab menu or guide. [Session Inspector](../session-inspector/README.md) provides one view with selectable Raw Log and Chat Group presentations; Raw Log is selected initially. +Open **Session log** from the Sidebar's new-tab menu or guide. [Session Inspector](../session-inspector/README.md) provides one view with selectable Raw log and Chat group presentations; Raw log is selected initially. -Press **Ctrl/Cmd+Shift+.** to open or collapse **NodeJS Inspector** in the bottom panel with the packaged DevTools frontend. Enabling this bundle starts the local debugging backend and unredacted Host fetch capture; no user override or debugging argument is required. +Press **Ctrl/Cmd+Shift+.** to open or collapse **NodeJS inspector** in the bottom panel with the packaged DevTools frontend. Enabling this bundle starts the local debugging backend and unredacted Host fetch capture; no user override or debugging argument is required. ----- @@ -38,7 +38,7 @@ Press **Ctrl/Cmd+Shift+.** to open or collapse **NodeJS Inspector** in the botto
Implementation internals — click to expand -[`cordis.patch.yml`](cordis.patch.yml) enables Session Inspector and the NodeJS Inspector backend with `captureFetch: true`, independently of debugging flags. See the Inspector [README](../inspector/README.md) for debugging and capture behavior. Each plugin owns its behavior and lifetime. +[`cordis.patch.yml`](cordis.patch.yml) enables Session Inspector and the NodeJS inspector backend with `captureFetch: true`, independently of debugging flags. See the Inspector [README](../inspector/README.md) for debugging and capture behavior. Each plugin owns its behavior and lifetime.
diff --git a/packages/experimental/inspector-profile/docs/DETAIL.md b/packages/experimental/inspector-profile/docs/DETAIL.md index 7f66b5acb9..7fe5bbc402 100644 --- a/packages/experimental/inspector-profile/docs/DETAIL.md +++ b/packages/experimental/inspector-profile/docs/DETAIL.md @@ -1,13 +1,13 @@ -![Session Log table in the sidebar and the NodeJS Inspector console](preview.svg) +![Session log table in the sidebar and the NodeJS inspector console](preview.svg) Use it when troubleshooting: check exactly what DSH recorded for a session, or debug DSH itself in Chrome DevTools. It does not change how the agent works. ## How to use -- In the right sidebar, click **+** (**New tab**), then choose **Session Log** on the **Start** page that opens. -- **Raw Log** lists the session's events grouped by turn and step; click a row to see its full data. Switch the selector to **Chat Group** to see the chat messages and their groups instead. +- In the right sidebar, click **+** (**New tab**), then choose **Session log** on the **Start** page that opens. +- **Raw log** lists the session's events grouped by turn and step; click a row to see its full data. Switch the selector to **Chat group** to see the chat messages and their groups instead. - Click the crosshair button next to the selector, then click a message in the chat to find its record. Press Esc to cancel. -- Press **Ctrl+Shift+.** (**Cmd+Shift+.** on macOS) to open or collapse **NodeJS Inspector** at the bottom of the window: Chrome DevTools, in English, connected to the DSH backend. +- Press **Ctrl+Shift+.** (**Cmd+Shift+.** on macOS) to open or collapse **NodeJS inspector** at the bottom of the window: Chrome DevTools, in English, connected to the DSH backend. ## Good to know diff --git a/packages/experimental/inspector-profile/locale/en.json b/packages/experimental/inspector-profile/locale/en.json index 1d0807525f..05afa6bc75 100644 --- a/packages/experimental/inspector-profile/locale/en.json +++ b/packages/experimental/inspector-profile/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Developer Tools", + "title": "Developer tools", "description": "Inspect and debug raw session data, grouped chat messages, and the NodeJS backend.", "detail": "docs/DETAIL.md" } diff --git a/packages/experimental/inspector/README.i18n.yaml b/packages/experimental/inspector/README.i18n.yaml index 0b5af98a06..32a171be97 100644 --- a/packages/experimental/inspector/README.i18n.yaml +++ b/packages/experimental/inspector/README.i18n.yaml @@ -9,13 +9,13 @@ en: 1060bee3ef37134d zh: 7a7b0096714f4279 /deepseek-ai-dsh-experimental-inspector/summary: - en: e2a633d32f00aa99 + en: 4a540c3df0359524 zh: 1d48f445800e3a35 /deepseek-ai-dsh-experimental-inspector/table-of-contents: en: 331f844c32631a82 zh: a9bc365f7d61d9ed /deepseek-ai-dsh-experimental-inspector/use-this-package: - en: 6d290aabc543f878 + en: 7d4d718b1a6f92ae zh: 7ad283b159ae2c29 /deepseek-ai-dsh-experimental-inspector/runtime-layout: en: 7db0c435e07d5005 diff --git a/packages/experimental/inspector/README.md b/packages/experimental/inspector/README.md index 3b958daa73..b4b78ff4da 100644 --- a/packages/experimental/inspector/README.md +++ b/packages/experimental/inspector/README.md @@ -11,7 +11,7 @@ English | [中文](README.zh.md) Inspect one running dsh Host and its browser Clients in Chrome DevTools: Host and Client Console contexts, Host Sources and debugging, captured Host fetches, and a shared Cordis tree, with all CDP state in a Worker. -The optional [Developer Tools bundle](../inspector-profile/README.md) enables both NodeJS Inspector and Session Log, including Host fetch capture. Web startup does not require or accept `--inspect`. +The optional [Developer tools bundle](../inspector-profile/README.md) enables both NodeJS inspector and Session log, including Host fetch capture. Web startup does not require or accept `--inspect`. ## Table of Contents @@ -35,7 +35,7 @@ For trusted local development, `pnpm run demo:inspector` explicitly mounts the s The bundle sets `experimental-inspector` to `disabled: false` with `captureFetch: true`. The Host's optional `--inspect` argument hook only opens an external Chrome window; it does not control whether the Inspector runs. -When this component is enabled, **Ctrl/Cmd+Shift+.** toggles **NodeJS Inspector** in the full-width bottom panel without requiring a selected Session. Drag its top edge to resize it, taking space from the content above; the selected height is retained across collapse and navigation. The focused divider also accepts Up/Down, Home and End. Its iframe loads on the first open and remains mounted while collapsed and across Session or main-panel navigation; unloading the component resets it. The X button and shortcut restore the opening control's focus when that control remains connected. The iframe automatically connects to this Host and opens Console; the manual Connection panel is omitted. Each embedded frontend shows Host and only its embedding page's Client in Console, Sources and the Cordis tree. The printed `devtools://` URL shows all Clients. +When this component is enabled, **Ctrl/Cmd+Shift+.** toggles **NodeJS inspector** in the full-width bottom panel without requiring a selected Session. Drag its top edge to resize it, taking space from the content above; the selected height is retained across collapse and navigation. The focused divider also accepts Up/Down, Home and End. Its iframe loads on the first open and remains mounted while collapsed and across Session or main-panel navigation; unloading the component resets it. The X button and shortcut restore the opening control's focus when that control remains connected. The iframe automatically connects to this Host and opens Console; the manual Connection panel is omitted. Each embedded frontend shows Host and only its embedding page's Client in Console, Sources and the Cordis tree. The printed `devtools://` URL shows all Clients. The Host serves the mirrored DevTools frontend at the application-relative `inspector/devtools/devtools_app.html` path, using the existing Web login. The `inspector/devtools` prefix redirects to that entry; GET and HEAD serve files, missing resources return 404, and other methods return 405. Its `cdp` WebSocket route authenticates the same browser session and forwards only to this Inspector's Worker page target. The bottom panel supplies its claimed `clientSourceId` to the entry and automatic WebSocket endpoint; the selection follows that logical Client across reconnects. An absent selector includes all Clients, an unavailable selected Client leaves Host visible, and empty or repeated selectors reject the upgrade with 400. Explicit `ws` or `wss` query parameters override the automatic endpoint, including its selection. diff --git a/packages/experimental/inspector/locale/en.json b/packages/experimental/inspector/locale/en.json index 51f965efc6..a41eb0a6ba 100644 --- a/packages/experimental/inspector/locale/en.json +++ b/packages/experimental/inspector/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "NodeJS Inspector", + "title": "NodeJS inspector", "description": "Press Ctrl/Cmd+Shift+. to open Chrome DevTools in the bottom panel for development and debugging." } } diff --git a/packages/experimental/inspector/src/client/bottom/locales.ts b/packages/experimental/inspector/src/client/bottom/locales.ts index e6d6c05711..9387c60c96 100644 --- a/packages/experimental/inspector/src/client/bottom/locales.ts +++ b/packages/experimental/inspector/src/client/bottom/locales.ts @@ -1,4 +1,4 @@ -/** Localized labels for the NodeJS Inspector bottom panel and command. */ +/** Localized labels for the NodeJS inspector bottom panel and command. */ export const zh = { title: 'NodeJS 诊断', toggle: '展开或收起 NodeJS 诊断', @@ -9,11 +9,11 @@ export const zh = { /** English labels checked against the bottom-panel dictionary. */ export const en: Record = { - title: 'NodeJS Inspector', - toggle: 'Toggle NodeJS Inspector', + title: 'NodeJS inspector', + toggle: 'Toggle NodeJS inspector', close: 'Collapse', - resize: 'Resize NodeJS Inspector panel', - frameTitle: 'NodeJS Inspector', + resize: 'Resize NodeJS inspector panel', + frameTitle: 'NodeJS inspector', } declare module '@deepseek-ai/dsh-client-ui-slots' { diff --git a/packages/experimental/inspector/tests/bottom.client.spec.tsx b/packages/experimental/inspector/tests/bottom.client.spec.tsx index da27215e2a..5900a7f790 100644 --- a/packages/experimental/inspector/tests/bottom.client.spec.tsx +++ b/packages/experimental/inspector/tests/bottom.client.spec.tsx @@ -50,7 +50,7 @@ it('opens on Mod+Shift+Period without a Session or Sidebar and retains the ifram expect(press(window, { ctrlKey: false }).defaultPrevented).toBe(false) expect(body.container.querySelector('iframe')).toBeNull() expect(press().defaultPrevented).toBe(true) - const frame = body.view.getByTitle('NodeJS Inspector') + const frame = body.view.getByTitle('NodeJS inspector') expect(frame.getAttribute('src')).toBe('inspector/devtools/devtools_app.html?disableLocaleInfoBar=true&clientSourceId=client-page') expect(frame.getAttribute('referrerpolicy')).toBe('no-referrer') expect(frame.getAttribute('name')).toBe('dsh-nodejs-inspector') @@ -62,7 +62,7 @@ it('opens on Mod+Shift+Period without a Session or Sidebar and retains the ifram press() expect(body.container.querySelector('section')?.hidden).toBe(true) press() - expect(body.view.getByTitle('NodeJS Inspector')).toBe(frame) + expect(body.view.getByTitle('NodeJS inspector')).toBe(frame) expect(body.container.querySelector('section')?.hidden).toBe(false) }) @@ -70,8 +70,8 @@ it('resizes with the owned pointer, bounds the height, and retains it across col const { body } = await setup() press() const panel = body.container.querySelector('section')! - const frame = body.view.getByTitle('NodeJS Inspector') - const divider = body.view.getByRole('separator', { name: 'Resize NodeJS Inspector panel' }) + const frame = body.view.getByTitle('NodeJS inspector') + const divider = body.view.getByRole('separator', { name: 'Resize NodeJS inspector panel' }) let captured: number | undefined Object.assign(divider, { setPointerCapture: (id: number) => { captured = id }, @@ -113,7 +113,7 @@ it('resizes with the owned pointer, bounds the height, and retains it across col press() press() expect(height()).toBe(20) - expect(body.view.getByTitle('NodeJS Inspector')).toBe(frame) + expect(body.view.getByTitle('NodeJS inspector')).toBe(frame) }) it('resizes the horizontal divider with arrow keys and Home or End', async () => { @@ -137,7 +137,7 @@ it('routes frame input through the effective binding and restores opener focus', try { opener.focus() press(opener) - const frame = body.view.getByTitle('NodeJS Inspector') + const frame = body.view.getByTitle('NodeJS inspector') const document = frame.contentDocument! frame.focus() expect(press(document).defaultPrevented).toBe(true) @@ -168,7 +168,7 @@ it('keeps modal arbitration and updates locale without replacing the iframe', as expect(body.container.querySelector('iframe')).toBeNull() } finally { modal.remove() } press() - const frame = body.view.getByTitle('NodeJS Inspector') + const frame = body.view.getByTitle('NodeJS inspector') act(() => { locale.setLocale('zh') }) expect(body.view.getByTitle('NodeJS 诊断')).toBe(frame) act(() => { body.view.getByRole('button', { name: '收起' }).click() }) @@ -178,7 +178,7 @@ it('keeps modal arbitration and updates locale without replacing the iframe', as it('removes the command and frame on unload and starts a fresh closed panel on reload', async () => { const { runtime, locale, provider, body, plugin } = await setup() press() - const frame = body.view.getByTitle('NodeJS Inspector') + const frame = body.view.getByTitle('NodeJS inspector') await provider.dispose() expect(runtime.ctx.shortcuts.catalog.getSnapshot().find(row => row.id === ID)).toBeUndefined() expect(frame.isConnected).toBe(false) @@ -186,5 +186,5 @@ it('removes the command and frame on unload and starts a fresh closed panel on r await runtime.mount(plugin) expect(body.container.querySelector('iframe')).toBeNull() press() - expect(body.view.getByTitle('NodeJS Inspector')).not.toBe(frame) + expect(body.view.getByTitle('NodeJS inspector')).not.toBe(frame) }) diff --git a/packages/experimental/session-inspector/README.i18n.yaml b/packages/experimental/session-inspector/README.i18n.yaml index 88bab45bae..c30efa655b 100644 --- a/packages/experimental/session-inspector/README.i18n.yaml +++ b/packages/experimental/session-inspector/README.i18n.yaml @@ -9,13 +9,13 @@ en: 151ad1f553a733d7 zh: b2992178863a80c7 /deepseek-ai-dsh-experimental-session-inspector/summary: - en: 9ba1ce1f7f3c36ff + en: da1f12910d37aa6e zh: a6aaf26eb598a80a /deepseek-ai-dsh-experimental-session-inspector/table-of-contents: en: 76368b6b1da79c80 zh: 52384aee991ac542 /deepseek-ai-dsh-experimental-session-inspector/use-this-package: - en: a54a88121496d7d0 + en: f8ee16e89f8b1d07 zh: 85fb112b26ec0ccb /deepseek-ai-dsh-experimental-session-inspector/understand-the-implementation: en: 6d71dcef4dca024b @@ -27,7 +27,7 @@ en: 258d6659c6cb3789 zh: a6517f274c596a7a /deepseek-ai-dsh-experimental-session-inspector/known-limitations-and-deferred-work: - en: bf2b68c4be0d9d6f + en: 042b2cc65c722176 zh: 1530b5fa68f2baa1 /deepseek-ai-dsh-experimental-session-inspector/known-limitations-and-deferred-work/dev-note: en: e67f633dc6fe7773 diff --git a/packages/experimental/session-inspector/README.md b/packages/experimental/session-inspector/README.md index 76a8c719f2..932e3916a3 100644 --- a/packages/experimental/session-inspector/README.md +++ b/packages/experimental/session-inspector/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Inspect raw Session logs and Chat structures through the optional [Developer Tools bundle](../inspector-profile/README.md). Open **Session Log** from the Sidebar's new-tab menu or guide, then choose **Raw Log** or **Chat Group** from its upper-left selector. Follow object references between Nodes, Turns, and Steps, or locate a selected Node in Chat. Stream chunks appear as child rows, and Chat updates briefly highlight their rows. Both presentations support older history and resizable details without a composer. +Inspect raw Session logs and Chat structures through the optional [Developer tools bundle](../inspector-profile/README.md). Open **Session log** from the Sidebar's new-tab menu or guide, then choose **Raw log** or **Chat group** from its upper-left selector. Follow object references between Nodes, Turns, and Steps, or locate a selected Node in Chat. Stream chunks appear as child rows, and Chat updates briefly highlight their rows. Both presentations support older history and resizable details without a composer. ## Table of Contents @@ -24,39 +24,39 @@ Inspect raw Session logs and Chat structures through the optional [Developer Too ## Use this package -The Developer Tools bundle mounts this plugin as `session-inspector`. It registers the `session-inspector-log` Sidebar page for the Sidebar's current Session, without a Conversation View or header label. The selector initially shows Session Log, with the current row count alongside it. Changing presentation resets local selection, type filtering, and scroll state. This plugin requires no debugging argument, accepts no configuration, and changes no Session data. +The Developer tools bundle mounts this plugin as `session-inspector`. It registers the `session-inspector-log` Sidebar page for the Sidebar's current Session, without a Conversation View or header label. The selector initially shows Raw log, with the current row count alongside it. Changing presentation resets local selection, type filtering, and scroll state. This plugin requires no debugging argument, accepts no configuration, and changes no Session data. Click the filter icon in the Type header to enter a type fragment. Suggestions update asynchronously from all loaded records. Enter or **Apply filter** confirms the fragment; clicking a suggestion or selecting it with arrow keys and Enter confirms that type. Matching ignores case and uses containment: `delta` and `*delta*` both match reasoning and tool-call deltas. Editing alone does not change the table. Escape, Cancel, or clicking outside discards the draft; clearing and confirming restores all types. Filtered tables retain matching rows and their ancestor context, initially expanding folded ancestors. Context-only types appear muted and do not count toward the matching-row total. New and paginated rows use the confirmed filter. Confirming resets selection, disclosure choices, and reserved trailing space, and returns to the top. Following an object reference or picking a Chat element clears the filter if its target row is excluded, then reveals that row. -Session Log groups records between `turn/start` and `turn/end`, with nested `step/start`–`step/end` ranges. The start record is the sticky group header; the end remains a raw child at the group's tail. Turn and Step groups stay expanded after completion and can be folded manually. Records between Steps belong to the Turn; records outside a loaded range remain ungrouped. Missing starts are not synthesized: loading an earlier start adds ancestry to the existing rows. Assistant streams retain their own nesting inside the Step. +Raw log groups records between `turn/start` and `turn/end`, with nested `step/start`–`step/end` ranges. The start record is the sticky group header; the end remains a raw child at the group's tail. Turn and Step groups stay expanded after completion and can be folded manually. Records between Steps belong to the Turn; records outside a loaded range remain ungrouped. Missing starts are not synthesized: loading an earlier start adds ancestry to the existing rows. Assistant streams retain their own nesting inside the Step. -Assistant logs group original timed chunks beneath their matching `block-start`, including text, reasoning, and tool-call argument deltas. Unfinished blocks expand by default; `block-end` automatically collapses its block, which can then be reopened manually. Historical closed blocks start collapsed. Live chunks share an attempt row until settlement replaces them with the durable event. Chat Group nests actual group members below their group. Hidden or unlisted materialized Nodes remain root rows, inserted by event anchor between the existing roots instead of appended after the last Turn; the Inspector does not invent group membership. Inserts and updates briefly highlight unless the system requests reduced motion. +Assistant logs group original timed chunks beneath their matching `block-start`, including text, reasoning, and tool-call argument deltas. Unfinished blocks expand by default; `block-end` automatically collapses its block, which can then be reopened manually. Historical closed blocks start collapsed. Live chunks share an attempt row until settlement replaces them with the durable event. Chat group nests actual group members below their group. Hidden or unlisted materialized Nodes remain root rows, inserted by event anchor between the existing roots instead of appended after the last Turn; the Inspector does not invent group membership. Inserts and updates briefly highlight unless the system requests reduced motion. Use the disclosure arrow to fold children and select a row's type or data to inspect raw details. Horizontal data previews omit `type`, `seq`, and `sequence`, plus top-level `time`. Chat Node also omits top-level `key` and `kind`, which already have their own columns. When one field remains, only its value appears. Tool-call delta previews show only the argument fragment; reasoning delta previews show only the text. Preview filtering does not remove fields from raw details. Drag the detail panel's upper separator, or use its Up/Down keys, to resize it; selecting another row retains its height. A folded reasoning `block-start` omits `index` and shows `blockType: reasoning` followed by its joined reasoning deltas, within the normal preview length. Interleaved blocks keep separate summaries. Expanding the block restores its ordinary fields and original child rows; raw details retain the original block-start without synthesized text. -Chat details show a lazy object tree rather than JSON conversion. Maps retain object keys and values, Sets and arrays show members, and class instances retain their names and own fields. Collections expand in pages; getters are labelled without execution, and WeakMap/WeakSet contents remain opaque. The inspected root expands inline; unregistered cycles show their ancestor path. Expanded fields are cached until a Conversation publication changes, so mutable Turn/Step Data refreshes without resetting disclosure state or repeating enumeration for UI-only renders. +Chat details show a lazy object tree rather than JSON conversion. Maps retain object keys and values, Sets and arrays show members, and class instances retain their names and own fields. Collections expand in pages; getters are labelled without execution, and WeakMap/WeakSet contents remain opaque. The inspected root expands inline; unregistered cycles show their ancestor path. Expanded fields are cached until a Conversation publication changes, so mutable Turn/Step data refreshes without resetting disclosure state or repeating enumeration for UI-only renders. Arrays and TypedArrays read only the disclosed index prefix, including sparse slots marked separately from explicit `undefined`. **Other properties** opens their non-index and symbol fields without invoking getters. The initial numeric page does not enumerate the whole collection; opening Other properties explicitly scans its own keys. -Nested Node, Group, Turn, Step, and Data objects have reference buttons. Activating a reference opens its current loaded value and adds a breadcrumb; selecting an earlier breadcrumb returns to that level and discards the later path. The first breadcrumb retains the original row even when reference navigation selects another table row. A reference with a row unfolds its enclosing Inspector group, scrolls to the row, and flashes it. Node Data also has an inline disclosure arrow: expanding it stays in the current tree, while clicking its title navigates. Repeated Node Data ancestors remain links rather than recursively expanding. Selecting a table row or a picked Chat element starts a new breadcrumb path; Session Log keeps JSON details without reference navigation. +Nested Node, Group, Turn, Step, and Data objects have reference buttons. Activating a reference opens its current loaded value and adds a breadcrumb; selecting an earlier breadcrumb returns to that level and discards the later path. The first breadcrumb retains the original row even when reference navigation selects another table row. A reference with a row unfolds its enclosing Inspector group, scrolls to the row, and flashes it. Node data also has an inline disclosure arrow: expanding it stays in the current tree, while clicking its title navigates. Repeated Node data ancestors remain links rather than recursively expanding. Selecting a table row or a picked Chat element starts a new breadcrumb path; Raw log keeps JSON details without reference navigation. Selecting a Chat row or following a reference uses the Inspector's [DOM matcher](src/client/views/chat-node/dom.ts) to find existing Node, call, Group, or Turn attributes in the same Session's displayed main Chat. Inspector dispatches the existing `beforematch` behavior to searchable folded ancestors, then uses native scrolling and a body-level highlight overlay without adding elements to Chat content. A visible target only flashes; an offscreen target scrolls smoothly before flashing, with immediate scrolling and no flash for reduced motion. ID columns remain narrow; hover or focus an ID to read its complete value in a tooltip. Missing, empty, or still-hidden occurrences do not stop navigation. When event coordinates are available, Inspector tries nearby loaded content in the same Step, then the same Turn, then other loaded Turns, ordered by event distance. Existing Group and Turn occurrences also provide fallback positions. If no candidate can render, Inspector highlights the visible Chat column without moving it. -Selecting a Session Log row passes the [original log coordinates](src/client/views/session-log/chat-target.ts) through the same DOM lookup while retaining the log presentation and JSON details. Inspector reads the existing Chat snapshot and prefers tool call identity, exact event anchors, and the owning Assistant step before applying the same fallback selection. Stream children inherit their owning event and prefer reasoning or response parts when available. Missing targets do not load history or switch the active View; an empty Chat has nothing to highlight. +Selecting a Raw log row passes the [original log coordinates](src/client/views/session-log/chat-target.ts) through the same DOM lookup while retaining the log presentation and JSON details. Inspector reads the existing Chat snapshot and prefers tool call identity, exact event anchors, and the owning Assistant step before applying the same fallback selection. Stream children inherit their owning event and prefer reasoning or response parts when available. Missing targets do not load history or switch the active View; an empty Chat has nothing to highlight. The crosshair beside the presentation selector installs a transparent click-catching mask over the displayed main Chat. Inspector uses `elementsFromPoint()` to look below its mask and preview, then matches existing data attributes. A click selects that record in the current Inspector presentation, reveals its table ancestors, and opens its details without activating the underlying Chat control. Wheel movement is forwarded to the underlying Chat scrollport. Escape, the crosshair button, changing presentation or Session, hiding or closing the Inspector, or leaving the window cancels picking. Each plugin instance permits one active picker; separate instances do not cancel one another. Plugin unload releases its picker without calling panel callbacks and prevents old panels from restarting it. -Chat picking prefers the exact Node and reasoning/response part, then its Group, Step, or Turn. Session Log picking prefers a matching tool call or Node log anchor, then its Assistant, Step, or Turn record; a reasoning part selects its first reasoning block when available. Picking stays active with a notice if the loaded table has no match. Text similarity is not used. The [element and Chat-row matchers](src/client/views/chat-node/pick-match.ts) and the [log model's picker](src/client/views/session-log/model.ts) own these adjustable rules; nested tool calls may resolve to their root Node or log anchor. +Chat picking prefers the exact Node and reasoning/response part, then its Group, Step, or Turn. Raw log picking prefers a matching tool call or Node log anchor, then its Assistant, Step, or Turn record; a reasoning part selects its first reasoning block when available. Picking stays active with a notice if the loaded table has no match. Text similarity is not used. The [element and Chat-row matchers](src/client/views/chat-node/pick-match.ts) and the [log model's picker](src/client/views/session-log/model.ts) own these adjustable rules; nested tool calls may resolve to their root Node or log anchor. -Session Log uses the same field renderer for parsed JSON, with every nested object and array initially expanded. Containers can be collapsed individually; there are no reference buttons, breadcrumbs, collection-property disclosures, or collection paging. JSON retains `[Circular]` markers and readable bigint strings. Inspection failures appear locally without disabling the table. Preview filtering never removes fields from either detail presentation. +Raw log uses the same field renderer for parsed JSON, with every nested object and array initially expanded. Containers can be collapsed individually; there are no reference buttons, breadcrumbs, collection-property disclosures, or collection paging. JSON retains `[Circular]` markers and readable bigint strings. Inspection failures appear locally without disabling the table. Preview filtering never removes fields from either detail presentation. -Ancestor rows remain sticky beneath the table header, one nesting level below another, with an opaque tinted background. Each level follows the first row not covered by the header or earlier sticky levels. Automatic block closure keeps the visible row or surviving parent at its screen position and leaves unused space only below the data; new rows fill that space before normal tail following resumes. A manual disclosure keeps the clicked header at its measured viewport position and pauses following, retaining only the trailing space needed to keep it visible. **Follow Latest** clears reserved space and resumes following. Scrolling upward or choosing **Load earlier** also pauses following. Raw Log shows UTC timestamps with the server's millisecond precision; Chat Group has no time column. +Ancestor rows remain sticky beneath the table header, one nesting level below another, with an opaque tinted background. Each level follows the first row not covered by the header or earlier sticky levels. Automatic block closure keeps the visible row or surviving parent at its screen position and leaves unused space only below the data; new rows fill that space before normal tail following resumes. A manual disclosure keeps the clicked header at its measured viewport position and pauses following, retaining only the trailing space needed to keep it visible. **Follow latest** clears reserved space and resumes following. Scrolling upward or choosing **Load earlier** also pauses following. Raw log shows UTC timestamps with the server's millisecond precision; Chat group has no time column. ----- @@ -88,7 +88,7 @@ None; the package does not modify model requests. - **Loaded history** — tables inspect the Session's loaded window; older records require pagination. Live-to-durable row identities persist across window rebuilds and are released when their rows leave the window. -- **Projection controls** — Session Log does not display projection control-stream updates. +- **Projection controls** — Session log does not display projection control-stream updates. - **Publication cadence** — highlighting follows published Node changes, not every internal mutation between publications. Raw data is unredacted. - **Large windows** — each publication still copies the immutable row-reference array; filtering and table layout also scan the loaded rows. Appends do not regroup or recreate historical rows. Opening ordinary-object fields or Other properties scans all own keys. - **Live suggestions** — refreshing type candidates resets keyboard selection, even for an unchanged candidate list; reselect the candidate or click it before confirming. diff --git a/packages/experimental/session-inspector/locale/en.json b/packages/experimental/session-inspector/locale/en.json index c121fc58ba..601be55e25 100644 --- a/packages/experimental/session-inspector/locale/en.json +++ b/packages/experimental/session-inspector/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Session Log", + "title": "Session log", "description": "Enable analysis of the current session's raw data and grouped chat messages in the session sidebar." } } diff --git a/packages/experimental/session-inspector/src/client/locales.ts b/packages/experimental/session-inspector/src/client/locales.ts index 5a02c89b79..8a07ea5e95 100644 --- a/packages/experimental/session-inspector/src/client/locales.ts +++ b/packages/experimental/session-inspector/src/client/locales.ts @@ -71,16 +71,16 @@ declare module '@deepseek-ai/dsh-client-ui-slots' { /** English labels, checked against the Chinese key set. */ export const en: Record = { - 'tab.title': 'Session Log', + 'tab.title': 'Session log', 'tab.description': 'Analyze raw logs and grouped chat messages for the current session in the sidebar.', 'view.presentation': 'Presentation', - 'view.sessionLog': 'Raw Log', - 'view.chatNode': 'Chat Group', - 'picker.pick': 'Pick an element in Chat View', + 'view.sessionLog': 'Raw log', + 'view.chatNode': 'Chat group', + 'picker.pick': 'Pick an element in the chat view', 'picker.cancel': 'Cancel picking (Esc)', - 'picker.instructions': 'Hover in the main Chat to preview, then click to select. Esc cancels.', + 'picker.instructions': 'Hover in the main chat to preview, then click to select. Esc cancels.', 'picker.noMatch': 'No matching record in this table. Pick another element or press Esc to cancel.', - 'picker.noChat': 'Open this Session in the main Chat before picking.', + 'picker.noChat': 'Open this session in the main chat before picking.', 'table.id': 'Index', 'table.type': 'Type', 'filter.title': 'Filter types', @@ -100,7 +100,7 @@ export const en: Record = { 'table.data': 'Data', 'table.older': 'Load earlier', 'table.loading': 'Loading…', - 'table.latest': 'Follow Latest', + 'table.latest': 'Follow latest', 'table.empty': 'No records', 'table.raw': 'Raw data', 'table.rawError': 'This record could not be serialized. Select another record to continue.', @@ -110,14 +110,14 @@ export const en: Record = { 'table.collapse': 'Collapse children', 'table.removed': 'This record was removed or settled. Select a current record.', 'object.node': 'Node', - 'object.nodeData': 'Node Data', + 'object.nodeData': 'Node data', 'object.group': 'Group', - 'object.groupData': 'Group Data', + 'object.groupData': 'Group data', 'object.turn': 'Turn', - 'object.turnData': 'Turn Data', + 'object.turnData': 'Turn data', 'object.step': 'Step', - 'object.stepData': 'Step Data', - 'object.accessor': '[Getter / Setter, not evaluated]', + 'object.stepData': 'Step data', + 'object.accessor': '[Getter / setter, not evaluated]', 'object.empty': 'Empty', 'object.absent': '[Empty slot]', 'object.properties': 'Other properties', diff --git a/packages/experimental/session-inspector/src/client/views/InspectorObjectTree.tsx b/packages/experimental/session-inspector/src/client/views/InspectorObjectTree.tsx index 7417df14b0..9665cc83bd 100644 --- a/packages/experimental/session-inspector/src/client/views/InspectorObjectTree.tsx +++ b/packages/experimental/session-inspector/src/client/views/InspectorObjectTree.tsx @@ -106,8 +106,8 @@ export function InspectorJsonTree({ text, t }: PropsLocale<'session-inspector'> } /** - * Inspect the root and nested Node Data inline; other registered objects remain navigation links. - * Node Data titles also navigate, while repeated ancestors never expand recursively. + * Inspect the root and nested Node data inline; other registered objects remain navigation links. + * Node data titles also navigate, while repeated ancestors never expand recursively. * Expanded fields are cached until their value or Conversation publication changes. * @param props - Original object, weak identity lookup, locale, and reference activation. * @returns A lazily expanded, typed tree that retains Map keys and collection identities. diff --git a/packages/experimental/session-inspector/tests/object-tree.client.spec.tsx b/packages/experimental/session-inspector/tests/object-tree.client.spec.tsx index c97e8f2899..b3d3e05d63 100644 --- a/packages/experimental/session-inspector/tests/object-tree.client.spec.tsx +++ b/packages/experimental/session-inspector/tests/object-tree.client.spec.tsx @@ -143,7 +143,7 @@ it('expands Node Data inline but keeps its title navigable and cyclic references target: { nodeKey: 'node-1' }, read: () => data } props.index.set(data, reference) const ui = render() - const link = ui.getByRole('button', { name: '↗ Node Data · 1' }) + const link = ui.getByRole('button', { name: '↗ Node data · 1' }) const branch = link.closest('details')! expect(branch.open).toBe(false) expect(ui.queryByText('"inline node data"')).toBeNull() @@ -151,7 +151,7 @@ it('expands Node Data inline but keeps its title navigable and cyclic references fireEvent(branch, new Event('toggle')) expect(ui.getByText('"inline node data"')).toBeTruthy() expect(props.navigate).not.toHaveBeenCalled() - expect(ui.getAllByRole('button', { name: '↗ Node Data · 1' })).toHaveLength(2) + expect(ui.getAllByRole('button', { name: '↗ Node data · 1' })).toHaveLength(2) expect(ui.container.querySelectorAll('details')).toHaveLength(2) fireEvent.click(link) expect(props.navigate).toHaveBeenCalledExactlyOnceWith(reference) diff --git a/packages/experimental/session-search/README.i18n.yaml b/packages/experimental/session-search/README.i18n.yaml index 9e76d6fea8..dac818a47a 100644 --- a/packages/experimental/session-search/README.i18n.yaml +++ b/packages/experimental/session-search/README.i18n.yaml @@ -3,7 +3,7 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/experimental/session-search/README.md /: - en: ac0fb71830f58337 + en: c8e3f38d9f1a4e2f zh: 0ece950cf48261a1 /deepseek-ai-dsh-experimental-session-search: en: f4f9c83d1dd36580 diff --git a/packages/experimental/session-search/README.md b/packages/experimental/session-search/README.md index 1feee8ec67..5ca11a0a81 100644 --- a/packages/experimental/session-search/README.md +++ b/packages/experimental/session-search/README.md @@ -1,5 +1,5 @@ --- -description: "Give Agents tools to search and read earlier sessions from the current workspace." +description: "Give agents tools to search and read earlier sessions from the current workspace." kind: "package-bundle" --- diff --git a/packages/experimental/session-search/locale/en.json b/packages/experimental/session-search/locale/en.json index 062c34fca3..712a4d1697 100644 --- a/packages/experimental/session-search/locale/en.json +++ b/packages/experimental/session-search/locale/en.json @@ -1,7 +1,7 @@ { "meta": { "title": "Session search", - "description": "Give Agents tools to search and read earlier sessions from the current workspace.", + "description": "Give agents tools to search and read earlier sessions from the current workspace.", "detail": "docs/DETAIL.md" } } diff --git a/packages/experimental/session-search/package.json b/packages/experimental/session-search/package.json index c4ebf0a594..02be68de4c 100644 --- a/packages/experimental/session-search/package.json +++ b/packages/experimental/session-search/package.json @@ -1,6 +1,6 @@ { "name": "@deepseek-ai/dsh-experimental-session-search", - "description": "Give Agents tools to search and read earlier sessions from the current workspace.", + "description": "Give agents tools to search and read earlier sessions from the current workspace.", "version": "0.2.1-rc.2", "icon": "./icon.svg", "publishConfig": { diff --git a/packages/experimental/tool-agent-team/locale/en.json b/packages/experimental/tool-agent-team/locale/en.json index f517900c98..80aaa02ab8 100644 --- a/packages/experimental/tool-agent-team/locale/en.json +++ b/packages/experimental/tool-agent-team/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Team Tools", + "title": "Team tools", "description": "Give agents tools to coordinate members, exchange messages, and manage shared tasks." } } diff --git a/packages/experimental/tool-worktree/README.i18n.yaml b/packages/experimental/tool-worktree/README.i18n.yaml index 8920ad53de..33ca340c2f 100644 --- a/packages/experimental/tool-worktree/README.i18n.yaml +++ b/packages/experimental/tool-worktree/README.i18n.yaml @@ -9,13 +9,13 @@ en: db61f6b6ad671f45 zh: 9f3083d7f5605cbb /deepseek-ai-dsh-experimental-tool-worktree/summary: - en: f0df470fd16d8d29 + en: addc1cb3414df6be zh: 5f2d371d844b0568 /deepseek-ai-dsh-experimental-tool-worktree/table-of-contents: en: d152484eb41ac6b4 zh: 2f8a621b991260a1 /deepseek-ai-dsh-experimental-tool-worktree/use-this-package: - en: f69d3fdeed949ff5 + en: 06074f9f6989911b zh: 05c9a85b7cdcfbad /deepseek-ai-dsh-experimental-tool-worktree/understand-the-implementation: en: c03f875211ab69a8 diff --git a/packages/experimental/tool-worktree/README.md b/packages/experimental/tool-worktree/README.md index 9ce2da182f..b614aa6a8e 100644 --- a/packages/experimental/tool-worktree/README.md +++ b/packages/experimental/tool-worktree/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Add Git Worktrees from the GUI plugin manager to let a model create and enter a new Git worktree with one call. This optional bundle is installed with Harness and stays off until selected. Native tool calls and PTC programs receive the same typed result, and the existing file policy governs creation. +Add Git worktrees from the GUI plugin manager to let a model create and enter a new Git worktree with one call. This optional bundle is installed with Harness and stays off until selected. Native tool calls and PTC programs receive the same typed result, and the existing file policy governs creation. ## Table of Contents @@ -25,7 +25,7 @@ Add Git Worktrees from the GUI plugin manager to let a model create and enter a ## Use this package -In the GUI, open **Plugins → Official → Git Worktrees** and turn on the switch. The bundle loads the [worktree runtime](../worktree/README.md) and this tool together; it is off in every shipped profile. Turning it off removes `create_worktree` without deleting existing checkouts or branches. The built-in `working_directory` tool remains available. +In the GUI, open **Plugins → Official → Git worktrees** and turn on the switch. The bundle loads the [worktree runtime](../worktree/README.md) and this tool together; it is off in every shipped profile. Turning it off removes `create_worktree` without deleting existing checkouts or branches. The built-in `working_directory` tool remains available. For an explicit composition, mount this function plugin after the worktree runtime and tool registry. diff --git a/packages/experimental/tool-worktree/locale/en.json b/packages/experimental/tool-worktree/locale/en.json index 4d2ed41bc0..5d58b91ae1 100644 --- a/packages/experimental/tool-worktree/locale/en.json +++ b/packages/experimental/tool-worktree/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Git Worktrees", + "title": "Git worktrees", "description": "Let the agent create a new Git branch and worktree, then continue there.", "detail": "docs/DETAIL.md" } diff --git a/packages/preset/agent-preset/skills/cordis-plugin-development/references/host-plugin.md b/packages/preset/agent-preset/skills/cordis-plugin-development/references/host-plugin.md index fe88761ae4..b2de52dc8e 100644 --- a/packages/preset/agent-preset/skills/cordis-plugin-development/references/host-plugin.md +++ b/packages/preset/agent-preset/skills/cordis-plugin-development/references/host-plugin.md @@ -30,7 +30,7 @@ A Host-only bundle needs no dependencies, install scripts, or build tool: Plugin Manager and Settings read `meta.title` and `meta.description` from exported locale JSON and an icon from exported resources without activating plugins. Complete this checklist before installation: -- Write a recognizable title and a description of the capability in `locale/en.json` under `meta.title` and `meta.description`. Add the same fields for the user's language and other supported locales, such as `locale/zh.json`; do not leave template copy unrelated to the delivered plugin. +- Write a recognizable title and a description of the capability in `locale/en.json` under `meta.title` and `meta.description`. Add the same fields for the user's language and other supported locales, such as `locale/zh.json`; do not leave template copy unrelated to the delivered plugin. Write English titles and other UI labels in sentence case: capitalize only the first word and proper nouns, as in `Git worktrees`. - Create an original icon or use artwork whose license permits redistribution, retaining any required attribution. - Export `./locale/*.json` and `./icon`; retain existing runtime exports. Keep the patch, locales, icon, and every runtime file in `files`, updating it whenever you add modules or assets. For a packed or published bundle, verify the actual packed file list. Local directory installation links the checkout instead, so check the files directly; `files` does not filter a linked directory. - Check that the icon is a valid image of the declared format and satisfies the path and size limits below. An accepted filename alone does not establish that its bytes render. @@ -38,7 +38,7 @@ Plugin Manager and Settings read `meta.title` and `meta.description` from export For example, the locale file contains: ```json -{ "meta": { "title": "My Decoration", "description": "Draws a badge under the composer." } } +{ "meta": { "title": "My decoration", "description": "Draws a badge under the composer." } } ``` ```json diff --git a/packages/preset/agent-preset/skills/cordis-plugin-development/templates/decoration/locale/en.json b/packages/preset/agent-preset/skills/cordis-plugin-development/templates/decoration/locale/en.json index 28dfb853d7..3a3e18d25c 100644 --- a/packages/preset/agent-preset/skills/cordis-plugin-development/templates/decoration/locale/en.json +++ b/packages/preset/agent-preset/skills/cordis-plugin-development/templates/decoration/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Composer Decoration", + "title": "Composer decoration", "description": "Displays a blue circle below the message composer." } } diff --git a/packages/preset/agent-preset/skills/cordis-plugin-development/templates/mcp/locale/en.json b/packages/preset/agent-preset/skills/cordis-plugin-development/templates/mcp/locale/en.json index 1498e59800..9fd546fa2a 100644 --- a/packages/preset/agent-preset/skills/cordis-plugin-development/templates/mcp/locale/en.json +++ b/packages/preset/agent-preset/skills/cordis-plugin-development/templates/mcp/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Demo MCP Connection", + "title": "Demo MCP connection", "description": "Connects a remote MCP server to make its tools available to the agent." } } diff --git a/snapshots/web/session-inspector/log.expected.md b/snapshots/web/session-inspector/log.expected.md index 6f82049736..4621741cb4 100644 --- a/snapshots/web/session-inspector/log.expected.md +++ b/snapshots/web/session-inspector/log.expected.md @@ -1,4 +1,4 @@ -- table "Raw Log": +- table "Raw log": - rowgroup: - row "Index Type Filter types T/S Time (UTC) Data": - columnheader "Index"