diff --git a/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.i18n.yaml b/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.i18n.yaml index 0409e65f17..f156fed8f2 100644 --- a/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.i18n.yaml @@ -9,8 +9,8 @@ en: ca2848e3bbc3a009 zh: 0551e32ff6d187d0 /agent-note-agent-teams-teammates-are-subagents/decision: - en: b639631e54322fd9 - zh: b80c1dad17f9afd1 + en: 5742777c069a9bb6 + zh: ffddb9c47183da7e /agent-note-agent-teams-teammates-are-subagents/alternatives-considered: en: ef0cf648ff54784f zh: 2a0b3745a54e7072 diff --git a/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.md b/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.md index 29fb17bd56..6003614631 100644 --- a/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.md +++ b/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.md @@ -18,7 +18,7 @@ Every root Agent leads the Team identified by its Session id; every live subagen Task ownership uses `ownerId`, and reassignment accepts a Session id. Team persists only task changes. Messages use the target Agent inbox; historical `team/message/*` records remain readable but are never delivered or acknowledged. -This supersedes the roster, provisioning, recovery, and messaging decisions in [Agent Teams](../feature/2026-08-05-agent-teams.md) and the [Team direct inbox decision](../../archived/simplification/2026-09-26-team-direct-inbox.md). The task-board and shared-checkout decisions remain active. [Adjacent Agent messaging](2026-08-27-adjacent-agent-steer-messaging.md) includes sibling routing, while [required runtime-context identity](2026-10-13-team-identity-runtime-context.md) owns the role statement. The [upgrade guide](../../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md) lists removed tools, methods, and configuration. +This supersedes the roster, provisioning, recovery, and messaging decisions in [Agent Teams](../feature/2026-08-05-agent-teams.md) and the [Team direct inbox decision](../../archived/simplification/2026-09-26-team-direct-inbox.md). The task-board and shared-checkout decisions remain active. [Adjacent Agent messaging](2026-08-27-adjacent-agent-steer-messaging.md) includes sibling routing, while [required runtime-context identity](2026-10-13-team-identity-runtime-context.md) owns the role statement. The [upgrade guide](../../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md) lists removed tools, methods, and configuration. ## Alternatives considered diff --git a/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.zh.md b/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.zh.md index 52d4f7d6eb..7a49c11e4f 100644 --- a/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.zh.md +++ b/.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.zh.md @@ -18,7 +18,7 @@ Status: implemented 任务所有权使用 `ownerId`,重新分配接受 Session id。Team 只持久化任务变更。消息使用目标 Agent 收件箱;历史 `team/message/*` 记录仍可读取,但不会被投递或确认。 -本决策取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 的成员列表、创建、恢复和消息决策,以及 [Team 直接收件箱决策](../../archived/simplification/2026-09-26-team-direct-inbox.md)。任务板和共享工作树决策仍有效。[相邻 Agent 消息](2026-08-27-adjacent-agent-steer-messaging.zh.md)纳入同级路由,[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)负责角色声明。[升级指南](../../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md)列出移除的工具、方法和配置。 +本决策取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 的成员列表、创建、恢复和消息决策,以及 [Team 直接收件箱决策](../../archived/simplification/2026-09-26-team-direct-inbox.md)。任务板和共享工作树决策仍有效。[相邻 Agent 消息](2026-08-27-adjacent-agent-steer-messaging.zh.md)纳入同级路由,[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)负责角色声明。[升级指南](../../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md)列出移除的工具、方法和配置。 ## Alternatives considered diff --git a/apps/cli/package.json b/apps/cli/package.json index 27134f3e3c..1c1e6dc362 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh", "description": "dsh CLI: profile launch, plugin management, and configuration inspection", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/apps/desktop-host/package.json b/apps/desktop-host/package.json index f4ae9f07ce..570d4fc856 100644 --- a/apps/desktop-host/package.json +++ b/apps/desktop-host/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-desktop-host", "description": "Private Node-mode host process for the Electron desktop application", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "private": true, "license": "MIT", "type": "module", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 33f7bb5ba8..c7c1246e9a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-desktop", "description": "Electron desktop shell for a bundled dsh runtime and external plugins", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "private": true, "license": "MIT", "type": "module", diff --git a/apps/plugin-market-generator/package.json b/apps/plugin-market-generator/package.json index 48362d3a31..65bf8927e7 100644 --- a/apps/plugin-market-generator/package.json +++ b/apps/plugin-market-generator/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-plugin-market-generator", "description": "Reference generator of plugin market index files from a list of npm packages and Git repositories", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "private": true, "type": "module", "scripts": { diff --git a/apps/web/package.json b/apps/web/package.json index 7cb5c60150..e07f7b89dc 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web-frontend", "description": "Web application entry: vite build over the @deepseek-ai/dsh-client-web shell library; dist/ served by apps/cli's dsh web", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/apps/web/tests/clickable-links-gallery.e2e.ts b/apps/web/tests/clickable-links-gallery.e2e.ts index 2ddd8b3711..f508456d08 100644 --- a/apps/web/tests/clickable-links-gallery.e2e.ts +++ b/apps/web/tests/clickable-links-gallery.e2e.ts @@ -78,6 +78,8 @@ interface GalleryCall { /** The five successful writes; docs/press.md and src/tokens.css join via other tools. */ const WRITES = ['site/report.html', 'a/style.css', 'b/style.css', 'site/index.html', 'site/app.js'] +const LONG_COMMAND_DESCRIPTION = 'Check every generated stylesheet and component theme against the shared design tokens, inspect the application pages for inconsistent colors, and report all remaining lint failures before publishing the production build' + /** The gallery turn's tool calls, in surface order. */ const CALLS: GalleryCall[] = [ ...WRITES.map(path => ({ @@ -166,6 +168,16 @@ const CALLS: GalleryCall[] = [ args: { command: 'pnpm run lint', description: 'Run the lint gate', workdir: 'site' }, result: 'style.css: unexpected hex literal\n[exit code: 1]', }, + { + name: 'bash', + args: { command: 'pnpm run lint:styles', description: LONG_COMMAND_DESCRIPTION, workdir: 'site' }, + result: 'theme.css: unexpected hex literal\n[exit code: 1]', + }, + { + name: 'bash', + args: { command: 'pnpm run build', description: 'Build the site', workdir: 'site' }, + result: '(no output)\n[timed out after 100ms]\n[exit code: null]', + }, { name: 'web_search', args: { queries: ['clickable link styles', 'produced files ui'] }, @@ -378,6 +390,55 @@ describe('web e2e: clickable links gallery', () => { expect(await fetchSummaryLink.getAttribute('target')).toBe('_blank') await expandTurnProcesses(page) + const failedCommands = [ + { description: 'Run the lint gate', long: false }, + { description: LONG_COMMAND_DESCRIPTION, long: true }, + ].map(({ description, long }) => ({ + row: page.locator('[data-sample="bash"]').filter({ hasText: description }), + long, + })) + try { + for (const colorScheme of ['light', 'dark'] as const) { + await page.emulateMedia({ colorScheme }) + await expect.poll(() => page.locator('body').evaluate(element => element.hasAttribute('data-ds-dark-theme'))).toBe(colorScheme === 'dark') + for (const { row, long } of failedCommands) { + const exitBadge = row.getByText('exit code 1', { exact: true }) + await exitBadge.waitFor() + for (const width of ['760px', '260px']) { + await row.evaluate((element, value) => { element.style.width = value }, width) + const geometry = await exitBadge.evaluate((badge) => { + const description = badge.previousElementSibling! + const row = badge.closest('[data-sample]')! + const textRect = description.getBoundingClientRect() + const badgeRect = badge.getBoundingClientRect() + const rowRect = row.getBoundingClientRect() + return { + neutral: getComputedStyle(description).color === getComputedStyle(row).color, + fits: badgeRect.right <= rowRect.right + 1 && badgeRect.bottom <= rowRect.bottom + 1, + inline: Math.abs(badgeRect.top - textRect.top) < 5, + truncated: description.scrollWidth > description.clientWidth, + ellipsis: getComputedStyle(description).textOverflow === 'ellipsis', + singleLine: Math.abs(rowRect.height - textRect.height) < 1, + height: rowRect.height, + gap: badgeRect.left - textRect.right, + } + }) + const context = JSON.stringify({ width, colorScheme, long, ...geometry }) + expect(geometry.neutral, context).toBe(true) + expect(geometry.fits, context).toBe(true) + expect(geometry.inline, context).toBe(true) + expect(geometry.singleLine, context).toBe(true) + expect(geometry.height, context).toBeCloseTo(24, 0) + if (long || width === '760px') expect(geometry.truncated, context).toBe(long) + expect(geometry.ellipsis, context).toBe(true) + expect(geometry.gap, context).toBeCloseTo(8, 0) + } + } + } + } finally { + for (const { row } of failedCommands) await row.evaluate((element) => { element.style.width = '' }) + await page.emulateMedia({ colorScheme: null }) + } // Clicking the collapsed summary link opens the page without expanding the row. const fetchPopupPromise = page.waitForEvent('popup') await fetchSummaryLink.click() @@ -397,12 +458,20 @@ describe('web e2e: clickable links gallery', () => { /^Edit src\/tokens\.css/, /^Grep linkColor/, /Run the lint gate|pnpm run lint/, + /Build the site/, ]) { const toggle = page.getByRole('button', { name: row }).first() await toggle.waitFor({ timeout: 10_000 }) const box = await toggle.boundingBox() await toggle.click(box === null ? {} : { position: { x: box.width - 8, y: box.height / 2 } }) } + const noExitCommand = page.locator('[data-sample="bash"]').filter({ hasText: 'Build the site' }) + expect(await noExitCommand.getAttribute('data-state')).toBe('error') + expect(await noExitCommand.getByText('no exit code', { exact: true }).count()).toBe(1) + const noExitTerminal = page.locator('[data-terminal]').filter({ hasText: 'pnpm run build' }) + expect(await noExitTerminal.getByText('no exit code', { exact: true }).count()).toBe(1) + expect(await noExitTerminal.getByText('[timed out after 100ms]', { exact: true }).count()).toBe(1) + expect(await noExitTerminal.getByText('[exit code: null]', { exact: true }).count()).toBe(0) // Both generic rows (the unclassified str_replace_editor and the unknown // design_tokens_sync) expand to their IN/OUT surfaces. const genericRows = page.getByRole('button', { name: /^Tool call/ }) diff --git a/apps/web/tests/expected/clickable-links-gallery/ui.expected.md b/apps/web/tests/expected/clickable-links-gallery/ui.expected.md index 851f8c5f2e..f51a8054d8 100644 --- a/apps/web/tests/expected/clickable-links-gallery/ui.expected.md +++ b/apps/web/tests/expected/clickable-links-gallery/ui.expected.md @@ -9,7 +9,7 @@ - button "Copy" - status: Completed - button "Completed in {{duration}}" [expanded] -- button "Wrote files, called tools, searched code, etc." [expanded] +- button "Wrote files, ran commands, called tools, etc." [expanded] - button "Write site/report.html 1KB +1 -0": - text: Write - button "site/report.html" @@ -65,11 +65,17 @@ - button "Inspect" - button "Glob **/*.css" - button "Bash List the built site" -- button "Failed Bash Run the lint gate" [expanded] +- button "Failed Bash Run the lint gate exit code 1" [expanded] - text: Failed site pnpm run lint exit code 1 - button "Copy" - text: "style.css: unexpected hex literal" - button "Inspect" +- button "Failed Bash Check every generated stylesheet and component theme against the shared design tokens, inspect the application pages for inconsistent colors, and report all remaining lint failures before publishing the production build exit code 1" +- button "Failed Bash Build the site no exit code" [expanded] +- text: Failed site pnpm run build no exit code +- button "Copy" +- text: (no output) [timed out after {{duration}}] +- button "Inspect" - button "Search clickable link styles, produced files ui" [expanded] - paragraph: - text: Unify links per diff --git a/apps/web/tests/expected/models-settings/declared-edit.expected.md b/apps/web/tests/expected/models-settings/declared-edit.expected.md index e0c3479659..8210c7907d 100644 --- a/apps/web/tests/expected/models-settings/declared-edit.expected.md +++ b/apps/web/tests/expected/models-settings/declared-edit.expected.md @@ -59,6 +59,40 @@ - text: 文本 - checkbox "图片" [checked] - text: 图片 + - group "推理等级 1": + - text: 推理等级 + - button "推理等级说明": "?" + - switch "推理等级 1" [checked] + - combobox "档位 1": + - option "off" [selected] + - option "minimal" + - option "medium" + - option "xhigh" + - option "max" + - textbox "off API 参数值" [disabled]: + - /placeholder: 关闭思考 + - button "删除档位 off" + - combobox "档位 2": + - option "minimal" + - option "low" [selected] + - option "medium" + - option "xhigh" + - option "max" + - textbox "low API 参数值": + - /placeholder: low + - text: low + - button "删除档位 low" + - combobox "档位 3": + - option "minimal" + - option "medium" + - option "high" [selected] + - option "xhigh" + - option "max" + - textbox "high API 参数值": + - /placeholder: high + - text: high + - button "删除档位 high" + - button "添加档位" - button "添加模型" - button "取消" - button "保存" diff --git a/apps/web/tests/expected/models-settings/image-only.expected.md b/apps/web/tests/expected/models-settings/image-only.expected.md index ccde68253e..bbeacdc7be 100644 --- a/apps/web/tests/expected/models-settings/image-only.expected.md +++ b/apps/web/tests/expected/models-settings/image-only.expected.md @@ -59,6 +59,40 @@ - text: 文本 - checkbox "图片" [checked] [disabled] - text: 图片 + - group "推理等级 1": + - text: 推理等级 + - button "推理等级说明": "?" + - switch "推理等级 1" [checked] + - combobox "档位 1": + - option "off" [selected] + - option "minimal" + - option "medium" + - option "xhigh" + - option "max" + - textbox "off API 参数值" [disabled]: + - /placeholder: 关闭思考 + - button "删除档位 off" + - combobox "档位 2": + - option "minimal" + - option "low" [selected] + - option "medium" + - option "xhigh" + - option "max" + - textbox "low API 参数值": + - /placeholder: low + - text: low + - button "删除档位 low" + - combobox "档位 3": + - option "minimal" + - option "medium" + - option "high" [selected] + - option "xhigh" + - option "max" + - textbox "high API 参数值": + - /placeholder: high + - text: deep + - button "删除档位 high" + - button "添加档位" - button "添加模型" - button "取消" - button "保存" diff --git a/apps/web/tests/models-settings.e2e.ts b/apps/web/tests/models-settings.e2e.ts index 824d83ea8d..0abc01dcd1 100644 --- a/apps/web/tests/models-settings.e2e.ts +++ b/apps/web/tests/models-settings.e2e.ts @@ -6,10 +6,11 @@ // while the settings document records only that reference. Each saved row // appears after route topology invalidation without presenting liveness as // provider status. The customized-settings fold writes its curated fields — -// the endpoint, and a declared route's own name and protocol — as merge -// patches against the stored profile. Zero model calls: configuration is pure -// settings/credentials/llm-domain traffic, so there is no fixture and a -// stray stream would fail loud because the adapter registry is empty. The provider under test is +// the endpoint, a declared route's own name and protocol, and the reasoning +// levels of its models — as merge patches against the stored profile. Zero +// model calls: configuration is pure settings/credentials/llm-domain traffic, +// so there is no fixture and a stray stream would fail loud because the +// adapter registry is empty. The provider under test is // minimax-cn so a developer's real ANTHROPIC/OPENAI environment keys can // never shadow the derived reference. The deletion dialog distinguishes a // reference-free profile from a page-managed key before the credential and @@ -300,23 +301,29 @@ describe('web e2e: Models settings page configures a dormant provider', () => { await custom.getByLabel('Provider ID').fill('acme-gateway') await custom.getByLabel('显示名称').fill('Acme Gateway') await custom.getByLabel('API 地址').fill('https://gateway.acme.example/v1') - // No reasoning effort on a provider card at all: effort is a per-model - // capability, the models under one provider disagree about it, and a - // switch in the composer already records provider+model+effort together. + // No provider-scoped reasoning control: effort is a per-model capability + // and the models under one provider disagree about it, so each model row + // declares its own levels instead. expect(await dialog.getByLabel('推理强度').count()).toBe(0) await custom.getByRole('button', { name: '添加模型', exact: true }).click() await custom.getByLabel('模型 ID 1').fill('acme-large') await custom.getByRole('button', { name: '模型选项 1' }).click() expect(await custom.getByRole('group', { name: '输入类型 1' }).getByRole('checkbox', { name: '图片' }).isChecked()).toBe(false) await custom.getByRole('group', { name: '输入类型 1' }).getByRole('checkbox', { name: '图片' }).check() + // The switch seeds off, low, medium, and high rows; this route serves no medium. + const reasoning = custom.getByRole('group', { name: '推理等级 1' }) + await reasoning.getByRole('switch', { name: '推理等级 1' }).click() + await reasoning.getByRole('button', { name: '删除档位 medium', exact: true }).click() await custom.getByRole('button', { name: '创建提供商', exact: true }).click() const row = dialog.getByText('Acme Gateway', { exact: true }).first() await row.waitFor({ timeout: 10_000 }) const document = await readFile(join(scaffold.harnessHome, 'profiles', 'scaffold', 'cordis.patch.yml'), 'utf8') expect(document).toContain('acme-gateway:') + expect(document).toContain('reasoningEfforts:') await expect(scaffold.ctx.llm.resolveModelInfo('acme-gateway', 'acme-large')).resolves.toMatchObject({ inputModalities: ['text', 'image'], + reasoning: { efforts: [{ id: 'off', name: 'Off' }, { id: 'low', name: 'Low' }, { id: 'high', name: 'High' }] }, }) // The tag follows the adapter's installed catalog: this route is in no @@ -345,6 +352,11 @@ describe('web e2e: Models settings page configures a dormant provider', () => { expect(await name.inputValue()).toBe('Acme Gateway') await dialog.getByRole('button', { name: '模型选项 1' }).click() expect(await dialog.getByRole('group', { name: '输入类型 1' }).getByRole('checkbox', { name: '图片' }).isChecked()).toBe(true) + // The stored levels come back as rows beside the values they send. + const reasoning = dialog.getByRole('group', { name: '推理等级 1' }) + expect(await reasoning.getByRole('combobox').evaluateAll(selects => selects.map(select => (select as HTMLSelectElement).value))) + .toEqual(['off', 'low', 'high']) + expect(await reasoning.getByLabel('high API 参数值').inputValue()).toBe('high') await assertModelInputLayout(page, dialog) const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd) await compareOrRefreshGolden(DECLARED_EDIT_EXPECTED, snapshot, MODE) @@ -352,6 +364,8 @@ describe('web e2e: Models settings page configures a dormant provider', () => { await protocol.selectOption('anthropic-messages') await name.fill('Acme 网关') await dialog.getByRole('group', { name: '输入类型 1' }).getByRole('checkbox', { name: '图片' }).uncheck() + // A gateway with its own spelling for the top level. + await reasoning.getByLabel('high API 参数值').fill('deep') await dialog.getByRole('button', { name: '保存', exact: true }).click() await expect.poll(async () => dialog.getByLabel('API 协议').count(), { timeout: 10_000 }).toBe(0) // The adapter re-resolved the route under the new protocol and re-registered @@ -365,8 +379,11 @@ describe('web e2e: Models settings page configures a dormant provider', () => { const document = await readFile(join(scaffold.harnessHome, 'profiles', 'scaffold', 'cordis.patch.yml'), 'utf8') expect(document).toContain('api: anthropic-messages') expect(document).toContain('displayName: Acme 网关') + expect(document).toContain('high: deep') + // The level keeps its id and name; only its wire spelling changed. await expect(scaffold.ctx.llm.resolveModelInfo('acme-gateway', 'acme-large')).resolves.toMatchObject({ inputModalities: ['text'], + reasoning: { efforts: [{ id: 'off' }, { id: 'low' }, { id: 'high', name: 'High' }] }, }) expect(tripwire.pageErrors).toEqual([]) }, 60_000) diff --git a/apps/web/tests/sidebar-terminal.e2e.ts b/apps/web/tests/sidebar-terminal.e2e.ts index a8173b6d27..07ee0c02f2 100644 --- a/apps/web/tests/sidebar-terminal.e2e.ts +++ b/apps/web/tests/sidebar-terminal.e2e.ts @@ -287,8 +287,10 @@ describe.skipIf(process.platform === 'win32')('Web sidebar terminal', () => { it('completes commands, preserves the process through collapse and reload, resizes, and kills on tab close', async () => { onTestFailed(() => saveFailureShot(page, 'sidebar-terminal')) const unaryInput: string[] = [] + const listRequests: string[] = [] page.on('request', (request) => { if (/\/api\/terminal\/(write|resize|follow)$/u.test(new URL(request.url()).pathname)) unaryInput.push(request.url()) + if (new URL(request.url()).pathname === '/api/terminal/list') listRequests.push(request.url()) }) await openTerminal(page) const terminal = page.locator('[data-sidebar-terminal]') @@ -335,6 +337,7 @@ describe.skipIf(process.platform === 'win32')('Web sidebar terminal', () => { await expect.poll(async () => await screen.innerText()).toContain(`SIZE:${terminals()[0]!.rows} ${terminals()[0]!.cols}`) await page.screenshot({ path: `${shots}/fullscreen.png`, fullPage: true }) const tabIds = await page.locator('[data-dockkit-tab]').evaluateAll(tabs => tabs.map(tab => tab.getAttribute('data-dockkit-tab'))) + expect(listRequests).toHaveLength(0) await page.reload({ waitUntil: 'load' }) await page.locator('[data-dockkit-tab]').filter({ hasText: 'Development' }).waitFor({ timeout: 15_000 }) await expect.poll(async () => await page.locator('[data-dockkit-tab-title]').allInnerTexts()).toEqual(['Development', 'bash']) @@ -344,6 +347,7 @@ describe.skipIf(process.platform === 'win32')('Web sidebar terminal', () => { expect(await page.locator('[data-dockkit-tab]').evaluateAll(tabs => tabs.map(tab => tab.getAttribute('data-dockkit-tab')))).toEqual(tabIds) await expect.poll(async () => await screen.innerText()).toContain('PERSIST:xterm-256color') await page.getByRole('button', { name: 'Exit fullscreen', exact: true }).waitFor() + expect(listRequests).toHaveLength(1) await page.getByRole('button', { name: 'Collapse right sidebar', exact: true }).click() await page.reload({ waitUntil: 'load' }) await page.locator('[data-sidebar-right-expand]').waitFor() @@ -354,6 +358,7 @@ describe.skipIf(process.platform === 'win32')('Web sidebar terminal', () => { const secondTab = page.locator('[data-dockkit-tab]').filter({ hasText: 'bash' }) await secondTab.click() await expect.poll(async () => await screen.innerText()).toContain(`SECOND_PID:${secondPid}`) + expect(listRequests).toHaveLength(2) await secondTab.hover() await secondTab.locator('[data-dockkit-tab-close]').click() await expect.poll(async () => await secondTab.count()).toBe(0) diff --git a/docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.i18n.yaml b/docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.i18n.yaml similarity index 86% rename from docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.i18n.yaml rename to docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.i18n.yaml index ca805fd4fa..867a0c0505 100644 --- a/docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.i18n.yaml +++ b/docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.i18n.yaml @@ -1,13 +1,13 @@ # Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading # section, a hash of its English and Chinese blocks outside code blocks and generated regions. # After editing either side, bring the other along and re-record with: -# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md +# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md /: en: 1f073189776760d8 zh: cfbb0403aea96f84 /agent-teams-uses-subagents-and-session-ids: - en: 32ee389e58de25fe - zh: 420fd4344f8f1214 + en: a6aba40e910d4ecb + zh: 11881b6e9d8228d9 /agent-teams-uses-subagents-and-session-ids/change: en: 208add38ac8dc327 zh: c34fce9b2a978e5f diff --git a/docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md b/docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md similarity index 100% rename from docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md rename to docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md diff --git a/docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md b/docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md similarity index 100% rename from docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md rename to docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md diff --git a/docs/user/guide/providers.i18n.yaml b/docs/user/guide/providers.i18n.yaml index 684e061d4b..e96b8abe57 100644 --- a/docs/user/guide/providers.i18n.yaml +++ b/docs/user/guide/providers.i18n.yaml @@ -21,8 +21,8 @@ en: 6a94264c99535caf zh: f97bb0dc6a40c0d3 /configure-models/advanced-configuration: - en: aaefe21c2fb77396 - zh: c02d1e2a6b2a1290 + en: b882ec69a6899d8a + zh: 66a1713aca0e6f1c /configure-models/advanced-configuration/image-input: en: 75350cb615da03a4 zh: 6f8b3510085e4ac4 @@ -30,11 +30,11 @@ en: fc09d02a89b211a7 zh: e0f23a8912a24178 /configure-models/advanced-configuration/reasoning-effort: - en: f7581a0760fdfd2c - zh: dc6eaa16f74a0e9f + en: f2ffb493997dcc72 + zh: dee9238f072e53ad /configure-models/advanced-configuration/request-compatibility: en: 6f52195d1502783e zh: 34dbbeacb541afd4 /configure-models/troubleshooting: - en: 0477cd6fa2772d6b - zh: 780619fe998bdef7 + en: af9f866bec7701f0 + zh: 0e4f5e1da975be41 diff --git a/docs/user/guide/providers.md b/docs/user/guide/providers.md index f0904aec0c..4a57c7b7bc 100644 --- a/docs/user/guide/providers.md +++ b/docs/user/guide/providers.md @@ -47,7 +47,7 @@ If a saved default names a provider that was deleted, the composer displays **Se The generated [plugin configuration catalog](../../config-catalog.md) lists every supported field and default for every plugin; [`dsh-llm-pi-ai`](../../config-catalog.md#deepseek-aidsh-llm-pi-ai) is the provider section this page configures. The [`dsh-llm-pi-ai`](../../../packages/llm/llm-pi-ai/README.md) and [`dsh-llm-deepseek`](../../../packages/llm/llm-deepseek/README.md) references own direct `cordis.patch.yml` configuration, catalog resolution, reasoning controls, credentials, and adapter errors. ::: tip Additional settings -The Models page exposes the API key, display name, base URL, API protocol, and each model's id, display name, context window, max output tokens, and input types. Configure reasoning effort levels, request-compatibility switches, headers, timeouts, and retry policy in `$DSH_HOME/profiles//cordis.patch.yml`, the same document the page writes. Edit it directly, or, when the browser runs on the same machine as the server, open it with **Open configuration file** in the Settings header; the adapters re-read it on the next request, so nothing needs a restart. The subsections below cover the fields most gateways need. +The Models page exposes the API key, display name, base URL, API protocol, and each model's id, display name, context window, max output tokens, and input types, plus the reasoning levels of a model on a custom model API provider. Configure the reasoning levels of a built-in provider's models, request-compatibility switches, headers, timeouts, and retry policy in `$DSH_HOME/profiles//cordis.patch.yml`, the same document the page writes. Edit it directly, or, when the browser runs on the same machine as the server, open it with **Open configuration file** in the Settings header; the adapters re-read it on the next request, so nothing needs a restart. The subsections below cover the fields most gateways need. For the standard Web UI launch with `dsh web`, `` is `web`, so the path is `$DSH_HOME/profiles/web/cordis.patch.yml`. If you launch a custom profile, use the name selected at startup instead. ::: @@ -129,7 +129,11 @@ A model that needs a newer DeepSeek Harness fails with `CLIENT_UPGRADE_REQUIRED` ### Reasoning effort -The model picker offers an **Effort** menu for a model that declares reasoning levels. A built-in provider's models inherit their levels from the installed catalog. A model you enter by hand declares none, so the Effort entry does not appear in the menu and the endpoint's own default decides whether the model thinks. Declare the levels with `reasoningEfforts` in `$DSH_HOME/profiles//cordis.patch.yml`: +The model picker offers an **Effort** menu for a model that declares reasoning levels. A built-in provider's models inherit their levels from the installed catalog, and DeepSeek's models keep the adapter's levels. A model you enter by hand declares none until you add them, so until then the Effort entry does not appear in the menu and the endpoint's own default decides whether the model thinks. + +On a custom model API provider, declare the levels in **Settings → Models**: edit the provider, open **Customized settings**, expand the model's **Model options**, and turn on **Reasoning levels**. The rows start with off, low, medium, and high. Each row pairs a level with the **API value** sent for it, which defaults to the level name; change it only where the model provider's API documentation spells the level differently. Add or delete rows to match what the endpoint supports, keep at least one level besides off, and save. Turning the switch off removes the declaration. + +The page writes `reasoningEfforts` in `$DSH_HOME/profiles//cordis.patch.yml`, where you can also declare the levels directly: ```yaml - id: llm-pi-ai @@ -216,7 +220,7 @@ Every switch, its accepted values, and the protocols that take it are listed und - **Fetching available models reports neither a `data` array nor a `models` object** — The endpoint's listing is in a format discovery does not read. Enter the models by hand. - **The gateway refuses every request although the key and URL are right** — Its request shape differs from OpenAI's. Start with `compat.supportsDeveloperRole: false` and `compat.maxTokensField: max_tokens` on the route. - **Only reasoning models fail** — pi-ai sends their system prompt as the `developer` role, which the gateway rejects. Set `compat.supportsDeveloperRole: false`. -- **The Effort menu does not appear for a model you entered by hand** — It declares no levels. Add `reasoningEfforts` to the model in `cordis.patch.yml`. +- **The Effort menu does not appear for a model you entered by hand** — It declares no levels. On a custom model API provider, turn on **Reasoning levels** in the model's **Model options**; elsewhere, add `reasoningEfforts` to the model in `cordis.patch.yml`. - **`off` does not stop a DeepSeek model from thinking** — An empty `off` sends no reasoning field at all, and an endpoint that thinks by default keeps thinking. Set `compat.thinkingFormat: deepseek` on the model or the route. - **A compat switch is refused as having no value** — A key written with nothing after the colon. Give it a value, or remove the key to keep the installed catalog's. - **An image is refused before sending** — The model declares no image modality. Give a custom provider's model `input: [text, image]`; on DeepSeek's own route, select an image-capable entry from the configured catalog (`deepseek-flash` by default) and confirm that your gateway serves that model with image input. diff --git a/docs/user/guide/providers.zh.md b/docs/user/guide/providers.zh.md index 87f9eb520f..9189f66815 100644 --- a/docs/user/guide/providers.zh.md +++ b/docs/user/guide/providers.zh.md @@ -47,7 +47,7 @@ Provider ID 是永久的,因为请求、已保存会话、模型默认值和 自动生成的[插件配置目录](../../config-catalog.zh.md)列出每个插件的所有受支持字段与默认值;[`dsh-llm-pi-ai`](../../config-catalog.zh.md#deepseek-aidsh-llm-pi-ai) 就是本页所配置的那个提供商段落。[`dsh-llm-pi-ai`](../../../packages/llm/llm-pi-ai/README.zh.md) 和 [`dsh-llm-deepseek`](../../../packages/llm/llm-deepseek/README.zh.md) 参考文档负责直接 `cordis.patch.yml` 配置、目录解析、推理控制、凭据与适配器错误。 ::: tip 其他设置 -模型页提供 API 密钥、显示名称、API 地址、API 协议,以及每个模型的 ID、显示名称、上下文窗口、最大输出 token 数和输入类型。推理等级、请求兼容性开关、请求头、超时和重试策略在 `$DSH_HOME/profiles//cordis.patch.yml` 中设置,也就是模型页写入的同一份文档。可以直接编辑它;浏览器与服务器在同一台机器时,也可以点击设置页顶部的**打开配置文件**打开它。适配器会在下一次请求时重新读取,无需重启任何东西。下面各小节介绍多数网关会用到的字段。 +模型页提供 API 密钥、显示名称、API 地址、API 协议,以及每个模型的 ID、显示名称、上下文窗口、最大输出 token 数和输入类型;自定义模型 API 提供商的模型还可以在这里设置推理等级。内置提供商模型的推理等级、请求兼容性开关、请求头、超时和重试策略在 `$DSH_HOME/profiles//cordis.patch.yml` 中设置,也就是模型页写入的同一份文档。可以直接编辑它;浏览器与服务器在同一台机器时,也可以点击设置页顶部的**打开配置文件**打开它。适配器会在下一次请求时重新读取,无需重启任何东西。下面各小节介绍多数网关会用到的字段。 按常规方式通过 `dsh web` 启动 Web UI 时,`` 就是 `web`,完整路径为 `$DSH_HOME/profiles/web/cordis.patch.yml`。如果使用自定义 profile,请替换为启动时指定的名称。 ::: @@ -129,7 +129,11 @@ DeepSeek 将省略的 `inputModalities` 视为纯文本,并拒绝空列表。 ### 推理等级 -对于声明了推理等级的模型,模型选择器会提供**推理等级**菜单。内置提供商的模型从已安装目录继承其等级。手动录入的模型不声明任何等级,因此模型菜单里不会出现推理等级项,由端点自身的默认值决定模型是否思考。请在 `$DSH_HOME/profiles//cordis.patch.yml` 中用 `reasoningEfforts` 声明等级: +对于声明了推理等级的模型,模型选择器会提供**推理等级**菜单。内置提供商的模型从已安装目录继承其等级,DeepSeek 的模型沿用适配器提供的等级。手动录入的模型在添加等级之前不声明任何等级,因此模型菜单里不会出现推理等级项,由端点自身的默认值决定模型是否思考。 + +自定义模型 API 提供商可以在**设置 → 模型**中声明等级:编辑该提供商,打开**自定义设置**,展开该模型的**模型选项**,然后打开**推理等级**。默认带有 off、low、medium、high 四行。每行把一个档位与发送给它的 **API 参数值**配成一对,参数值默认与档位名相同;只有模型提供商的 API 文档写法不同时才需要修改。按端点支持的档位增删行,至少保留一个 off 以外的档位,然后保存。关闭开关会删除该声明。 + +模型页会把 `reasoningEfforts` 写入 `$DSH_HOME/profiles//cordis.patch.yml`,也可以直接在其中声明等级: ```yaml - id: llm-pi-ai @@ -216,7 +220,7 @@ DeepSeek 将省略的 `inputModalities` 视为纯文本,并拒绝空列表。 - **获取可用模型提示既没有 `data` 数组也没有 `models` 对象**:端点返回的列表格式不在探测的读取范围内。请手动输入模型。 - **密钥与地址都正确,网关却拒绝每一个请求**:它的请求形状与 OpenAI 不同。先在路由上设 `compat.supportsDeveloperRole: false` 与 `compat.maxTokensField: max_tokens`。 - **只有推理模型失败**:pi-ai 把它们的系统提示词以 `developer` 角色发出,而网关拒绝该角色。设 `compat.supportsDeveloperRole: false`。 -- **手动录入的模型没有推理等级菜单**:该模型没有声明任何等级。在 `cordis.patch.yml` 中给该模型加上 `reasoningEfforts`。 +- **手动录入的模型没有推理等级菜单**:该模型没有声明任何等级。如果是自定义模型 API 提供商,在该模型的**模型选项**中打开**推理等级**;其他提供商在 `cordis.patch.yml` 中给该模型加上 `reasoningEfforts`。 - **`off` 无法让 DeepSeek 模型停止思考**:留空的 `off` 不发送任何推理字段,默认思考的端点就继续思考。请在模型或路由上设置 `compat.thinkingFormat: deepseek`。 - **某个 compat 开关因没有值而被拒绝**:冒号后什么都没写。给它一个值,或删掉该键以沿用已安装 catalog 的值。 - **图片在发送前被拒绝**:该模型未声明图片模态。请给自定义提供商的模型加上 `input: [text, image]`;在 DeepSeek 自身的路由上,请从配置的目录中选择支持图片的条目(默认为 `deepseek-flash`),并确认网关提供该模型且支持图片输入。 diff --git a/package.json b/package.json index 7768fb109a..042d7eb56b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@deepseek-ai/dsh-root", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "license": "MIT", "private": true, "type": "module", diff --git a/packages/acp/acp/package.json b/packages/acp/acp/package.json index d846a8a6dc..936f60429a 100644 --- a/packages/acp/acp/package.json +++ b/packages/acp/acp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-acp", "description": "Automation-only Agent Client Protocol server for driving DeepSeek Harness agents over JSON-RPC stdio", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/account-controller/package.json b/packages/api/account-controller/package.json index 5b3906cf69..e3c47d79f6 100644 --- a/packages/api/account-controller/package.json +++ b/packages/api/account-controller/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-account-controller", "description": "Expose safe account operations over authenticated Remote", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/gateway/package.json b/packages/api/gateway/package.json index 314fc022cf..d582e3a2e2 100644 --- a/packages/api/gateway/package.json +++ b/packages/api/gateway/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-gateway", "description": "Typert Remote Host dispatcher and Client API endpoint", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/job-controller/package.json b/packages/api/job-controller/package.json index 780e34ab5c..52c691a824 100644 --- a/packages/api/job-controller/package.json +++ b/packages/api/job-controller/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-job-controller", "description": "Job Remote observation stream and the reference-counted client job-output service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/remote-access/package.json b/packages/api/remote-access/package.json index 6153e79da3..091571fb6b 100644 --- a/packages/api/remote-access/package.json +++ b/packages/api/remote-access/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-remote-access", "description": "Method-level Remote access control for non-owner Peers and the per-Peer access snapshot", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/remotes/package.json b/packages/api/remotes/package.json index 1a52ccda25..ce79b4be96 100644 --- a/packages/api/remotes/package.json +++ b/packages/api/remotes/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-remotes", "description": "Remote BFF assembly for application-selected Host capabilities", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/session-controller/package.json b/packages/api/session-controller/package.json index c48a6b4fe4..0021704902 100644 --- a/packages/api/session-controller/package.json +++ b/packages/api/session-controller/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-session-controller", "description": "Session Remote commands, cold reads, and live control transport", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/settings-controller/package.json b/packages/api/settings-controller/package.json index 61cfcb7ad6..4ca0361a2f 100644 --- a/packages/api/settings-controller/package.json +++ b/packages/api/settings-controller/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-settings-controller", "description": "Remote owner for the configuration surfaces over the settings-domain seams", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/terminal-controller/README.i18n.yaml b/packages/api/terminal-controller/README.i18n.yaml index e25664e9e3..c7bad45785 100644 --- a/packages/api/terminal-controller/README.i18n.yaml +++ b/packages/api/terminal-controller/README.i18n.yaml @@ -18,8 +18,8 @@ en: 056c8b23819be51f zh: d4525149ad4c32ce /deepseek-ai-dsh-api-terminal-controller/understand-the-implementation: - en: b34684c82aa5fe5f - zh: 07638715c8708546 + en: a3216b572cb70a6b + zh: 3adb2a394488b026 /deepseek-ai-dsh-api-terminal-controller/further-exploration: en: b1b5d2a2bfb8cc6a zh: aa0ea568aa63fda5 diff --git a/packages/api/terminal-controller/README.md b/packages/api/terminal-controller/README.md index 856ebc96de..d4f4f7ac0c 100644 --- a/packages/api/terminal-controller/README.md +++ b/packages/api/terminal-controller/README.md @@ -59,6 +59,8 @@ The opening snapshot advertises the carrier's complete-frame `inputWindow`; in-p The Client saves each Session/content-to-terminal association before allocation under its own `dsh.terminal.binding.v1.*` localStorage key. The content identity is globally unique; layout-local tab ids only identify live view occurrences. Independent record writes and deletes preserve other windows' bindings. Restored views reuse that identity; the sidebar terminal provider restores its views before querying unrepresented Host terminals. A new view may create a process, while a recovered view reports a missing target without creating a replacement. Explicit close removes the association after saving its cleanup request. The Host supplies current process metadata and screen contents; neither is saved in the browser. The Client model acknowledges screen writes after the browser emulator processes them, pipelines input within its unacknowledged UTF-8 byte budget and ignores stale attachment responses. Reconnection creates a fresh command sequence and never replays unacknowledged input. Provider input failures end the attachment; its process and recovery screen remain available for explicit reconnection. Client-owned errors carry locale keys. Plugin disposal awaits active and previously detached output streams without closing Host processes. +Client discovery belongs to each Session in the current window. Concurrent and later restored views share one list, including an empty result; new identities allocate directly from `create`. Explicit retry replaces a settled result or failure, and reconnection refreshes each previously discovered Session once. Remounting a failed view preserves its error without retrying. Confirmed mutations and attachment metadata take precedence over an older pending list response, and responses from replaced connections cannot publish. A view releasing its DOM or lifetime does not cancel another view’s lookup. Plugin disposal discards cached metadata and joins outstanding discovery. Other windows’ unrepresented terminals become discoverable through explicit refresh or reconnection; current attachments carry their own live metadata. Environment lookup remains separate because its working directory can change. + The separate `retain(sessionId, id, signal)` Remote stream acknowledges a window hold without Agent activation, screen output, input transfer or process creation. The terminal provider supplies the sidebar's complete open-tab inventory, and the Client intersects it with its own saved identities. Duplicate occurrences share one hold per window; stale associations alone retain nothing. Restored output attachments wait for an acknowledged current hold. Transport cancellation releases exactly that physical generation; plugin disposal joins all hold streams. Failed cleanup retains ownership and retries without reopening admission or restarting the idle grace. New views start automatically, using an explicit guide selection or the remembered available shell. The last selected shell path is stored under `dsh.terminal.shell` in origin-scoped localStorage. Default launches verify the saved path through Host discovery and fall back to the current default when it is absent. The guide records a selection before opening its tab; each new tab retains its own chosen path and allocation identity. Storage failures do not prevent startup. Restoring existing terminals neither reads this preference nor discovers shells. diff --git a/packages/api/terminal-controller/README.zh.md b/packages/api/terminal-controller/README.zh.md index 220d88190d..92374aa4b8 100644 --- a/packages/api/terminal-controller/README.zh.md +++ b/packages/api/terminal-controller/README.zh.md @@ -59,6 +59,8 @@ Host 通过 `ctx.subprocess.spawnTerminal` 创建 `TERM=xterm-256color` 的终 Client 在分配前将每条 Session/内容与终端身份的关联保存到独立的 localStorage key `dsh.terminal.binding.v1.*`。内容身份全局唯一;布局内的 tab id 只标识活动视图 occurrence。逐条记录的写入和删除会保留其他窗口的关联。恢复视图复用该身份;侧栏 terminal provider 先恢复自己的视图,再查询尚无视图的 Host 终端。新视图可以创建进程,恢复视图在目标缺失时显示错误,不创建替代进程。显式关闭先保存清理请求,再删除关联。当前进程元数据和屏幕内容由 Host 提供,不保存在浏览器中。Client 模型在浏览器完成屏幕解析后确认帧,在未确认 UTF-8 字节预算内连续发送输入,并忽略旧连接迟到的响应。重连建立新的命令序列,绝不重放未确认输入。提供者输入失败会结束附件;进程和恢复屏幕仍可供显式重连使用。Client 自产错误携带本地化键。插件卸载等待活跃及先前断开的输出流结束,不关闭 Host 进程。 +客户端发现状态由当前窗口中的各个 Session 持有。并发及稍后恢复的视图共享一次列表查询,包括空结果;新标识直接通过 `create` 分配终端。显式重试会替换已完成的结果或失败,重连则为每个已经查询过的 Session 刷新一次列表。失败视图重新挂载时保留错误,不会重试。已确认的操作和 attachment 元数据优先于较早发起的列表响应,被替换连接的响应不能发布状态。单个视图释放 DOM 或结束生命周期不会取消其他视图的查询。插件卸载会丢弃缓存元数据并等待未完成的发现请求结束。其他窗口中尚未出现在本窗口的终端可通过显式刷新或重连发现;当前 attachment 自行传递实时元数据。环境查询保持独立,因为工作目录可能变化。 + 独立的 `retain(sessionId, id, signal)` Remote 流确认窗口持有关系,不激活 Agent、发送屏幕输出、转移输入权或创建进程。终端 provider 提供侧栏的完整打开标签清单,Client 将其与自己保存的身份取交集。同一窗口中的重复 occurrence 共用一个持有流;孤立的旧关联不会保活任何终端。恢复输出连接前必须等待当前持有关系确认。传输取消只释放对应的物理流代次;插件卸载等待全部持有流结束。清理失败时保留所有权并重试,不重新接受连接,也不重新计算空闲宽限期。 新视图自动启动,使用开始页明确选中的 shell,或上次选择且仍可用的 shell。上次选择的路径保存在当前站点 localStorage 的 `dsh.terminal.shell` 中。默认启动通过 Host 探测验证保存的路径,不可用时回到当前默认项。开始页在打开标签页前记录选择,每个新标签页保留自己的 shell 路径和分配身份。存储失败不影响启动。恢复已有终端既不读取这一偏好,也不探测 shell。 diff --git a/packages/api/terminal-controller/package.json b/packages/api/terminal-controller/package.json index 6f76a55a5b..0905960c66 100644 --- a/packages/api/terminal-controller/package.json +++ b/packages/api/terminal-controller/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-terminal-controller", "description": "Session-owned interactive terminals with shell discovery, screen recovery and typed Remote control", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, @@ -73,6 +73,7 @@ "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-api-gateway": "workspace:*", "@deepseek-ai/dsh-brand": "workspace:*", + "@deepseek-ai/dsh-client-connection": "workspace:*", "@deepseek-ai/dsh-client-store": "workspace:*", "@deepseek-ai/dsh-fs": "workspace:*", "@deepseek-ai/dsh-remote-mock": "workspace:*", diff --git a/packages/api/terminal-controller/src/client/index.ts b/packages/api/terminal-controller/src/client/index.ts index 8ae538dc81..0a655937f5 100644 --- a/packages/api/terminal-controller/src/client/index.ts +++ b/packages/api/terminal-controller/src/client/index.ts @@ -3,6 +3,7 @@ import { Service, type Context } from '@deepseek-ai/cordis' import { remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type {} from '@deepseek-ai/dsh-api-gateway/client' +import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client' import { TerminalView, type TerminalRemote } from './model.ts' import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store' import { randomUUID } from '@deepseek-ai/dsh-util-crypto' @@ -11,6 +12,7 @@ import { preferredShell, rememberShell } from './shell-preference.ts' import { TerminalCloseRequests, type TerminalCloseRequest } from './close-requests.ts' import { TerminalWindowHold } from './retention.ts' import { TerminalBindings } from './bindings.ts' +import { TerminalInventory } from './inventory.ts' export type { TerminalView, TerminalViewState, TerminalViewIssue, TerminalRenderFrame, TerminalRemote } from './model.ts' @@ -43,6 +45,7 @@ export class ClientTerminals extends Service { private readonly closed = new Set(this.requests.pending().map(request => request.id)) private disposed = false private readonly views = new Map>() + private readonly inventories = new Map() private readonly bindings = new TerminalBindings() private readonly holds = new Map>() private readonly releasing = new Set>() @@ -54,14 +57,28 @@ export class ClientTerminals extends Service { */ constructor(ctx: Context, private readonly remote: TerminalRemote) { super(ctx, 'webTerminals') + const connection = ctx.get('connection') as ConnectionHandle + let connected = connection.generation.getSnapshot() !== undefined + ctx.on('connection/reset', () => { + if (!connected) { connected = true; return } + for (const [sessionId, inventory] of this.inventories) { + void inventory.reset()?.catch((error: unknown) => { + if (!this.disposed) { + this.ctx.logger.warn('Terminal list refresh failed; retry terminal recovery', { sessionId, error }) + } + }) + } + }) ctx.effect(() => async () => { this.disposed = true + const listing = [...this.inventories.values()].map(inventory => inventory.dispose()) + this.inventories.clear() const detaching = [...this.views.values()].flatMap(views => [...views.values()].map(view => view.dispose())) this.views.clear() this.bindings.clear() const holds = [...this.holds.values()].flatMap(holds => [...holds.values()].map(hold => hold.dispose())) this.holds.clear() - await Promise.all([...detaching, ...holds, ...this.releasing, ...this.closing.values()]) + await Promise.all([...listing, ...detaching, ...holds, ...this.releasing, ...this.closing.values()]) }, 'terminal-controller.client.views') for (const request of this.requests.pending()) this.cleanup(request) } @@ -83,7 +100,7 @@ export class ClientTerminals extends Service { const saved = terminalId ?? this.bindings.get(sessionId, contentId) const id = saved ?? randomUUID() as WebTerminalId this.bindings.set(sessionId, contentId, id) - view = new TerminalView(sessionId, this.remote, this.ctx.remote, id, saved === undefined, shellPath, + view = new TerminalView(sessionId, this.remote, this.ctx.remote, this.inventory(sessionId), id, saved === undefined, shellPath, signal => this.hold(sessionId, id).ready(signal)) views.set(key, view) this.reconcileHolds() @@ -92,6 +109,15 @@ export class ClientTerminals extends Service { return view } + private inventory(sessionId: SessionId): TerminalInventory { + let inventory = this.inventories.get(sessionId) + if (inventory === undefined) { + inventory = new TerminalInventory(() => this.remote.list(sessionId)) + this.inventories.set(sessionId, inventory) + } + return inventory + } + /** * Discover available launch choices on demand without allocating a PTY. * @param sessionId - target Session. @@ -183,16 +209,16 @@ export class ClientTerminals extends Service { } /** - * Query Host terminals without a live view or unfinished close. + * Read shared Session discovery without a live view or unfinished close. * @param sessionId - Session being displayed. + * @param refresh - explicitly replace settled discovery, including a previous failure. * @returns terminals available for opening as recovered tabs. */ - async recover(sessionId: SessionId): Promise { - const result = await this.remote.list(sessionId) - if (!result.ok) throw new Error(result.error.message) + async recover(sessionId: SessionId, refresh: boolean = false): Promise { + const available = await this.inventory(sessionId).read(refresh) const held = new Set([...(this.views.get(sessionId)?.values() ?? [])].map(view => view.id)) const closing = new Set(this.requests.pending().map(request => request.id)) - return result.value.filter(info => !held.has(info.id) && !closing.has(info.id) && !this.closed.has(info.id)) + return [...available.values()].filter(info => !held.has(info.id) && !closing.has(info.id) && !this.closed.has(info.id)) } /** @@ -213,6 +239,7 @@ export class ClientTerminals extends Service { const result = await this.remote.close(record.sessionId, record.id) if (!result.ok) throw result.error } + this.inventories.get(record.sessionId)?.remove(record.id) this.requests.remove(record.id) })().catch((error: unknown) => { if (remoteErrorOf(error)?.code === 'session/not-found') { @@ -232,7 +259,7 @@ export class ClientTerminals extends Service { } /** Required Client transport and terminal namespace. */ -export const inject = ['remote', 'remote.terminal'] +export const inject = ['remote', 'remote.terminal', 'connection'] /** * Install the Client terminal models. diff --git a/packages/api/terminal-controller/src/client/inventory.ts b/packages/api/terminal-controller/src/client/inventory.ts new file mode 100644 index 0000000000..565967a084 --- /dev/null +++ b/packages/api/terminal-controller/src/client/inventory.ts @@ -0,0 +1,103 @@ +/** Session-owned terminal discovery shared across view occurrences. */ +import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol' +import type { WebTerminalId, WebTerminalInfo } from '../types.ts' + +type Inventory = ReadonlyMap +interface Loading { + readonly kind: 'loading' + readonly promise: Promise + readonly updates: Map +} +type LoadState = { readonly kind: 'unloaded' | 'ready' | 'disposed' } + | { readonly kind: 'failed'; readonly error: Error } + | Loading + +/** One Session's cached list, retained failures and in-flight mutation reconciliation. */ +export class TerminalInventory { + private state: LoadState = { kind: 'unloaded' } + private items = new Map() + private readonly pending = new Set>() + + /** @param load - fetch the Session's retained Host terminals. */ + constructor(private readonly load: () => Promise>) {} + + /** + * Read the cached list or join its single in-flight request. + * @param refresh - explicitly replace a settled result or failure; overlapping refreshes share one request. + * @returns current terminal metadata; failures remain cached until refresh or reset. + */ + read(refresh: boolean = false): Promise { + if (this.state.kind === 'disposed') return Promise.reject(new Error('Terminal inventory disposed')) + if (this.state.kind === 'loading') return this.state.promise + if (!refresh && this.state.kind === 'ready') return Promise.resolve(this.items) + if (!refresh && this.state.kind === 'failed') return Promise.reject(this.state.error) + const completed = Promise.withResolvers() + const request: Loading = { kind: 'loading', promise: completed.promise, updates: new Map() } + this.state = request + this.pending.add(completed.promise) + void this.loadRequest(request).then(completed.resolve, completed.reject) + void completed.promise.then(() => { this.pending.delete(completed.promise) }, () => { this.pending.delete(completed.promise) }) + return completed.promise + } + + private async loadRequest(request: Loading): Promise { + try { + const result = await this.load() + if (this.state !== request) return await this.read() + if (!result.ok) throw result.error + const items = new Map(result.value.map(info => [info.id, info])) + for (const [id, info] of request.updates) { + if (info === undefined) items.delete(id) + else items.set(id, info) + } + this.items = items + this.state = { kind: 'ready' } + return items + } catch (error) { + if (this.state !== request) return this.read() + const failure = error instanceof Error ? error : new Error('Terminal discovery failed', { cause: error }) + this.state = { kind: 'failed', error: failure } + throw failure + } + } + + /** + * Preserve acknowledged mutations and attachment metadata across a pending list response. + * @param info - latest confirmed terminal metadata. + */ + update(info: WebTerminalInfo): void { + if (this.state.kind === 'disposed') return + this.items.set(info.id, info) + if (this.state.kind === 'loading') this.state.updates.set(info.id, info) + } + + /** + * Remove a terminal after confirmed closure or unavailability. + * @param id - terminal identity. + */ + remove(id: WebTerminalId): void { + if (this.state.kind === 'disposed') return + this.items.delete(id) + if (this.state.kind === 'loading') this.state.updates.set(id, undefined) + } + + /** + * Replace discovery after a connection change; never discover a Session that has not needed a list. + * @returns the replacement lookup, or undefined for an unused or disposed inventory. + */ + reset(): Promise | undefined { + if (this.state.kind === 'unloaded' || this.state.kind === 'disposed') return undefined + this.state = { kind: 'unloaded' } + return this.read() + } + + /** + * Discard metadata and prevent late responses from publishing. + * @returns after all owned list requests settle. + */ + async dispose(): Promise { + this.state = { kind: 'disposed' } + this.items.clear() + await Promise.allSettled(this.pending) + } +} diff --git a/packages/api/terminal-controller/src/client/model.ts b/packages/api/terminal-controller/src/client/model.ts index d143e9c57a..ff797500f7 100644 --- a/packages/api/terminal-controller/src/client/model.ts +++ b/packages/api/terminal-controller/src/client/model.ts @@ -1,5 +1,6 @@ /** React-free browser terminal state and reconnecting Remote-stream ownership. */ import { preferredShell, rememberShell } from './shell-preference.ts' +import type { TerminalInventory } from './inventory.ts' import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store' import { Deque } from '@deepseek-ai/dsh-deque' import { RemoteStreamCarrierError, type ClientRemote, type RemoteStream } from '@deepseek-ai/dsh-api-gateway/client' @@ -83,6 +84,7 @@ export class TerminalView { * @param sessionId - Session owning the terminal. * @param remote - typed terminal Remote operations. * @param gateway - reconnecting stream factory. + * @param inventory - Session-owned terminal list shared by all views. * @param id - Host terminal identity, reused when recovering an item from its Session list. * @param createWhenMissing - allow allocation only for a new tab, never a listed terminal. * @param shellPath - explicit shell chosen at the guide; omission uses the remembered available shell. @@ -92,8 +94,9 @@ export class TerminalView { private readonly sessionId: SessionId, private readonly remote: TerminalRemote, private readonly gateway: Pick, + private readonly inventory: TerminalInventory, readonly id: WebTerminalId, - private readonly createWhenMissing = true, + private createWhenMissing = true, private readonly shellPath?: string, private readonly retain?: (signal: AbortSignal) => Promise, ) {} @@ -104,8 +107,9 @@ export class TerminalView { */ mount(): () => void { this.mounted = true - if (this.state.getSnapshot().info === undefined) void this.refresh() - else this.connect() + const state = this.state.getSnapshot() + if (state.phase === 'idle') void this.refresh() + else if (state.info !== undefined && state.phase !== 'failed' && state.phase !== 'closed') this.connect() return () => { this.mounted = false this.detach() @@ -113,22 +117,24 @@ export class TerminalView { } /** - * Start or recover this tab, deduplicating mounts and retries during allocation. - * Only a new tab may allocate a shell; listed terminals cannot be silently replaced. + * Start or explicitly retry this tab, sharing Session discovery with other views. + * New identities allocate directly; confirmed or restored terminals cannot be replaced. * @returns after environment lookup and creation or recovery settle. */ refresh(): Promise { if (this.creation !== undefined) return this.creation if (this.loading !== undefined) return this.loading if (this.closing !== undefined || this.lifetime.signal.aborted) return Promise.resolve() + const retry = this.state.getSnapshot().phase !== 'idle' this.patch({ phase: 'loading', error: undefined, issue: undefined }) this.loading = (async () => { const [environment, available] = await Promise.all([ - this.remote.environment(this.sessionId, this.lifetime.signal), this.remote.list(this.sessionId), + this.remote.environment(this.sessionId, this.lifetime.signal), + this.createWhenMissing ? undefined : this.inventory.read(retry), ]) if (this.stopped()) return this.patch({ environment: valueOf(environment) }) - const info = valueOf(available).find(item => item.id === this.id) + const info = available?.get(this.id) if (info !== undefined) this.adopt(info) else if (this.createWhenMissing) { let path = this.shellPath @@ -156,6 +162,7 @@ export class TerminalView { cols: Math.min(80, environment.maxCols), rows: Math.min(24, environment.maxRows), }, this.lifetime.signal)) if (!this.lifetime.signal.aborted) { + this.inventory.update(info) this.adopt(info) } })().catch((error: unknown) => { this.fail(error) }).finally(() => { this.creation = undefined }) @@ -163,6 +170,7 @@ export class TerminalView { } private adopt(info: WebTerminalInfo): void { + this.createWhenMissing = false this.patch({ info, title: info.title }) if (this.retain === undefined) { if (this.mounted && this.closing === undefined) this.connect(); return } void this.retain(this.lifetime.signal).then(() => { @@ -285,7 +293,9 @@ export class TerminalView { if (title.trim() === this.state.getSnapshot().title || this.lifetime.signal.aborted) return try { valueOf(await this.remote.rename(this.sessionId, this.id, title)) + if (this.stopped()) return const current = this.state.getSnapshot().info + if (current !== undefined) this.inventory.update({ ...current, title: title.trim() }) this.patch({ ...(current === undefined ? {} : { info: { ...current, title: title.trim() } }), title: title.trim() }) } catch (error) { this.fail(error) } } @@ -302,6 +312,7 @@ export class TerminalView { // Even a refused or lost creation response may leave an allocation to close. await this.creation valueOf(await this.remote.close(this.sessionId, this.id)) + this.inventory.remove(this.id) this.detach() this.patch({ phase: 'closed', writable: false }) })().catch((error: unknown) => { this.closing = undefined; this.fail(error); throw error }) @@ -363,6 +374,7 @@ export class TerminalView { continue } if (frame.type !== 'output') { + this.inventory.update(frame.info) this.patch({ info: frame.info, title: frame.info.title, phase: 'connected', writable: frame.info.state === 'running' && frame.writable }) } if (frame.type !== 'state') { @@ -393,6 +405,7 @@ export class TerminalView { private fail(error: unknown): void { const failure = remoteErrorOf(error) const issue = failure?.code === 'terminal/view' ? failure.details.issue : failure?.code === 'terminal/limit-reached' ? 'terminalLimit' : failure?.code === 'terminal/unavailable' ? 'missingTerminal' : undefined + if (!this.stopped() && issue === 'missingTerminal') this.inventory.remove(this.id) this.patch({ phase: error instanceof RemoteStreamCarrierError ? 'disconnected' : 'failed', writable: false, issue, error: error instanceof Error ? error.message : String(error) }) } } diff --git a/packages/api/terminal-controller/tests/inventory.client.spec.ts b/packages/api/terminal-controller/tests/inventory.client.spec.ts new file mode 100644 index 0000000000..4126dcbe84 --- /dev/null +++ b/packages/api/terminal-controller/tests/inventory.client.spec.ts @@ -0,0 +1,109 @@ +/** Shared discovery preserves explicit retries, acknowledged changes and connection ownership. */ +import { expect, it, vi } from 'vitest' +import { RemoteError, type RemoteResult } from '@deepseek-ai/dsh-typert-protocol' +import { TerminalInventory } from '../src/client/inventory.ts' +import type { WebTerminalId, WebTerminalInfo } from '../src/types.ts' + +const info: WebTerminalInfo = { id: 'terminal' as WebTerminalId, title: 'bash', shell: { name: 'bash', path: '/bin/bash', args: [] }, cwd: '/workspace', cols: 80, rows: 24, state: 'running', exitCode: null } +const success = (value: WebTerminalInfo[]): RemoteResult => ({ ok: true, value }) + +it('retains an empty list and a discovery failure until an explicit shared retry', async () => { + const error = new RemoteError('gateway/bad-request', 'Host unavailable', {}) + const load = vi.fn<() => Promise>>() + .mockResolvedValueOnce({ ok: false, error }).mockResolvedValue(success([])) + const inventory = new TerminalInventory(load) + try { + await expect(inventory.read()).rejects.toBe(error) + await expect(inventory.read()).rejects.toBe(error) + expect(load).toHaveBeenCalledOnce() + const retry = inventory.read(true) + expect(inventory.read(true)).toBe(retry) + expect((await retry).size).toBe(0) + expect((await inventory.read()).size).toBe(0) + expect(load).toHaveBeenCalledTimes(2) + } finally { await inventory.dispose() } +}) + +it('merges creation, rename, state frames and closure over an older pending list', async () => { + const listed = Promise.withResolvers>() + const inventory = new TerminalInventory(() => listed.promise) + const created = { ...info, id: 'created' as WebTerminalId } + const closed = { ...info, id: 'closed' as WebTerminalId } + const renamed = { ...info, title: 'Build', state: 'exited' as const, exitCode: 0 } + const pending = inventory.read() + try { + inventory.update(created) + inventory.update(renamed) + inventory.remove(closed.id) + listed.resolve(success([info, closed])) + const result = await pending + expect([...result.values()]).toEqual([renamed, created]) + inventory.remove(created.id) + expect([...(await inventory.read()).values()]).toEqual([renamed]) + } finally { listed.resolve(success([])); await inventory.dispose() } +}) + +it.each(['success', 'failure'] as const)('ignores a previous connection response after replacement: %s', async (outcome) => { + const previous = Promise.withResolvers>() + const current = Promise.withResolvers>() + const load = vi.fn<() => Promise>>() + .mockReturnValueOnce(previous.promise).mockReturnValueOnce(current.promise) + const inventory = new TerminalInventory(load) + const pending = inventory.read() + const next = { ...info, title: 'Current Host' } + try { + const replacement = inventory.reset() + expect(inventory.read()).toBe(replacement) + current.resolve(success([next])) + expect([...(await inventory.read()).values()]).toEqual([next]) + if (outcome === 'success') previous.resolve(success([info])) + else previous.reject(new Error('Old connection failed')) + expect([...(await pending).values()]).toEqual([next]) + expect(load).toHaveBeenCalledTimes(2) + } finally { previous.resolve(success([])); current.resolve(success([])); await inventory.dispose() } +}) + +it('does not discover unused inventories on reconnect and refreshes a settled inventory once', async () => { + const load = vi.fn(async () => success([info])) + const inventory = new TerminalInventory(load) + try { + inventory.update(info) + expect(inventory.reset()).toBeUndefined() + expect(load).not.toHaveBeenCalled() + await inventory.read() + const refreshed = inventory.reset() + expect(inventory.read()).toBe(refreshed) + await refreshed + expect(load).toHaveBeenCalledTimes(2) + } finally { await inventory.dispose() } +}) + +it('joins outstanding requests on disposal and rejects their late results', async () => { + const listed = Promise.withResolvers>() + const load = vi.fn(() => listed.promise) + const inventory = new TerminalInventory(load) + const pending = inventory.read() + const rejected = expect(pending).rejects.toThrow('Terminal inventory disposed') + let disposed = false + const disposing = inventory.dispose().then(() => { disposed = true }) + try { + expect(disposed).toBe(false) + inventory.update(info) + inventory.remove(info.id) + expect(inventory.reset()).toBeUndefined() + await expect(inventory.read()).rejects.toThrow('Terminal inventory disposed') + listed.resolve(success([info])) + await rejected + await disposing + expect(disposed).toBe(true) + expect(load).toHaveBeenCalledOnce() + } finally { listed.resolve(success([])); await disposing } +}) + +it('preserves a non-Error transport rejection as the cached failure cause', async () => { + const load = vi.fn<() => Promise>>().mockRejectedValue('offline') + const inventory = new TerminalInventory(load) + try { + await expect(inventory.read()).rejects.toMatchObject({ message: 'Terminal discovery failed', cause: 'offline' }) + } finally { await inventory.dispose() } +}) diff --git a/packages/api/terminal-controller/tests/model.client.spec.ts b/packages/api/terminal-controller/tests/model.client.spec.ts index fbae792001..5f2563f041 100644 --- a/packages/api/terminal-controller/tests/model.client.spec.ts +++ b/packages/api/terminal-controller/tests/model.client.spec.ts @@ -7,6 +7,7 @@ import { RemoteMock, streamMethod, type StreamHandle } from '@deepseek-ai/dsh-re import type {} from '@deepseek-ai/dsh-api-terminal-controller/remote' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { TerminalView, type TerminalRemote } from '../src/client/model.ts' +import { TerminalInventory } from '../src/client/inventory.ts' import type { TerminalInput, TerminalEnvironment, TerminalFrame, WebTerminalId, WebTerminalInfo } from '../src/types.ts' const sessionId = 'session' as SessionId @@ -66,7 +67,9 @@ function fixture(prepareStream?: (stream: RemoteStream) => void, inp }, } cleanups.push(async () => { await Promise.all(streams.map(stream => stream.dispose())) }) - const model = new TerminalView(sessionId, remote, gateway, info.id) + const inventory = new TerminalInventory(() => remote.list(sessionId)) + cleanups.push(() => inventory.dispose()) + const model = new TerminalView(sessionId, remote, gateway, inventory, info.id) cleanups.push(() => model.dispose()) return { model, remote, gateway, options, streams, generation, attachments, sent } } diff --git a/packages/api/terminal-controller/tests/recovery.client.spec.ts b/packages/api/terminal-controller/tests/recovery.client.spec.ts index a3104bd55b..29a1286f78 100644 --- a/packages/api/terminal-controller/tests/recovery.client.spec.ts +++ b/packages/api/terminal-controller/tests/recovery.client.spec.ts @@ -10,6 +10,7 @@ import type {} from '@deepseek-ai/dsh-api-terminal-controller/remote' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { WebTerminalId, WebTerminalInfo, TerminalEnvironment } from '../src/types.ts' import { TerminalView, type TerminalRemote } from '../src/client/model.ts' +import { TerminalInventory } from '../src/client/inventory.ts' import { TerminalCloseRequests } from '../src/client/close-requests.ts' import { TerminalBindings } from '../src/client/bindings.ts' import * as TerminalClient from '../src/client/index.ts' @@ -52,23 +53,143 @@ function fixture() { })), } const gateway: Pick = { $stream: options => new RemoteStream({ generation: createSnapshotStore(undefined) }, options) } - function view() { - const model = new TerminalView(sessionId, remote, gateway, info.id) + function view(restored = false) { + const inventory = new TerminalInventory(() => remote.list(sessionId)) + cleanups.push(() => inventory.dispose()) + const model = new TerminalView(sessionId, remote, gateway, inventory, info.id, !restored) cleanups.push(() => model.dispose()) return model } - async function service() { + async function service(connected = false) { const ctx = new Context() ctx.provide('remote', { ...gateway, terminal: remote } as never) ctx.provide('remote.terminal', remote) + ctx.provide('connection', { generation: createSnapshotStore(connected ? {} : undefined) } as never) const fiber = ctx.plugin(TerminalClient) cleanups.push(async () => { await ctx.fiber.dispose() }) await fiber - return { service: ctx.webTerminals, dispose: () => fiber.dispose() } + return { service: ctx.webTerminals, ctx, dispose: () => fiber.dispose() } } return { remote, view, service } } +it('shares one Session list across concurrent and later restored terminal views', async () => { + storage() + const h = fixture() + const { service } = await h.service() + const terminals = Array.from({ length: 8 }, (_, index) => ({ ...info, id: `restored-${index}` as WebTerminalId })) + vi.mocked(h.remote.list).mockResolvedValue(success(terminals)) + const first = terminals.slice(0, 4).map((terminal, index) => service.view(sessionId, `tab-${index}`, `content-${index}`, terminal.id)) + await Promise.all(first.map(model => model.refresh())) + for (const [index, terminal] of terminals.slice(4).entries()) { + const model = service.view(sessionId, `later-${index}`, `later-${index}`, terminal.id) + await model.refresh() + expect(model.state.getSnapshot().info?.id).toBe(terminal.id) + } + expect(h.remote.list).toHaveBeenCalledOnce() + expect(h.remote.create).not.toHaveBeenCalled() +}) + +it('creates new terminal identities without reading the Session list', async () => { + storage() + const h = fixture() + const { service } = await h.service() + for (let index = 0; index < 8; index++) { + const model = service.view(sessionId, `new-${index}`, `new-${index}`) + await model.refresh() + } + expect(h.remote.create).toHaveBeenCalledTimes(8) + expect(h.remote.list).not.toHaveBeenCalled() +}) + +it('preserves a missing terminal failure across remounts without retrying discovery', async () => { + storage() + const h = fixture() + const { service } = await h.service() + const model = service.view(sessionId, 'missing', 'missing', info.id) + await model.refresh() + const failed = model.state.getSnapshot() + expect(failed.issue).toBe('missingTerminal') + for (let index = 0; index < 5; index++) { + model.mount()() + await setImmediate() + } + expect(model.state.getSnapshot()).toBe(failed) + expect(h.remote.list).toHaveBeenCalledOnce() + expect(h.remote.create).not.toHaveBeenCalled() +}) + +it('refreshes each discovered Session once per reconnect and ignores the initial connection notification', async () => { + storage() + const h = fixture() + const { service, ctx } = await h.service() + vi.mocked(h.remote.list).mockResolvedValue(success([info])) + await service.view(sessionId, 'a', 'a', info.id).refresh() + await service.view(sessionId, 'b', 'b', info.id).refresh() + await service.view('new-session' as SessionId, 'new', 'new').refresh() + ctx.emit('connection/reset') + expect(h.remote.list).toHaveBeenCalledOnce() + ctx.emit('connection/reset') + await service.recover(sessionId) + expect(h.remote.list).toHaveBeenCalledTimes(2) + expect(vi.mocked(h.remote.list).mock.calls.every(call => call[0] === sessionId)).toBe(true) +}) + +it('refreshes after late plugin activation and retains a failed refresh for explicit retry', async () => { + const h = fixture() + const { service, ctx, dispose } = await h.service(true) + vi.mocked(h.remote.list).mockResolvedValueOnce(success([info])) + expect(await service.recover(sessionId)).toEqual([info]) + vi.mocked(h.remote.list).mockResolvedValueOnce(failure('Offline')) + ctx.emit('connection/reset') + await expect(service.recover(sessionId)).rejects.toThrow('Offline') + await expect(service.recover(sessionId)).rejects.toThrow('Offline') + expect(h.remote.list).toHaveBeenCalledTimes(2) + vi.mocked(h.remote.list).mockResolvedValueOnce(success([])) + expect(await service.recover(sessionId, true)).toEqual([]) + await dispose() + ctx.emit('connection/reset') + expect(h.remote.list).toHaveBeenCalledTimes(3) +}) + +it('shares failed discovery between views and keeps a disposed view from cancelling another waiter', async () => { + storage() + const h = fixture() + const { service } = await h.service() + vi.mocked(h.remote.list).mockResolvedValueOnce(failure('Offline')) + const first = service.view(sessionId, 'a', 'a', info.id) + await first.refresh() + const second = service.view(sessionId, 'b', 'b', info.id) + await second.refresh() + expect(h.remote.list).toHaveBeenCalledOnce() + expect(second.state.getSnapshot().error).toBe('Offline') + const listed = Promise.withResolvers>() + vi.mocked(h.remote.list).mockReturnValueOnce(listed.promise) + const pending = Promise.all([first.refresh(), second.refresh()]) + try { + await first.dispose() + const disposed = first.state.getSnapshot() + listed.resolve(success([info])) + await pending + expect(first.state.getSnapshot()).toBe(disposed) + expect(second.state.getSnapshot().info?.id).toBe(info.id) + expect(h.remote.list).toHaveBeenCalledTimes(2) + } finally { listed.resolve(success([])); await pending } +}) + +it('joins a failed reconnect lookup during plugin disposal without publishing another failure', async () => { + const h = fixture() + const { service, ctx, dispose } = await h.service(true) + await service.recover(sessionId) + const listed = Promise.withResolvers>() + vi.mocked(h.remote.list).mockReturnValueOnce(listed.promise) + ctx.emit('connection/reset') + const disposing = dispose() + listed.reject(new Error('Connection closed during disposal')) + await disposing + expect(h.remote.list).toHaveBeenCalledTimes(2) +}) + it('starts automatically and deduplicates overlapping mounts and refreshes', async () => { const h = fixture() const model = h.view() @@ -90,7 +211,7 @@ it('starts automatically and deduplicates overlapping mounts and refreshes', asy it('recovers a listed process without creating another, including its title and screen', async () => { const h = fixture() vi.mocked(h.remote.list).mockResolvedValue(success([{ ...info, title: 'Build' }])) - const model = h.view() + const model = h.view(true) model.mount() await model.refresh() expect(h.remote.create).not.toHaveBeenCalled() @@ -103,8 +224,10 @@ it('does not recreate a recovered terminal that disappeared after the recovery l const { service } = await h.service() vi.mocked(h.remote.list).mockResolvedValueOnce(success([info])) expect(await service.recover(sessionId)).toEqual([info]) + vi.mocked(h.remote.retain).mockImplementation(() => { throw new RemoteError('terminal/unavailable', 'gone', {}) }) const model = service.view(sessionId, 'recovered', 'recovered', info.id) await model.refresh() + await expect.poll(() => model.state.getSnapshot().issue).toBe('missingTerminal') expect(model.state.getSnapshot()).toMatchObject({ phase: 'failed', writable: false }) expect(model.state.getSnapshot().issue).toBe('missingTerminal') expect(h.remote.create).not.toHaveBeenCalled() @@ -259,15 +382,15 @@ it('queries current held views after a slow recovery response and reports discov const listed = Promise.withResolvers>() vi.mocked(h.remote.list).mockReturnValueOnce(listed.promise) const recovering = service.recover(sessionId) - vi.mocked(h.remote.list).mockResolvedValueOnce(success([info])) const model = service.view(sessionId, 'recovered', 'recovered', info.id) - await model.refresh() - listed.resolve(success([info])) + const loading = model.refresh() + try { expect(h.remote.list).toHaveBeenCalledOnce() } + finally { listed.resolve(success([info])); await loading } expect(await recovering).toEqual([]) expect(service.view(sessionId, 'recovered', 'recovered', info.id)).toBe(model) expect(model.id).toBe(info.id) vi.mocked(h.remote.list).mockResolvedValueOnce(failure('Session unavailable')) - await expect(service.recover(sessionId)).rejects.toThrow('Session unavailable') + await expect(service.recover(sessionId, true)).rejects.toThrow('Session unavailable') }) it('ignores an unknown tab and retries only saved close requests', async () => { @@ -703,7 +826,9 @@ it('ignores a delayed retention failure after the restored view was disposed', a vi.mocked(h.remote.list).mockResolvedValue(success([info])) const retained = Promise.withResolvers() const gateway: Pick = { $stream: options => new RemoteStream({ generation: createSnapshotStore(undefined) }, options) } - const model = new TerminalView(sessionId, h.remote, gateway, info.id, false, undefined, () => retained.promise) + const inventory = new TerminalInventory(() => h.remote.list(sessionId)) + cleanups.push(() => inventory.dispose()) + const model = new TerminalView(sessionId, h.remote, gateway, inventory, info.id, false, undefined, () => retained.promise) cleanups.push(() => model.dispose()) await model.refresh() await model.dispose() diff --git a/packages/api/terminal-controller/tsconfig.client.json b/packages/api/terminal-controller/tsconfig.client.json index 055e471ad7..59af423145 100644 --- a/packages/api/terminal-controller/tsconfig.client.json +++ b/packages/api/terminal-controller/tsconfig.client.json @@ -12,7 +12,8 @@ "src/client/retention.ts", "src/client/shell-preference.ts", "src/types.ts", - "src/client/close-requests.ts" + "src/client/close-requests.ts", + "src/client/inventory.ts" ], "references": [ { @@ -38,6 +39,9 @@ }, { "path": "../../util/deque" + }, + { + "path": "../../client/connection/tsconfig.client.json" } ] } diff --git a/packages/api/workspace-controller/package.json b/packages/api/workspace-controller/package.json index ad3ff8c022..9210f16d13 100644 --- a/packages/api/workspace-controller/package.json +++ b/packages/api/workspace-controller/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-workspace-controller", "description": "Workspace Remote commands and reconnect-safe state transport", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/api/workspace-files/package.json b/packages/api/workspace-files/package.json index 5a61f8d1c4..b3929c75eb 100644 --- a/packages/api/workspace-files/package.json +++ b/packages/api/workspace-files/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-api-workspace-files", "description": "Workspace file service and Client resource provider: bounded reads, directory listing, and live metadata over the workspaceFiles Remote namespace", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/attachment/attachment-local/package.json b/packages/attachment/attachment-local/package.json index 094cd1b4b0..b26c8327ab 100644 --- a/packages/attachment/attachment-local/package.json +++ b/packages/attachment/attachment-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-attachment-local", "description": "Private content-addressed DSH_HOME attachment storage", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/attachment/attachment/package.json b/packages/attachment/attachment/package.json index b7c2088bd1..cf389c756e 100644 --- a/packages/attachment/attachment/package.json +++ b/packages/attachment/attachment/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-attachment", "description": "Durable immutable attachment storage seam for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/boot/app-boot/package.json b/packages/boot/app-boot/package.json index 402df0b09e..1977a920d2 100644 --- a/packages/boot/app-boot/package.json +++ b/packages/boot/app-boot/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-app-boot", "description": "Shared boot glue for the app bins: .env loading, fail-loud Loader guards, snapshot-aware config resolution, and the Loader boot sequence", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/boot/cmdline/package.json b/packages/boot/cmdline/package.json index 6278b1a04a..9fbfb5cdbb 100644 --- a/packages/boot/cmdline/package.json +++ b/packages/boot/cmdline/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-cmdline", "description": "Immutable command-line handoff from a dsh launcher to any app plugin that injects cmdlineArgs", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/boot/config-editor/package.json b/packages/boot/config-editor/package.json index 3d50383961..4d22dea41b 100644 --- a/packages/boot/config-editor/package.json +++ b/packages/boot/config-editor/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-config-editor", "description": "Persist plugin configuration through profile patches and Loader reconciliation", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/boot/hmr/package.json b/packages/boot/hmr/package.json index 65ab59317c..0dcf9b49a7 100644 --- a/packages/boot/hmr/package.json +++ b/packages/boot/hmr/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-hmr", "description": "Coordinated module and profile configuration hot reload", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/boot/plugin-manager/package.json b/packages/boot/plugin-manager/package.json index af390f30ec..387a324956 100644 --- a/packages/boot/plugin-manager/package.json +++ b/packages/boot/plugin-manager/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-plugin-manager", "description": "Current-profile plugin and bundle management shared by dsh CLI, Web and agent tools", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/boot/plugin-market/package.json b/packages/boot/plugin-market/package.json index 0d01b58fcf..1ed7cb7b4d 100644 --- a/packages/boot/plugin-market/package.json +++ b/packages/boot/plugin-market/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-plugin-market", "description": "Configured plugin market indexes for the Web Discover page and the plugin_market agent tool", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/browser-use/browser-use/package.json b/packages/browser-use/browser-use/package.json index be19abf6f4..92206186e0 100644 --- a/packages/browser-use/browser-use/package.json +++ b/packages/browser-use/browser-use/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-browser-use", "description": "Exclusive named browser-use provider registration", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/bundle/acp-app/package.json b/packages/bundle/acp-app/package.json index 5c36ad8bf3..fde797834d 100644 --- a/packages/bundle/acp-app/package.json +++ b/packages/bundle/acp-app/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-acp-app", "description": "The dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-base", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/bundle/base/package.json b/packages/bundle/base/package.json index 2e6e13bcb5..bad07101bb 100644 --- a/packages/bundle/base/package.json +++ b/packages/bundle/base/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-base", "description": "The shared dsh core as a profile bundle: the first patch layer of base-backed profiles, inserting core rows over the empty profile root", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/bundle/headless/package.json b/packages/bundle/headless/package.json index 0ea5e51ab9..3ed8f43f6b 100644 --- a/packages/bundle/headless/package.json +++ b/packages/bundle/headless/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-headless", "description": "The dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layer", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/bundle/sdk-app/package.json b/packages/bundle/sdk-app/package.json index 12c9611c63..e9aab5b18a 100644 --- a/packages/bundle/sdk-app/package.json +++ b/packages/bundle/sdk-app/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sdk-app", "description": "The dsh SDK profile bundle: stdio JSON-RPC serving and process lifecycle over dsh-base", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/bundle/sdk-minimal/package.json b/packages/bundle/sdk-minimal/package.json index 1dce9046a8..954731516d 100644 --- a/packages/bundle/sdk-minimal/package.json +++ b/packages/bundle/sdk-minimal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sdk-minimal", "description": "The standalone minimal SDK profile bundle: JSON-RPC, one DeepSeek adapter, persistent shell, and JSONL sessions", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/bundle/web-app/package.json b/packages/bundle/web-app/package.json index d0f8256e09..9949c36fea 100644 --- a/packages/bundle/web-app/package.json +++ b/packages/bundle/web-app/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web-app", "description": "The dsh browser-surface bundle: the web patch layer over dsh-base plus the runtime glue plugin (frontend dist serving, bash runtime variables, URL line)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/connection/package.json b/packages/client/connection/package.json index ed2a572414..a1303455d1 100644 --- a/packages/client/connection/package.json +++ b/packages/client/connection/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-connection", "description": "Authenticated RPC transport and generation lifecycle", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/file-upload/package.json b/packages/client/file-upload/package.json index 9e0a80e4b5..5ede008af5 100644 --- a/packages/client/file-upload/package.json +++ b/packages/client/file-upload/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-file-upload", "description": "Agent-scoped browser file upload, streaming intake, and staged receipt service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/hmr/package.json b/packages/client/hmr/package.json index ae3999f9d9..0d4d80d579 100644 --- a/packages/client/hmr/package.json +++ b/packages/client/hmr/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-hmr", "description": "Web client graph synchronization and rebuilt-bundle reload transport", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/locale/package.json b/packages/client/locale/package.json index 80bfe0d3df..9293e95665 100644 --- a/packages/client/locale/package.json +++ b/packages/client/locale/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-locale", "description": "Locale plugin: Host-backed preference, extensible language catalog, browser fallback, and typed built-in dictionaries", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/modules/package.json b/packages/client/modules/package.json index 4dea6b9512..80e72d3df3 100644 --- a/packages/client/modules/package.json +++ b/packages/client/modules/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-modules", "description": "Client module system, dual-face: node half composes the __DSH_BOOT__ entry graph (incremental dsh.client scan, bundle route, index tap, webPlugins service); browser half is the lazy-CJS module table the vendored cordis Loader consumes as its internal seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/product-analytics/package.json b/packages/client/product-analytics/package.json index 7c2cca4595..b168224e92 100644 --- a/packages/client/product-analytics/package.json +++ b/packages/client/product-analytics/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-product-analytics", "description": "Desktop product event collection and authenticated Host reporting", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/resources/package.json b/packages/client/resources/package.json index f1a0764dc1..0c7f7475f3 100644 --- a/packages/client/resources/package.json +++ b/packages/client/resources/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-resources", "description": "Unified client resource model: protocol-registered providers turn URL addresses into live values, consumed through the useResource global standard hook", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/shortcuts/package.json b/packages/client/shortcuts/package.json index 4e7c4efe67..5715c055ab 100644 --- a/packages/client/shortcuts/package.json +++ b/packages/client/shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-shortcuts", "description": "Application keyboard command registry and physical-key routing", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/store/package.json b/packages/client/store/package.json index e778b25bb8..3e7be5b523 100644 --- a/packages/client/store/package.json +++ b/packages/client/store/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-store", "description": "React-free observable and snapshot-store contracts with the shared Zustand/Immer engine", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-agent-preset/package.json b/packages/client/ui-agent-preset/package.json index 4d422833ba..7f35f20e96 100644 --- a/packages/client/ui-agent-preset/package.json +++ b/packages/client/ui-agent-preset/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-agent-preset", "description": "Agent-preset surfaces: the default for later sessions, this session's seat, and the composition editor", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-approval/package.json b/packages/client/ui-approval/package.json index 0bc13fcac9..9b98bfcf78 100644 --- a/packages/client/ui-approval/package.json +++ b/packages/client/ui-approval/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-approval", "description": "Approval composer takeover over the scoped Remote Event waterfall", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-attachment/package.json b/packages/client/ui-attachment/package.json index 1087377ba9..89aa2133d3 100644 --- a/packages/client/ui-attachment/package.json +++ b/packages/client/ui-attachment/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-attachment", "description": "Dynamic attachment presentation plugin for conversation input, message-image, and trajectory image slots", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-brand-official/package.json b/packages/client/ui-brand-official/package.json index 7605437782..1200f2ec9e 100644 --- a/packages/client/ui-brand-official/package.json +++ b/packages/client/ui-brand-official/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-brand-official", "description": "Official DeepSeek Harness brand occupants for the Web client's sidebar slots", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-chat/package.json b/packages/client/ui-chat/package.json index 7692f43431..600891f0ec 100644 --- a/packages/client/ui-chat/package.json +++ b/packages/client/ui-chat/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-chat", "description": "Chat Conversation target, node definitions, renderers, and details surface", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-commands/package.json b/packages/client/ui-commands/package.json index b81b71f412..8b420220b8 100644 --- a/packages/client/ui-commands/package.json +++ b/packages/client/ui-commands/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-commands", "description": "Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-conversation/package.json b/packages/client/ui-conversation/package.json index f46b4affa7..af2dfe734b 100644 --- a/packages/client/ui-conversation/package.json +++ b/packages/client/ui-conversation/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-conversation", "description": "Target-neutral Conversation assembly, shell, composer, queue, and view navigation", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-deliverables/package.json b/packages/client/ui-deliverables/package.json index 204db77b79..cd27c21231 100644 --- a/packages/client/ui-deliverables/package.json +++ b/packages/client/ui-deliverables/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-deliverables", "description": "Changed-files card with per-file comparison tabs, delivery cards, and clickable final-response file references for Web", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-directory-picker-browse/package.json b/packages/client/ui-directory-picker-browse/package.json index 63ab41f757..bc385b1d93 100644 --- a/packages/client/ui-directory-picker-browse/package.json +++ b/packages/client/ui-directory-picker-browse/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-directory-picker-browse", "description": "In-app directory browsing surface: the workspace directory-flow owner rendering the host's listing and creation primitives", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-directory-picker-native/package.json b/packages/client/ui-directory-picker-native/package.json index de2fa0b644..028cd7f5bb 100644 --- a/packages/client/ui-directory-picker-native/package.json +++ b/packages/client/ui-directory-picker-native/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-directory-picker-native", "description": "Native directory-picker surface: the renderless workspace directory-flow occupant driving the local Desktop or Host OS chooser", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-dockkit/package.json b/packages/client/ui-dockkit/package.json index 30f63d7952..4b0e96a932 100644 --- a/packages/client/ui-dockkit/package.json +++ b/packages/client/ui-dockkit/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-dockkit", "description": "Docking layout kit: split-tree engine with invertible operations, and the React components that render and drive it (zero cordis)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-goal/package.json b/packages/client/ui-goal/package.json index 57fecbba84..d311effb7a 100644 --- a/packages/client/ui-goal/package.json +++ b/packages/client/ui-goal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-goal", "description": "Session goal surface: GoalBar docked above the composer, read from the goal session projection", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-input-trigger/package.json b/packages/client/ui-input-trigger/package.json index c87423762e..99c77ef47c 100644 --- a/packages/client/ui-input-trigger/package.json +++ b/packages/client/ui-input-trigger/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-input-trigger", "description": "Input trigger pipeline: '/' and '@' detection, candidate menu, pick routing to registered sources", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-jobs/package.json b/packages/client/ui-jobs/package.json index 848c6b5234..568f0c4d9f 100644 --- a/packages/client/ui-jobs/package.json +++ b/packages/client/ui-jobs/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-jobs", "description": "Session-header background-job list with on-demand streaming record panels", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "type": "module", "main": "lib/index.js", "types": "lib/types/index.d.ts", diff --git a/packages/client/ui-layout/package.json b/packages/client/ui-layout/package.json index e18ff2a308..1364e73ff9 100644 --- a/packages/client/ui-layout/package.json +++ b/packages/client/ui-layout/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-layout", "description": "Shell plugin: three-column AppFrame with drag handles, ctx.layout viewing-state service (navigation + panels)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-message-feedback/package.json b/packages/client/ui-message-feedback/package.json index 77eb42149e..53ead2c2a3 100644 --- a/packages/client/ui-message-feedback/package.json +++ b/packages/client/ui-message-feedback/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-message-feedback", "description": "The Web feedback surface: per-message Like/Dislike in the assistant-message action strip and the feedback dialog behind both ratings and /feedback, backed by the messageFeedback and sessionFeedback Host Remotes", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-model-selection/package.json b/packages/client/ui-model-selection/package.json index ed88e48db8..cf4cc6bbac 100644 --- a/packages/client/ui-model-selection/package.json +++ b/packages/client/ui-model-selection/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-model-selection", "description": "Model selection over the shared model catalog, Session projection, and session.selectModel", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-open-in-app/package.json b/packages/client/ui-open-in-app/package.json index c905cb0c95..d60f650542 100644 --- a/packages/client/ui-open-in-app/package.json +++ b/packages/client/ui-open-in-app/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-open-in-app", "description": "Web \"Open In...\" controls: the Session-header split button opening the workspace directory in an installed application, and the document preview's default-application controls for one file", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-permission-presets/package.json b/packages/client/ui-permission-presets/package.json index 19ca816109..2da0aa5520 100644 --- a/packages/client/ui-permission-presets/package.json +++ b/packages/client/ui-permission-presets/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-permission-presets", "description": "Permission surfaces: a new-session default in General settings and a current-session /permission popup over the permissions projection", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-plan/package.json b/packages/client/ui-plan/package.json index 711a302cee..f17c979ef2 100644 --- a/packages/client/ui-plan/package.json +++ b/packages/client/ui-plan/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-plan", "description": "Plan mode controls, persistent transcript plan cards, and sidebar Markdown previews", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-plugin-manager/package.json b/packages/client/ui-plugin-manager/package.json index c8aa26b659..08cb28d792 100644 --- a/packages/client/ui-plugin-manager/package.json +++ b/packages/client/ui-plugin-manager/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-plugin-manager", "description": "Plugin management for the dsh web client: the sidebar Plugins panel installs, enables, disables, retries, and composes installed plugin packages", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-plugin-market/package.json b/packages/client/ui-plugin-market/package.json index 8345c167d4..4b0faffe5b 100644 --- a/packages/client/ui-plugin-market/package.json +++ b/packages/client/ui-plugin-market/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-plugin-market", "description": "Discover tab of the dsh web Plugins page: browse configured plugin market indexes and install their bundles", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-primitives/package.json b/packages/client/ui-primitives/package.json index c7a527537f..20938d74c7 100644 --- a/packages/client/ui-primitives/package.json +++ b/packages/client/ui-primitives/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-primitives", "description": "Pure React atoms for the dsh web UI: controls, icons, markdown, and JSON inspectors (zero cordis)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-reference/package.json b/packages/client/ui-reference/package.json index 9407948899..613dbbbb68 100644 --- a/packages/client/ui-reference/package.json +++ b/packages/client/ui-reference/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-reference", "description": "Unified Web @file and @session reference source", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-renderer/package.json b/packages/client/ui-renderer/package.json index 16eb56ae73..9804270cc4 100644 --- a/packages/client/ui-renderer/package.json +++ b/packages/client/ui-renderer/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-renderer", "description": "Browser UI renderer: React slot bindings, ctx.uiRenderer, and the assembled application root", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-schedule/package.json b/packages/client/ui-schedule/package.json index 42c4e8b7c0..9093cf049a 100644 --- a/packages/client/ui-schedule/package.json +++ b/packages/client/ui-schedule/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-schedule", "description": "Host task management page and Session reminder catalog", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "type": "module", "main": "lib/index.js", "types": "lib/types/index.d.ts", diff --git a/packages/client/ui-secret-mask/package.json b/packages/client/ui-secret-mask/package.json index bcea18b512..572cc78f84 100644 --- a/packages/client/ui-secret-mask/package.json +++ b/packages/client/ui-secret-mask/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-secret-mask", "description": "Web display masking for recognized credentials in sent user text", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-session/package.json b/packages/client/ui-session/package.json index 51117e93f5..805a0911eb 100644 --- a/packages/client/ui-session/package.json +++ b/packages/client/ui-session/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-session", "description": "Session Controller adapter for React and session-scoped slots", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-account/package.json b/packages/client/ui-settings-account/package.json index 6acb8547e7..a79538e709 100644 --- a/packages/client/ui-settings-account/package.json +++ b/packages/client/ui-settings-account/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-account", "description": "Manage DeepSeek login and open Platform billing pages", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-agent-loop/package.json b/packages/client/ui-settings-agent-loop/package.json index 9ac8e7f2a9..b48efdc710 100644 --- a/packages/client/ui-settings-agent-loop/package.json +++ b/packages/client/ui-settings-agent-loop/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-agent-loop", "description": "Settings page of the agent loop on the dsh web client's Plugins page: the parallel tool-call cap of the agent-loop namespace", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-general/package.json b/packages/client/ui-settings-general/package.json index 9ade4ce5c1..425d28a799 100644 --- a/packages/client/ui-settings-general/package.json +++ b/packages/client/ui-settings-general/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-general", "description": "Settings ownerless-copy and product onboarding plugin: the General section, shell trigger/header chrome content, settings dictionaries, and the versioned welcome notice", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-models/README.i18n.yaml b/packages/client/ui-settings-models/README.i18n.yaml index 6774d1be46..58750ae463 100644 --- a/packages/client/ui-settings-models/README.i18n.yaml +++ b/packages/client/ui-settings-models/README.i18n.yaml @@ -21,8 +21,8 @@ en: f4817eb2e3537ed9 zh: e589c193fa85473f /deepseek-ai-dsh-client-ui-settings-models/use-this-package/editing-a-provider: - en: eac663a25c60adf9 - zh: 24450ef377a79e01 + en: 3af73644655c8b7b + zh: d689eba772100c63 /deepseek-ai-dsh-client-ui-settings-models/use-this-package/adding-and-deleting-providers: en: 9d533c65c82cd4a4 zh: 0a5da334e127c035 diff --git a/packages/client/ui-settings-models/README.md b/packages/client/ui-settings-models/README.md index e2ed635385..1b07faff50 100644 --- a/packages/client/ui-settings-models/README.md +++ b/packages/client/ui-settings-models/README.md @@ -53,12 +53,14 @@ A blank key field offers a saved credential when one is detected, with no status An endpoint without a fixed catalog default shows the provider's `baseURLEnv` configuration names below the input. A profile endpoint remains the first placeholder choice. -The collapsed 自定义设置 fold carries the curated extras: `baseURL` for both families (the placeholder shows the endpoint the profile resolves to, the public endpoint for deepseek, else the `defaultBaseURL` the configurable-provider directory reports for a pi-ai catalog route), each adapter's model catalog, and the **display name** and **API protocol** of a pi-ai route the adapter does not ship. Profile `headers` remain deployment configuration in `cordis.patch.yml` or Cordis config and have no Models-page editor. The Provider ID stays fixed: it is the settings key, the name every other namespace and every logged session references, and the stem of a credential reference the page cannot read back to move. Reasoning effort is deliberately not among the editable fields: it is a per-model capability, so a provider-scoped control could only be set to a value some models reject. Each model row edits `id`, optional display `name`, optional `contextWindow`/`maxTokens`, and input types; unrelated model fields survive edits. +The collapsed 自定义设置 fold carries the curated extras: `baseURL` for both families (the placeholder shows the endpoint the profile resolves to, the public endpoint for deepseek, else the `defaultBaseURL` the configurable-provider directory reports for a pi-ai catalog route), each adapter's model catalog, and the **display name** and **API protocol** of a pi-ai route the adapter does not ship. Profile `headers` remain deployment configuration in `cordis.patch.yml` or Cordis config and have no Models-page editor. The Provider ID stays fixed: it is the settings key, the name every other namespace and every logged session references, and the stem of a credential reference the page cannot read back to move. There is no provider-scoped reasoning-effort field: effort is a per-model capability, so a provider-scoped control could only be set to a value some models reject. Each model row edits `id`, optional display `name`, optional `contextWindow`/`maxTokens`, input types, and, on a hand-declared pi-ai route, reasoning levels; unrelated model fields survive edits. The DeepSeek card edits the `llm-deepseek` endpoint, credentials, and model catalog. It uses Messages, with the default endpoint placeholder `https://api.deepseek.com/anthropic`. Expand **Customized settings → Model options** to edit each model. Both provider families share the same row layout, labels, and icons: context window and max output tokens occupy two columns, and **Input types** occupies a separate row with **Text** and **Image** checkboxes. A row without an input declaration displays the installed model’s input types, then the provider default, then Text. Known pi-ai providers load their installed catalog without endpoint I/O; opening a row does not write an override. Explicit input selections take precedence, including text-only overrides of vision models. Checkbox edits save the selected types, with at least one type required. DeepSeek writes `inputModalities`; pi-ai writes `input`. Unchecking Image for DeepSeek also removes `imagePixelBudget` and `imageMaxBytes`, because the adapter rejects those limits without image input. Clearing the input field in `cordis.patch.yml` restores adapter inheritance; **Restore default models** resets the entire catalog override. Declare only input types the upstream model can actually process. +A model row on a hand-declared pi-ai route also edits its **Reasoning levels**, the `reasoningEfforts` the adapter already reads from `cordis.patch.yml`; the **?** beside the label explains the two columns. With the switch off the field is absent, so the model does not reason and the model picker offers no level; a stored `false` is kept until the switch is used. Turning it on always starts from rows for off, low, medium, and high; it does not restore levels removed earlier, and **Cancel** discards the edit instead. Each row pairs a level select — the keys `llm-pi-ai`'s configuration schema accepts in `reasoningEfforts`, read from the namespace schema like the protocol choices and today `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`, each usable once — with the **API value** sent for it, which starts as the level name; a field cleared while typing stores the level name and shows it once the field is left. Off only turns reasoning off, so its value field is disabled; a value already written for it in `cordis.patch.yml` is shown and kept. Changing a row's level resets a value still spelled as the old level; a custom value carries over only between two thinking levels, so changing a row to or from Off resets it. A stored key the schema does not list keeps its row and stays selectable there. Rows can be added and deleted and are saved in row order, while the model picker lists the levels in escalation order. The section refuses what the adapter refuses: no level besides Off — including a `reasoningEfforts:` left without a value, which shows the switch on with no rows — or a value that is missing on a level besides Off or empty on any level. A refused value marks its field invalid, and leaving that field fills in the level name. The rows keep no state of their own, so a model row reused after a deletion, a reset, or a fold shows only its own model. Routes served from the installed catalog and the DeepSeek card keep the adapter's levels and show no reasoning control. + ### Adding and deleting providers **Add model provider** is the one entry to the add card, offered while any editor-backed settings namespace is mounted and enabled while either mode below can proceed. The card opens on a segmented mode switch with a one-line purpose beneath it: **Third-party model provider** carries the dormant-directory provider select over the provider editor — a bare-mounted `llm-pi-ai` offers its whole installed catalog before any route exists — and **Custom model API** carries the create form for a route pi-ai does not ship: a relay, a self-hosted server, or any other OpenAI- or Anthropic-compatible endpoint. A mode is offered only while its namespace is mounted; with one mode the card shows that form alone under the mode's name, and a mode whose namespace has nothing left to adopt or no protocol to declare is disabled with the reason as its hover text. A panel mounts the first time its mode is shown and stays mounted, hidden, while the card is open and its mode stays offered, so switching modes discards neither draft; the switch locks while either panel has a write or an endpoint interrogation in flight, since a switch underneath one would orphan its answer. Each tab controls its panel through ids, and closing either panel forgets the catalog draft's target. The custom form asks for a unique **Provider ID**, an endpoint, a protocol, and at least one uniquely-identified model, because nothing can default those. The protocol picker names each protocol by its product name — OpenAI Chat Completions, OpenAI Responses, Anthropic Messages — while the stored value stays the schema identifier; a protocol the adapter adds before this page names it shows that identifier. The endpoint placeholder follows the selected protocol: OpenAI Chat Completions and Responses show `https://gateway.example/v1`, while Anthropic Messages shows `https://gateway.example` because its SDK appends `/v1/messages`. Changing the protocol preserves any address already entered. The endpoint must be a parseable HTTP or HTTPS URL; localhost, IPv4 and IPv6 literals, and custom ports remain valid. A syntax error blocks both discovery and creation at the field, while a request failure remains a separate provider error. **Fetch available models** asks the `llm/discoverModels` Remote about the endpoint the form shows, so adding a provider is one pass instead of save-then-return; the reply opens a searchable picker rather than being written, and nothing is written until **Add selected**. Each candidate displays its original model id in a monospace font to distinguish models with the same name. Rows stay on one line; hovering reveals the model name, falling back to the full id when no name is supplied. Selection and configuration retain the original model id. Each selected candidate copies its id, display name, context window, output-token cap, and disclosed input types into the editable row when disclosed, while an existing row retains its user-tuned values. Search matches model ids and optional display names without clearing hidden selections. **Select all** adds the visible results, while **Deselect all** clears the entire selection so hidden results cannot be adopted accidentally. A row is deletable only when the user layer alone carries it (removal restores the composition base), and its confirmation dialog names the provider. diff --git a/packages/client/ui-settings-models/README.zh.md b/packages/client/ui-settings-models/README.zh.md index 9bcb1e0971..f272908232 100644 --- a/packages/client/ui-settings-models/README.zh.md +++ b/packages/client/ui-settings-models/README.zh.md @@ -53,12 +53,14 @@ API 密钥输入框初始为空,并通过 `autocomplete="new-password"` 请求 目录未提供固定默认端点时,输入框下方显示提供方的 `baseURLEnv` 配置变量名。Profile 中的端点仍是占位符的首选。 -收起的「自定义设置」折叠区承载精选的额外字段:两个家族都有 `baseURL`(占位符显示 profile 解析出的端点,deepseek 即公共端点;没有时显示可配置提供商目录为 pi-ai 内置路由报告的 `defaultBaseURL`)、各适配器自己的模型目录,以及适配器未提供的 pi-ai 路由的**显示名称**与 **API 协议**。Profile `headers` 仍是 `cordis.patch.yml` 或 Cordis 配置中的部署配置,Models 页面不提供编辑器。Provider ID 保持固定:它是 settings 的键、其他每个 namespace 与每一条已记录会话引用的名字,也是页面读不回、因而搬不走的凭据引用词干。推理等级刻意不在可编辑字段之列:它是按模型的能力,提供商级的控件只可能被设成某些模型会拒绝的值。每个模型行可编辑 `id`、可选显示 `name`、可选 `contextWindow`/`maxTokens` 和输入类型;无关的模型字段在编辑后仍会保留。 +收起的「自定义设置」折叠区承载精选的额外字段:两个家族都有 `baseURL`(占位符显示 profile 解析出的端点,deepseek 即公共端点;没有时显示可配置提供商目录为 pi-ai 内置路由报告的 `defaultBaseURL`)、各适配器自己的模型目录,以及适配器未提供的 pi-ai 路由的**显示名称**与 **API 协议**。Profile `headers` 仍是 `cordis.patch.yml` 或 Cordis 配置中的部署配置,Models 页面不提供编辑器。Provider ID 保持固定:它是 settings 的键、其他每个 namespace 与每一条已记录会话引用的名字,也是页面读不回、因而搬不走的凭据引用词干。这里没有提供商级的推理等级字段:它是按模型的能力,提供商级的控件只可能被设成某些模型会拒绝的值。每个模型行可编辑 `id`、可选显示 `name`、可选 `contextWindow`/`maxTokens`、输入类型,以及手工声明的 pi-ai 路由上的推理等级;无关的模型字段在编辑后仍会保留。 `llm-deepseek` 的 DeepSeek 卡片编辑端点、凭据和模型目录。它使用 Messages,默认端点占位符为 `https://api.deepseek.com/anthropic`。 展开**自定义设置 → 模型选项**编辑模型。两个提供商家族共用相同的模型行布局、标签和图标:上下文窗口与最大输出 token 数分为两列,**输入类型**独占下一行,提供**文本**和**图片**复选框。未声明输入类型的模型行优先显示已安装模型的输入类型,其次是提供商默认值,最后回退为文本。已知 pi-ai 提供商会加载已安装目录,不向端点发送请求;打开模型行不会写入覆盖值。显式输入选择具有优先权,包括为视觉模型设置的仅文本覆盖。修改复选框会保存所选类型,且至少保留一种。DeepSeek 写入 `inputModalities`,pi-ai 写入 `input`。DeepSeek 取消勾选图片时,还会移除 `imagePixelBudget` 和 `imageMaxBytes`,因为适配器在没有图片输入时拒绝这些限制。在 `cordis.patch.yml` 中清除输入字段可恢复适配器继承;**恢复默认模型**会重置整个模型目录覆盖。仅声明上游模型实际能够处理的输入类型。 +手工声明的 pi-ai 路由上,模型行还会编辑**推理等级**,即适配器本来就从 `cordis.patch.yml` 读取的 `reasoningEfforts`;标签旁的 **?** 说明两列的含义。开关关闭时该字段不存在,模型不推理,模型选择器不提供任何档位;已存储的 `false` 在开关被操作前保持不变。打开开关总是从 off、low、medium 和 high 四行开始,不会恢复之前删掉的档位;要放弃修改请点**取消**。每行把一个档位选择框——可选值是 `llm-pi-ai` 配置 schema 允许作为 `reasoningEfforts` 键的档位,与协议选项一样从命名空间 schema 读取,目前为 `off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`,每个档位只能用一次——与发送给它的 **API 参数值**配成一对,参数值初始为档位名;输入时清空的字段会存为档位名,离开字段后显示出来。Off 只表示关闭思考,因此它的参数值输入框是禁用的;`cordis.patch.yml` 中已为它写好的值会显示并保留。修改某行的档位时,仍是旧档位名的参数值会随之重置;自定义值只在两个思考档位之间保留,切到或切离 Off 都会重置。schema 未列出的已存键会保留自己的行,并可在该行中继续选择。行可以增删,并按行的顺序保存,模型选择器则按升级顺序列出档位。本节拒绝适配器会拒绝的配置:没有 Off 以外的档位(包括未写值的 `reasoningEfforts:`,此时开关显示为打开且没有任何行),或者 Off 以外的档位缺少参数值、任意档位的参数值为空字符串。被拒绝的值会把对应输入框标为无效,离开该输入框时会填入档位名。各行不保留自己的状态,因此删除、恢复默认或折叠之后被复用的模型行只显示它自己的模型。走已安装目录的路由和 DeepSeek 卡片沿用适配器的档位,不显示推理等级控件。 + ### 新增与删除提供商 **添加模型提供商**是新增卡片的唯一入口:只要任一带编辑器的设置 namespace 已挂载就会出现,下述两种方式任一可继续时才可点击。卡片打开后顶部是分段式的方式切换,下方一行小字说明所选方式的用途:**第三方模型提供商**承载休眠目录提供商选择框与提供商编辑器——裸挂载的 `llm-pi-ai` 在任何路由存在之前就能提供其完整的已安装 catalog;**自定义模型 API**承载为 pi-ai 不提供的路由(中转站、自部署服务或其他兼容 OpenAI / Anthropic 协议的接口)准备的创建表单。某种方式只在其 namespace 已挂载时提供;只剩一种方式时卡片以该方式为标题直接显示表单;namespace 已无可采用的提供商或没有可声明的协议时,对应方式会被禁用并把原因作为悬停提示。面板在其方式首次显示时挂载,之后在卡片打开且该方式仍被提供期间保持挂载但隐藏,因此切换方式不会丢掉任何一边的草稿;任一面板有写入或端点探测进行中时滑块锁定,因为此时切换会让结果落到看不见的面板上。每个 tab 通过 id 控制其面板,关闭任一面板都会忘记目录草稿的目标。自定义表单会索要唯一的 **Provider ID**、端点、协议与至少一个可唯一识别的模型,因为没有东西能为它们兜底。协议选择框以产品名称显示各协议——OpenAI Chat Completions、OpenAI Responses、Anthropic Messages——存储的值仍是 schema 标识符;适配器新增而本页尚未命名的协议直接显示其标识符。端点占位示例随所选协议变化:OpenAI Chat Completions 与 Responses 显示 `https://gateway.example/v1`,Anthropic Messages 显示 `https://gateway.example`,因为其 SDK 会追加 `/v1/messages`。切换协议会保留已输入的地址。端点必须是可解析的 HTTP 或 HTTPS URL;localhost、IPv4 与 IPv6 字面地址以及自定义端口仍然有效。语法错误会在字段处阻止询问与创建,请求失败则继续作为独立的提供商错误显示。**获取可用模型**通过 `llm/discoverModels` Remote 查询表单显示的端点,因此新增提供商一次即可完成,而非先保存再返回;回复打开的是可搜索选择器而非直接写入,只有点击**添加所选**才会写入。候选项使用等宽字体显示原始模型 id,以区分同名模型。每行保持单行,悬停可查看模型名称,未提供名称时回退到完整 id。勾选与配置仍使用原始模型 id。每个选中候选会在提供商公布相应信息时,把 id、显示名、上下文窗口、最大输出 token 数和已公布的输入类型复制进可编辑行;已经存在的行保留用户调整过的值。搜索会匹配模型 id 与可选显示名称,且不会清除隐藏项的勾选状态。**全选**会加入可见结果,而**取消全选**会清空全部勾选,以免意外采用隐藏结果。只有用户层单独携带某行时,该行才可删除(删除会恢复组合基线),其确认对话框会指名该提供商。 diff --git a/packages/client/ui-settings-models/package.json b/packages/client/ui-settings-models/package.json index 84bf24fe80..00401c684e 100644 --- a/packages/client/ui-settings-models/package.json +++ b/packages/client/ui-settings-models/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-models", "description": "Models settings and shared product-onboarding dialogs over existing settings and credential joins", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-models/src/client/CustomProviderCard.tsx b/packages/client/ui-settings-models/src/client/CustomProviderCard.tsx index 08d27fdf18..1a16603664 100644 --- a/packages/client/ui-settings-models/src/client/CustomProviderCard.tsx +++ b/packages/client/ui-settings-models/src/client/CustomProviderCard.tsx @@ -17,11 +17,12 @@ * and at least one model — are required here rather than at load, so the * failure names the field while the user is still looking at it. * - * There is deliberately no reasoning-effort control, here or on the editor - * card: effort is a per-MODEL capability, and the models under one provider - * disagree about it, so a provider-scoped control can only be set to a value - * some of them reject. The composer's model picker offers each model its own - * levels instead. + * There is deliberately no provider-scoped reasoning-effort control, here or + * on the editor card: effort is a per-MODEL capability, and the models under + * one provider disagree about it, so a provider-scoped control can only be set + * to a value some of them reject. Each model row of a hand-declared route + * declares its own `reasoningEfforts` instead, and the composer's model picker + * offers each model those levels. */ import { useEffect, useState } from 'react' @@ -33,6 +34,7 @@ import { EditorFooter } from './EditorFooter.tsx' import { validateDeepSeekModels } from './DeepSeekModelsEditor.tsx' import { ModelListEditor } from './ModelListEditor.tsx' import type { ModelDraft } from './ModelListEditor.tsx' +import { validateReasoningEfforts } from './ModelReasoningEfforts.tsx' import { deriveKeyRef } from './store.ts' import { protocolLabel } from './protocol-label.ts' import type { ModelsOperations } from './operations.ts' @@ -67,6 +69,8 @@ export interface CustomProviderCardProps { taken: readonly string[] /** Wire protocols the adapter can serve, in the order it reports them. */ protocols: readonly string[] + /** Reasoning levels the adapter's schema accepts, in escalation order; empty shows no reasoning control. */ + reasoningLevels: readonly string[] /** * Revision of the `llm-pi-ai` user section this card opened at, sent with * the create so a route another tab declared meanwhile is a refusal rather @@ -94,7 +98,7 @@ export interface CustomProviderCardProps { * @returns the creation card. */ export function CustomProviderCard(props: CustomProviderCardProps): ReactNode { - const { taken, protocols, operations, t, onBusyChange } = props + const { taken, protocols, reasoningLevels, operations, t, onBusyChange } = props // The write is checked against the revision on which this draft was opened. const [openedAt] = useState(() => props.revision) const [route, setRoute] = useState('') @@ -123,8 +127,9 @@ export function CustomProviderCard(props: CustomProviderCardProps): ReactNode { const baseUrlInvalid = baseURL.length > 0 && !isHttpUrl(normalizedBaseURL) // Rows are checked by the same per-row validator the editor cards use, so a // bad row is named by its position here too. Capacities have route-level - // fallbacks; what a route cannot default is at least one model. - const modelFailure = validateDeepSeekModels(models) + // fallbacks; what a route cannot default is at least one model. Every row of + // this hand-declared route edits its reasoning levels, so they are checked too. + const modelFailure = validateDeepSeekModels(models) ?? validateReasoningEfforts(models) const keyFailure = apiKeyFailure(keyDraft) // The typed key with paste whitespace removed. A blank field yields an empty // string, which the create path reads as "no key supplied" — a route may @@ -287,6 +292,7 @@ export function CustomProviderCard(props: CustomProviderCardProps): ReactNode { 0 : wire === null && level === 'off' +} + +/** + * Read the declared levels from a row in their stored order, including a key + * the offered levels do not name, so an edit never drops it. + * @param model - model row as drafted. + * @returns the entries; empty for a valueless field, which pi-ai refuses, and + * `undefined` when the field is absent or `false`. + */ +function readEfforts(model: DeepSeekModelDraft): Entries | undefined { + const value = model['reasoningEfforts'] + if (value === null) return [] + if (typeof value !== 'object' || Array.isArray(value)) return undefined + return Object.entries(value) +} + +/** + * Replace a row's declared levels, preserving every other field. + * @param model - model row as drafted. + * @param entries - the levels to write in row order, or `undefined` to remove the field. + * @returns the replacement row. + */ +function withEfforts(model: DeepSeekModelDraft, entries: Entries | undefined): DeepSeekModelDraft { + const next = { ...model } + if (entries === undefined) { + // Absent is how a hand-declared model says it does not reason. + Reflect.deleteProperty(next, 'reasoningEfforts') + return next + } + next['reasoningEfforts'] = Object.fromEntries(entries) + return next +} + +/** A reasoning-level failure the Models cards name by row. */ +export interface ReasoningEffortsFailure { + /** Zero-based model position. */ + index: number + /** Message key owned by the Models settings section. */ + key: 'modelReasoningValueRequired' | 'modelReasoningNeedsLevel' +} + +/** + * Mirror the pi-ai refusals of a hand-declared `reasoningEfforts`, so a card + * names the row instead of failing at the write. + * @param value - drafted `models` value, or `undefined` while inherited. + * @returns the first refused row, or `undefined` when the adapter will accept every row. + */ +export function validateReasoningEfforts(value: unknown): ReasoningEffortsFailure | undefined { + for (const [index, model] of modelDrafts(value).entries()) { + const entries = readEfforts(model) + if (entries === undefined) continue + if (entries.some(([level, wire]) => !acceptedValue(level, wire))) return { index, key: 'modelReasoningValueRequired' } + if (!entries.some(([level]) => level !== 'off')) return { index, key: 'modelReasoningNeedsLevel' } + } + return undefined +} + +/** Props of {@link ModelReasoningEfforts}. */ +interface ModelReasoningEffortsProps { + /** Effective model row, including fields outside the curated editor. */ + model: DeepSeekModelDraft + /** Levels the adapter's schema accepts as `reasoningEfforts` keys, in escalation order. */ + levels: readonly string[] + /** One-based row position for accessible names. */ + position: number + /** Prevent changes while read-only or saving. */ + disabled: boolean + /** Section copy. */ + t: (key: ModelsKey) => string + /** Replace this row, preserving unrelated configuration. */ + onChange: (model: DeepSeekModelDraft) => void +} + +/** + * Edit the `reasoningEfforts` a hand-declared pi-ai model offers: a switch, + * then one row per level pairing the level with the value sent for it. Every + * row is read from the model, so a reused instance shows its new model. + * @param props - model declaration and row replacement action. + * @returns the reasoning-level group. + */ +export function ModelReasoningEfforts({ model, levels, position, disabled, t, onChange }: ModelReasoningEffortsProps): ReactNode { + const entries = readEfforts(model) + // Only the focused field keeps its own text, so a field cleared while typing + // stays empty although its level's name is stored. Leaving the field drops + // it, and deleting a model, resetting the list, or folding these options all + // move focus first, so no text outlives the field it was typed into. + const [typing, setTyping] = useState<{ level: string; text: string } | undefined>(undefined) + const label = `${t('modelReasoning')} ${String(position)}` + const used = entries?.map(([level]) => level) ?? [] + + const write = (next: Entries | undefined): void => { onChange(withEfforts(model, next)) } + const replaceAt = (current: Entries, index: number, entry: readonly [string, unknown]): Entries => + current.map((existing, at) => at === index ? entry : existing) + + const changeLevel = (current: Entries, index: number, from: string, wire: unknown, to: string): void => { + // Only a custom spelling between two thinking levels carries over; a value + // still spelled as its old level, or one crossing to or from Off, resets. + const carried = typeof wire === 'string' && wire.length > 0 && wire !== from && from !== 'off' && to !== 'off' + write(replaceAt(current, index, [to, carried ? wire : defaultValue(to)])) + } + + const addLevel = (current: Entries): void => { + const highest = Math.max(-1, ...used.map(level => levels.indexOf(level))) + const level = levels.find((candidate, index) => index > highest && !used.includes(candidate)) + ?? levels.find(candidate => !used.includes(candidate)) + /* v8 ignore next -- the add button is disabled once every level has a row */ + if (level === undefined) return + write([...current, [level, defaultValue(level)]]) + } + + const editValue = (current: Entries, index: number, level: string, text: string): void => { + setTyping({ level, text }) + const trimmed = text.trim() + write(replaceAt(current, index, [level, trimmed.length === 0 ? defaultValue(level) : trimmed])) + } + + const leaveValue = (current: Entries, index: number, level: string, wire: unknown): void => { + setTyping(undefined) + // Leaving a field whose stored value the adapter refuses repairs it with the level name. + if (!acceptedValue(level, wire)) write(replaceAt(current, index, [level, defaultValue(level)])) + } + + // A stored key the offered levels do not name stays selectable in its own row. + const choicesFor = (level: string): readonly string[] => [ + ...levels.includes(level) ? [] : [level], + ...levels.filter(choice => choice === level || !used.includes(choice)), + ] + const initialEntries = INITIAL_LEVELS.filter(level => levels.includes(level)).map(level => [level, defaultValue(level)] as const) + + const refused = entries?.some(([level, wire]) => !acceptedValue(level, wire)) === true + const missingLevel = entries !== undefined && !used.some(level => level !== 'off') + + return ( +
+
+ + {t('modelReasoning')} + + + + + { write(on ? initialEntries : undefined) }} + /> +
+ {entries === undefined + ? null + : ( + <> + {entries.length > 0 + ? ( + + ) + : null} + {entries.map(([level, wire], index) => ( + // A row is the entry at this position of the stored dict: changing its + // level edits that entry in place and rows keep no state of their own, + // so the position is the row's identity. Keying by level would remount + // the select on every change and drop keyboard focus. +
+ + { editValue(entries, index, level, event.target.value) }} + onBlur={() => { leaveValue(entries, index, level, wire) }} + /> + +
+ ))} + {refused + ?

{t('modelReasoningValueRequired')}

+ : missingLevel ?

{t('modelReasoningNeedsLevel')}

: null} + + + )} +
+ ) +} diff --git a/packages/client/ui-settings-models/src/client/ModelRow.tsx b/packages/client/ui-settings-models/src/client/ModelRow.tsx index b1c76adf8d..22a1177a40 100644 --- a/packages/client/ui-settings-models/src/client/ModelRow.tsx +++ b/packages/client/ui-settings-models/src/client/ModelRow.tsx @@ -7,6 +7,7 @@ import { import type { DeepSeekModelDraft } from './DeepSeekModelsEditor.tsx' import type { ModelsKey } from './locales.ts' import { ModelInputTypes } from './ModelInputTypes.tsx' +import { ModelReasoningEfforts } from './ModelReasoningEfforts.tsx' import styles from './ModelsSection.module.css' /** A capacity's editable text and adapter-specific inherited hint. */ @@ -24,6 +25,8 @@ interface ModelRowProps { inputField: 'inputModalities' | 'input' inputFallback?: readonly string[] | undefined inputLoading?: boolean + /** Levels the row's `reasoningEfforts` may declare; absent or empty shows no reasoning control. */ + reasoningLevels?: readonly string[] | undefined expanded: boolean disabled: boolean t: (key: ModelsKey) => string @@ -106,6 +109,13 @@ export function ModelRow(props: ModelRowProps): ReactNode { model={model} field={props.inputField} position={position} fallback={props.inputFallback} disabled={disabled || props.inputLoading === true} t={t} onChange={props.onChange} /> + {props.reasoningLevels !== undefined && props.reasoningLevels.length > 0 + ? ( + + ) + : null} ) : null} diff --git a/packages/client/ui-settings-models/src/client/ModelsSection.module.css b/packages/client/ui-settings-models/src/client/ModelsSection.module.css index fba78c573a..438cc7b319 100644 --- a/packages/client/ui-settings-models/src/client/ModelsSection.module.css +++ b/packages/client/ui-settings-models/src/client/ModelsSection.module.css @@ -575,6 +575,44 @@ line-height: 18px; } +.modelReasoning { + grid-column: 1 / -1; + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; +} + +.modelReasoningLabel { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.modelReasoningHead { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +/* Level select, API value, and delete action; the column labels share the grid. */ +.modelReasoningRow { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; + align-items: center; + gap: 6px; +} + +.modelReasoningRow .selectInput { + max-width: none; +} + +.modelReasoningValueInput { + font-family: var(--ds-font-family-code); + font-size: 13px; +} + .modelEmpty { padding: 12px; border: 1px dashed var(--dsw-alias-border-l3); diff --git a/packages/client/ui-settings-models/src/client/ModelsSection.tsx b/packages/client/ui-settings-models/src/client/ModelsSection.tsx index f3cc85ea78..461ab37aad 100644 --- a/packages/client/ui-settings-models/src/client/ModelsSection.tsx +++ b/packages/client/ui-settings-models/src/client/ModelsSection.tsx @@ -29,7 +29,7 @@ import type { LlmNativeAuth, SettingsNamespaceView } from '@deepseek-ai/dsh-api- // Type-only: pulls this package's SlotMap merge (the two Models child slots). import type {} from './slot-contract.ts' import { CustomProviderCard } from './CustomProviderCard.tsx' -import { deriveKeyRef, protocolChoices, providerUsable } from './store.ts' +import { deriveKeyRef, protocolChoices, providerUsable, reasoningLevelChoices } from './store.ts' import type { ModelsSettingsStore, ProviderRow } from './store.ts' import type { ModelsOperations } from './operations.ts' import type { SettingsSchemaOperations } from './schema-operations.ts' @@ -376,6 +376,7 @@ function Loaded({ injected, renderSlot }: { injected: ModelsSectionFace; renderS // there is nothing to declare and the mode is not offered. const piAi = state.namespaces.get('llm-pi-ai') const protocols = protocolChoices(piAi, schema) + const reasoningLevels = reasoningLevelChoices(piAi, schema) // Each mode is offered while its namespace is mounted and enabled while it // has something to offer; the card shows the chosen mode where both are // offered, else the only one there is. A mode's panel is mounted while it is @@ -673,6 +674,7 @@ function Loaded({ injected, renderSlot }: { injected: ModelsSectionFace; renderS row.entry.provider)} protocols={protocols} + reasoningLevels={reasoningLevels} revision={piAi.revision} operations={operations} t={t} diff --git a/packages/client/ui-settings-models/src/client/ProviderEditor.tsx b/packages/client/ui-settings-models/src/client/ProviderEditor.tsx index 67c5fb345f..262f8c9d55 100644 --- a/packages/client/ui-settings-models/src/client/ProviderEditor.tsx +++ b/packages/client/ui-settings-models/src/client/ProviderEditor.tsx @@ -29,10 +29,13 @@ import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives' import { DeepSeekModelsEditor, modelDrafts, validateDeepSeekModels, } from './DeepSeekModelsEditor.tsx' +import type { DeepSeekModelsValidationFailure } from './DeepSeekModelsEditor.tsx' import { apiKeyFailure } from './apiKey.ts' import { EditorFooter } from './EditorFooter.tsx' import { ModelListEditor } from './ModelListEditor.tsx' -import { deriveKeyRef, protocolChoices } from './store.ts' +import { validateReasoningEfforts } from './ModelReasoningEfforts.tsx' +import type { ReasoningEffortsFailure } from './ModelReasoningEfforts.tsx' +import { deriveKeyRef, protocolChoices, reasoningLevelChoices } from './store.ts' import { protocolLabel } from './protocol-label.ts' import type { ModelsOperations } from './operations.ts' import type { SettingsSchemaOperations } from './schema-operations.ts' @@ -221,7 +224,17 @@ export function ProviderEditor(props: ProviderEditorProps): ReactNode { // The model list is validated by the same per-row checker for both families, // so a bad row is named by its position rather than by a blanket message. - const modelFailure = validateDeepSeekModels(schema.getPath(draft, ['models'])) + // Only a hand-declared pi-ai route's rows edit reasoning levels — the one + // model list with no installed catalog behind it — so only there are the + // levels read and the declarations checked. + const reasoningLevels = useMemo( + () => layout === 'pi-ai' && props.declared === true ? reasoningLevelChoices(namespace, schema) : [], + [layout, props.declared, namespace, schema], + ) + const editsReasoning = reasoningLevels.length > 0 + const validateModels = (value: unknown): DeepSeekModelsValidationFailure | ReasoningEffortsFailure | undefined => + validateDeepSeekModels(value) ?? (editsReasoning ? validateReasoningEfforts(value) : undefined) + const modelFailure = validateModels(schema.getPath(draft, ['models'])) const keyFailure = apiKeyFailure(keyDraft) // What a probe or a write must carry: the typed key with paste whitespace // removed. A blank field yields an empty string, which both call sites read @@ -265,7 +278,7 @@ export function ProviderEditor(props: ProviderEditorProps): ReactNode { // with a bad row; it stays because the schema check below would refuse // the write with a message naming a path instead of the row, and because // nothing but this function decides what is written. - const failure = validateDeepSeekModels(schema.getPath(next, ['models'])) + const failure = validateModels(schema.getPath(next, ['models'])) /* v8 ignore next 3 -- unreachable from the card: the same failure disables submit */ if (failure !== undefined) { return `${t('model')} ${String(failure.index + 1)}: ${t(failure.key)}` @@ -515,6 +528,7 @@ export function ProviderEditor(props: ProviderEditorProps): ReactNode { entry.value).filter((value): value is string => typeof value === 'string') } +/** + * The reasoning levels a hand-declared route's models may declare, read out of + * the owning namespace's own schema for the same reason as + * {@link protocolChoices}: the levels the page offers and the keys the + * adapter accepts in `reasoningEfforts` come from the same `Config`. + * @param namespace - the namespace view whose schema declares the profile shape. + * @param schema - settings schema operations. + * @returns the levels in the schema's escalation order, or an empty list when the schema has none. + */ +export function reasoningLevelChoices( + namespace: SettingsNamespaceView | undefined, + schema: SettingsSchemaOperations, +): string[] { + if (namespace === undefined) return [] + const path = ['providers', PROBE_ROUTE, 'models', '0', 'reasoningEfforts'] + const node = schema.nodeAtPath(schema.rehydrate(namespace.schema), path) + // `false | { [level]: value }`: the dict branch's key schema lists the levels. + const branches = (node as { list?: readonly { type?: string; sKey?: unknown }[] } | undefined)?.list ?? [] + const keys = branches.find(branch => branch.type === 'dict')?.sKey as { list?: readonly { value?: unknown }[] } | undefined + return (keys?.list ?? []).map(entry => entry.value).filter((value): value is string => typeof value === 'string') +} + /** The credential reference a resolved profile names (its `apiKeyEnv` field). */ function apiKeyEnvOf( namespace: SettingsNamespaceView | undefined, diff --git a/packages/client/ui-settings-models/tests/model-reasoning-efforts.client.spec.tsx b/packages/client/ui-settings-models/tests/model-reasoning-efforts.client.spec.tsx new file mode 100644 index 0000000000..6e31ede058 --- /dev/null +++ b/packages/client/ui-settings-models/tests/model-reasoning-efforts.client.spec.tsx @@ -0,0 +1,302 @@ +// @vitest-environment jsdom +/** Reasoning levels of hand-declared pi-ai models: echo, level rows, API values, refusals, and placement. */ +import { useState } from 'react' +import { cleanup, fireEvent, render, screen, within } from '@testing-library/react' +import { afterEach, describe, expect, it, vi } from 'vitest' +import type { DeepSeekModelDraft } from '../src/client/DeepSeekModelsEditor.tsx' +import { validateDeepSeekModels } from '../src/client/DeepSeekModelsEditor.tsx' +import { ModelListEditor } from '../src/client/ModelListEditor.tsx' +import { ModelReasoningEfforts, validateReasoningEfforts } from '../src/client/ModelReasoningEfforts.tsx' +import type { ModelsOperations } from '../src/client/operations.ts' +import { en } from '../src/client/locales.ts' + +afterEach(cleanup) + +const t = (key: keyof typeof en): string => en[key] +const DEFAULTS = { off: null, low: 'low', medium: 'medium', high: 'high' } +/** The levels llm-pi-ai's schema lists today. */ +const LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] + +/** Render the controlled group over local state, reporting every write. */ +function renderGroup(initial: DeepSeekModelDraft, options: { disabled?: boolean; levels?: readonly string[] } = {}) { + const writes: DeepSeekModelDraft[] = [] + function Harness() { + const [model, setModel] = useState(initial) + return ( + { writes.push(next); setModel(next) }} + /> + ) + } + render() + return { writes, efforts: () => writes.at(-1)?.['reasoningEfforts'] } +} + +const toggle = () => screen.getByRole('switch', { name: `${en.modelReasoning} 1` }) +const levels = () => screen.queryAllByRole('combobox').map(select => select.value) +const select = (row: number) => screen.getByRole('combobox', { name: `${en.modelReasoningLevel} ${String(row)}` }) +const value = (level: string) => screen.getByRole('textbox', { name: `${level} ${en.modelReasoningValue}` }) +const remove = (level: string) => screen.getByRole('button', { name: `${en.modelReasoningRemove} ${level}` }) +const add = () => screen.getByRole('button', { name: en.modelReasoningAdd }) +const entriesOf = (efforts: unknown) => Object.entries(efforts as object) + +describe('ModelReasoningEfforts', () => { + it.each([ + ['absent', { id: 'chat' }], + ['false', { id: 'chat', reasoningEfforts: false }], + ])('shows a model whose field is %s as switched off without writing', (_label, model) => { + const { writes } = renderGroup(model) + expect(toggle().getAttribute('aria-checked')).toBe('false') + expect(levels()).toEqual([]) + expect(writes).toEqual([]) + }) + + it('echoes stored levels in their stored order with their API values', () => { + renderGroup({ id: 'gateway', reasoningEfforts: { off: null, max: 'ultra', high: 'high' } }) + expect(toggle().getAttribute('aria-checked')).toBe('true') + expect(screen.getByRole('button', { name: en.modelReasoningHelp })).toBeTruthy() + expect(levels()).toEqual(['off', 'max', 'high']) + expect(value('off').value).toBe('') + expect(value('off').placeholder).toBe(en.modelReasoningOffValuePlaceholder) + expect(value('off').disabled).toBe(true) + expect(value('max').value).toBe('ultra') + expect(value('high').value).toBe('high') + // Each select offers its own level and the levels no other row uses. + expect([...select(3).options].map(option => option.value)).toEqual(['minimal', 'low', 'medium', 'high', 'xhigh']) + expect(screen.queryByText(en.modelReasoningValueRequired)).toBeNull() + }) + + it('turns on with off, low, medium, and high while keeping other fields', () => { + const { writes } = renderGroup({ id: 'chat', contextWindow: 131072, compat: { supportsDeveloperRole: false } }) + fireEvent.click(toggle()) + expect(writes.at(-1)).toEqual({ + id: 'chat', contextWindow: 131072, compat: { supportsDeveloperRole: false }, reasoningEfforts: DEFAULTS, + }) + expect(levels()).toEqual(['off', 'low', 'medium', 'high']) + }) + + it('removes the field when switched off and starts from the defaults when switched back on', () => { + const { writes } = renderGroup({ id: 'chat', reasoningEfforts: { off: 'none', high: 'deep' } }) + fireEvent.click(toggle()) + expect(writes[0]).toEqual({ id: 'chat' }) + fireEvent.click(toggle()) + expect(writes[1]).toEqual({ id: 'chat', reasoningEfforts: DEFAULTS }) + }) + + it('appends the next unused level above the highest row, then the lowest unused one', () => { + const { efforts } = renderGroup({ id: 'chat', reasoningEfforts: { low: 'low', max: 'max' } }) + fireEvent.click(add()) + expect(levels()).toEqual(['low', 'max', 'off']) + expect(entriesOf(efforts())).toEqual([['low', 'low'], ['max', 'max'], ['off', null]]) + fireEvent.click(add()) + expect(levels()).toEqual(['low', 'max', 'off', 'minimal']) + expect(entriesOf(efforts())).toEqual([['low', 'low'], ['max', 'max'], ['off', null], ['minimal', 'minimal']]) + }) + + it('stops adding once every level has a row', () => { + renderGroup({ id: 'chat', reasoningEfforts: { off: null, minimal: 'minimal', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' } }) + fireEvent.click(add()) + expect(levels()).toHaveLength(7) + expect(add().disabled).toBe(true) + }) + + it('changes a level in place, carrying a custom value only between thinking levels', () => { + const { efforts } = renderGroup({ id: 'chat', reasoningEfforts: { low: 'low', high: 'deep', max: 'max' } }) + fireEvent.change(select(1), { target: { value: 'xhigh' } }) + expect(levels()).toEqual(['xhigh', 'high', 'max']) + expect(entriesOf(efforts())).toEqual([['xhigh', 'xhigh'], ['high', 'deep'], ['max', 'max']]) + fireEvent.change(select(2), { target: { value: 'medium' } }) + expect(entriesOf(efforts())).toEqual([['xhigh', 'xhigh'], ['medium', 'deep'], ['max', 'max']]) + fireEvent.change(select(2), { target: { value: 'off' } }) + expect(entriesOf(efforts())).toEqual([['xhigh', 'xhigh'], ['off', null], ['max', 'max']]) + fireEvent.change(select(2), { target: { value: 'low' } }) + expect(entriesOf(efforts())).toEqual([['xhigh', 'xhigh'], ['low', 'low'], ['max', 'max']]) + }) + + it('edits API values, storing the level name for a cleared field and showing it once the field is left', () => { + const { efforts } = renderGroup({ id: 'gateway', reasoningEfforts: { off: 'none', high: 'high' } }) + expect(value('off').value).toBe('none') + fireEvent.change(value('high'), { target: { value: ' deep ' } }) + expect(efforts()).toEqual({ off: 'none', high: 'deep' }) + fireEvent.change(value('high'), { target: { value: '' } }) + expect(value('high').value).toBe('') + expect(efforts()).toEqual({ off: 'none', high: 'high' }) + fireEvent.blur(value('high')) + expect(value('high').value).toBe('high') + }) + + it.each([ + ['a level besides off without a value', { off: null, high: null }, 'high'], + ['an empty value', { off: null, high: '' }, 'high'], + ['an empty off value', { off: '', high: 'high' }, 'off'], + ])('flags %s that the adapter refuses', (_label, stored, level) => { + renderGroup({ id: 'chat', reasoningEfforts: stored }) + expect(value(level).getAttribute('aria-invalid')).toBe('true') + expect(screen.getByText(en.modelReasoningValueRequired)).toBeTruthy() + }) + + it('repairs a refused value with the level name once its field is left', () => { + const { efforts } = renderGroup({ id: 'chat', reasoningEfforts: { off: null, high: null } }) + expect(value('high').value).toBe('') + fireEvent.focus(value('high')) + fireEvent.blur(value('high')) + expect(efforts()).toEqual({ off: null, high: 'high' }) + expect(value('high').getAttribute('aria-invalid')).toBe('false') + expect(screen.queryByText(en.modelReasoningValueRequired)).toBeNull() + }) + + it('leaves an accepted value alone when its field is left', () => { + const { writes } = renderGroup({ id: 'chat', reasoningEfforts: { high: 'deep' } }) + fireEvent.blur(value('high')) + expect(writes).toEqual([]) + }) + + it('shows a valueless field as switched on with no levels, which the adapter refuses', () => { + const { writes } = renderGroup({ id: 'chat', reasoningEfforts: null }) + expect(toggle().getAttribute('aria-checked')).toBe('true') + expect(levels()).toEqual([]) + expect(screen.getByText(en.modelReasoningNeedsLevel)).toBeTruthy() + fireEvent.click(add()) + expect(writes.at(-1)).toEqual({ id: 'chat', reasoningEfforts: { off: null } }) + }) + + it('deletes a row and requires a level besides off', () => { + const { efforts } = renderGroup({ id: 'chat', reasoningEfforts: { off: null, high: 'high' } }) + expect(screen.queryByText(en.modelReasoningNeedsLevel)).toBeNull() + fireEvent.click(remove('high')) + expect(levels()).toEqual(['off']) + expect(efforts()).toEqual({ off: null }) + expect(screen.getByText(en.modelReasoningNeedsLevel)).toBeTruthy() + fireEvent.click(remove('off')) + expect(levels()).toEqual([]) + expect(efforts()).toEqual({}) + }) + + it('offers the levels it is given, seeding only the defaults the list names', () => { + const { efforts } = renderGroup({ id: 'gateway' }, { levels: ['off', 'low', 'high', 'ultra'] }) + fireEvent.click(toggle()) + expect(entriesOf(efforts())).toEqual([['off', null], ['low', 'low'], ['high', 'high']]) + expect([...select(3).options].map(option => option.value)).toEqual(['high', 'ultra']) + fireEvent.click(add()) + expect(levels()).toEqual(['off', 'low', 'high', 'ultra']) + expect(add().disabled).toBe(true) + }) + + it('keeps a stored key the offered levels do not name', () => { + const { efforts } = renderGroup({ id: 'gateway', reasoningEfforts: { turbo: 'turbo-x', high: 'high' } }) + expect(levels()).toEqual(['turbo', 'high']) + expect([...select(1).options].map(option => option.value)).toEqual(['turbo', 'off', 'minimal', 'low', 'medium', 'xhigh', 'max']) + fireEvent.change(value('high'), { target: { value: 'deep' } }) + expect(entriesOf(efforts())).toEqual([['turbo', 'turbo-x'], ['high', 'deep']]) + }) + + it('disables every control while read-only or saving', () => { + const { writes } = renderGroup({ id: 'chat', reasoningEfforts: { high: 'deep' } }, { disabled: true }) + expect((toggle() as HTMLButtonElement).disabled).toBe(true) + expect(select(1).disabled).toBe(true) + expect(value('high').disabled).toBe(true) + expect(remove('high').disabled).toBe(true) + expect(add().disabled).toBe(true) + toggle().click() + remove('high').click() + add().click() + expect(writes).toEqual([]) + }) +}) + +describe('reasoning-level validation', () => { + it.each([ + [{}], + [null], + [{ off: null }], + [{ off: 'none' }], + ])('refuses %j, which offers nothing beyond off', (stored) => { + expect(validateReasoningEfforts([{ id: 'ok' }, { id: 'chat', reasoningEfforts: stored }])) + .toEqual({ index: 1, key: 'modelReasoningNeedsLevel' }) + }) + + it.each([ + [{ high: null }], + [{ off: null, high: '' }], + [{ off: '', high: 'high' }], + ])('refuses %j, which holds a value the adapter rejects', (stored) => { + expect(validateReasoningEfforts([{ id: 'chat', reasoningEfforts: stored }])) + .toEqual({ index: 0, key: 'modelReasoningValueRequired' }) + }) + + it.each([[undefined], [false], [{ high: 'high' }], [{ off: null, max: 'ultra' }]])('accepts %j', (stored) => { + expect(validateReasoningEfforts([{ id: 'chat', reasoningEfforts: stored }])).toBeUndefined() + }) + + it('accepts an inherited model list', () => { + expect(validateReasoningEfforts(undefined)).toBeUndefined() + }) + + it('stays out of the shared model checker that DeepSeek and catalog rows use', () => { + expect(validateDeepSeekModels([{ id: 'chat', reasoningEfforts: { off: null } }])).toBeUndefined() + }) +}) + +describe('reasoning levels in the model list', () => { + const operations: ModelsOperations = { + discoverModels: () => Promise.resolve({ kind: 'found', models: [] }), + storeCredential: vi.fn(), + removeCredential: vi.fn(), + writeSettings: vi.fn(), + } + + /** Render the list over local state, reporting every write. */ + function renderList(initial: DeepSeekModelDraft[]) { + const writes: DeepSeekModelDraft[][] = [] + function Harness() { + const [models, setModels] = useState(initial) + return ( + { writes.push(next); setModels(next) }} reasoningLevels={LEVELS} + probe={{ settingsNs: 'llm-pi-ai' }} operations={operations} disabled={false} t={t} onBusyChange={() => {}} + /> + ) + } + render() + return { writes } + } + + it('edits a hand-declared route row', () => { + const { writes } = renderList([{ id: 'my-reasoner' }]) + fireEvent.click(screen.getByRole('button', { name: `${en.modelAdvanced} 1` })) + fireEvent.click(toggle()) + expect(writes.at(-1)).toEqual([{ id: 'my-reasoner', reasoningEfforts: DEFAULTS }]) + }) + + it('writes only the surviving model\'s own levels after the row above it is deleted', () => { + // Rows are keyed by position, so the survivor reuses the deleted model's editor instance. + const { writes } = renderList([{ id: 'a' }, { id: 'b' }]) + fireEvent.click(screen.getByRole('button', { name: `${en.modelAdvanced} 1` })) + fireEvent.click(screen.getByRole('button', { name: `${en.modelAdvanced} 2` })) + const first = within(screen.getByRole('group', { name: `${en.modelReasoning} 1` })) + fireEvent.click(first.getByRole('switch')) + fireEvent.click(first.getByRole('button', { name: `${en.modelReasoningRemove} medium` })) + fireEvent.change(first.getByRole('textbox', { name: `high ${en.modelReasoningValue}` }), { target: { value: 'deep' } }) + fireEvent.click(first.getByRole('switch')) + fireEvent.click(screen.getByRole('button', { name: `${en.removeModel} 1` })) + expect(writes.at(-1)).toEqual([{ id: 'b' }]) + + fireEvent.click(within(screen.getByRole('group', { name: `${en.modelReasoning} 1` })).getByRole('switch')) + expect(writes.at(-1)).toEqual([{ id: 'b', reasoningEfforts: DEFAULTS }]) + }) + + it.each([ + ['without a level list', undefined], + ['with an empty level list', []], + ])('shows no reasoning control %s, leaving catalog rows to the installed catalog', (_label, reasoningLevels) => { + render( {}} + />) + fireEvent.click(screen.getByRole('button', { name: `${en.modelAdvanced} 1` })) + expect(screen.getByRole('group', { name: `${en.modelInputTypes} 1` })).toBeTruthy() + expect(screen.queryByRole('switch', { name: `${en.modelReasoning} 1` })).toBeNull() + }) +}) diff --git a/packages/client/ui-settings-models/tests/provider-form.client.spec.tsx b/packages/client/ui-settings-models/tests/provider-form.client.spec.tsx index eb0e920fbc..7a49fa4505 100644 --- a/packages/client/ui-settings-models/tests/provider-form.client.spec.tsx +++ b/packages/client/ui-settings-models/tests/provider-form.client.spec.tsx @@ -12,7 +12,7 @@ import type { ModelsSectionInjected, ModelsSectionProps } from '../src/client/Mo import { CustomProviderCard } from '../src/client/CustomProviderCard.tsx' import { formatCapacity, parseCapacity } from '../src/client/DeepSeekModelsEditor.tsx' import { SettingsDescribeMirror } from '@deepseek-ai/dsh-client-ui-settings/src/client/settings-mirror.ts' -import { ModelsSettingsStore, deriveKeyRef, protocolChoices } from '../src/client/store.ts' +import { ModelsSettingsStore, deriveKeyRef, protocolChoices, reasoningLevelChoices } from '../src/client/store.ts' import { createModelsOperations } from '../src/client/operations.ts' import type { ModelsOperations } from '../src/client/operations.ts' import { en } from '../src/client/locales.ts' @@ -23,6 +23,7 @@ afterEach(cleanup) const t: ModelsSectionInjected['t'] = key => en[key] const PROTOCOLS = ['openai-completions', 'openai-responses', 'anthropic-messages'] +const LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] /** The pi-ai profile shape as the host serializes it, including the layer-1 fields. */ const PiAiConfig = Schema.object({ @@ -37,6 +38,10 @@ const PiAiConfig = Schema.object({ name: Schema.string(), contextWindow: Schema.number(), maxTokens: Schema.number(), + reasoningEfforts: Schema.union([ + Schema.const(false), + Schema.dict(Schema.union([Schema.string(), Schema.const(null)]), Schema.union(LEVELS)), + ]), })), reasoning: Schema.union(['off', 'high']), })), @@ -264,6 +269,23 @@ describe('protocolChoices', () => { }) }) +describe('reasoningLevelChoices', () => { + it('reads the levels out of the reasoningEfforts key schema and nothing else', () => { + const { namespace } = scriptedFace() + expect(reasoningLevelChoices(namespace, settingsSchema)).toEqual(LEVELS) + expect(reasoningLevelChoices(undefined, settingsSchema)).toEqual([]) + const plain = { ...namespace, schema: JSON.parse(JSON.stringify(Schema.object({}).toJSON())) as JsonValue } + expect(reasoningLevelChoices(plain, settingsSchema)).toEqual([]) + const withoutDict = Schema.object({ + providers: Schema.dict(Schema.object({ + models: Schema.array(Schema.object({ reasoningEfforts: Schema.union([Schema.const(false)]) })), + })), + }) + const falseOnly = { ...namespace, schema: JSON.parse(JSON.stringify(withoutDict.toJSON())) as JsonValue } + expect(reasoningLevelChoices(falseOnly, settingsSchema)).toEqual([]) + }) +}) + describe('model list editing', () => { it('changes image input without rewriting a neighboring model declaration', async () => { const neighbor = { id: 'vision', input: ['image'], name: 'Kept vision model' } @@ -675,7 +697,7 @@ describe('endpoint interrogation', () => { const scripted = scriptedFace() render( , ) @@ -850,6 +872,7 @@ describe('hand-declared providers', () => { { expect(screen.getByLabelText(en.customApi).value).toBe('') }) + it.each([ + ['blocks a hand-declared route', true], + ['leaves a catalog route to the adapter', false], + ])('%s whose stored reasoning levels the adapter refuses', async (_label, declared) => { + // Only a hand-declared route's rows edit reasoning levels, so only there + // can the card name the row and offer the switch that repairs it. + await mountSection({ + providers: { + 'acme-gateway': { + api: 'openai-completions', + models: [{ id: 'acme-large', reasoningEfforts: { off: null } }], + }, + }, + declaredRoutes: declared ? ['acme-gateway'] : [], + }) + openEditor('acme-gateway') + + const refusal = `${en.model} 1: ${en.modelReasoningNeedsLevel}` + expect(screen.queryByText(refusal) !== null).toBe(declared) + expect(buttonNamed(en.apply).disabled).toBe(declared) + }) + it('retries only the key after the profile landed, and reports the provider on cancel', async () => { const set = vi.fn() .mockResolvedValueOnce(remoteFail('credential store is read-only')) diff --git a/packages/client/ui-settings-plugin-inventory/package.json b/packages/client/ui-settings-plugin-inventory/package.json index fa07f754c0..4c4fb57521 100644 --- a/packages/client/ui-settings-plugin-inventory/package.json +++ b/packages/client/ui-settings-plugin-inventory/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-plugin-inventory", "description": "Read-only Cordis Loader inventory tab in Web Plugins settings", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-plugins/package.json b/packages/client/ui-settings-plugins/package.json index ef54469a3c..8e497f5299 100644 --- a/packages/client/ui-settings-plugins/package.json +++ b/packages/client/ui-settings-plugins/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-plugins", "description": "Built-in plugins settings section for the dsh web client: the Settings navigation entry and the tab chrome feature-owned tabs register into", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-session-log/package.json b/packages/client/ui-settings-session-log/package.json index 79b0f4bf04..b657db4479 100644 --- a/packages/client/ui-settings-session-log/package.json +++ b/packages/client/ui-settings-session-log/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-session-log", "description": "General settings control for Session-log upload with DeepSeek API requests", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-shell/package.json b/packages/client/ui-settings-shell/package.json index da5df7a340..ac7a8dcd6c 100644 --- a/packages/client/ui-settings-shell/package.json +++ b/packages/client/ui-settings-shell/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-shell", "description": "Settings page of the shell executor on the dsh web client's Plugins page: the command timeout and the per-stream output cap of the shell namespace", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-subagent/package.json b/packages/client/ui-settings-subagent/package.json index 5dbedc4b82..121827acf6 100644 --- a/packages/client/ui-settings-subagent/package.json +++ b/packages/client/ui-settings-subagent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-subagent", "description": "Settings page of Subagent delegation on the dsh web client's Plugins page: recursion depth, parallel capacity, and the models agents may choose for subagents", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings-web-search/package.json b/packages/client/ui-settings-web-search/package.json index 1b806093d7..9be0af668c 100644 --- a/packages/client/ui-settings-web-search/package.json +++ b/packages/client/ui-settings-web-search/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings-web-search", "description": "Settings page of the DeepSeek web-search provider on the dsh web client's Plugins page: its API key, endpoint, and per-request search budget", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-settings/package.json b/packages/client/ui-settings/package.json index f532908190..83ac9cbf88 100644 --- a/packages/client/ui-settings/package.json +++ b/packages/client/ui-settings/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-settings", "description": "Settings domain base plugin: shared configuration forms and the canonical settings slot-type contract", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-shortcuts/package.json b/packages/client/ui-shortcuts/package.json index e5e13f8f91..866036d5e1 100644 --- a/packages/client/ui-shortcuts/package.json +++ b/packages/client/ui-shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-shortcuts", "description": "Keyboard shortcut reference, recording, and local preference editing", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-sidebar-browser/package.json b/packages/client/ui-sidebar-browser/package.json index 24d6b56a46..950dcbdcdb 100644 --- a/packages/client/ui-sidebar-browser/package.json +++ b/packages/client/ui-sidebar-browser/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-sidebar-browser", "description": "Sandboxed Web browser tabs for the right Sidebar", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-sidebar-documentpreview/package.json b/packages/client/ui-sidebar-documentpreview/package.json index b178f0d7ed..8801ed4ec4 100644 --- a/packages/client/ui-sidebar-documentpreview/package.json +++ b/packages/client/ui-sidebar-documentpreview/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-sidebar-documentpreview", "description": "Extensible Sidebar previews for Office documents, spreadsheets, Markdown, code, images, PDF, HTML, and plain text", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-sidebar-files/package.json b/packages/client/ui-sidebar-files/package.json index f4ca8332e1..45476245fd 100644 --- a/packages/client/ui-sidebar-files/package.json +++ b/packages/client/ui-sidebar-files/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-sidebar-files", "description": "Workspace file tree tab type for the right Sidebar: lazy directory listing over the workspaceFiles Remote namespace, opening files into the Sidebar", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-sidebar-right/package.json b/packages/client/ui-sidebar-right/package.json index b7723a3d0b..0212d4533a 100644 --- a/packages/client/ui-sidebar-right/package.json +++ b/packages/client/ui-sidebar-right/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-sidebar-right", "description": "Right Sidebar: the docking surface's session-bound state, its panel and header expand control, and the navigation service over it", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-sidebar-terminal/package.json b/packages/client/ui-sidebar-terminal/package.json index cf0dda5732..355fe4b863 100644 --- a/packages/client/ui-sidebar-terminal/package.json +++ b/packages/client/ui-sidebar-terminal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-sidebar-terminal", "description": "Interactive shell tabs for the right Sidebar", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-sidebar/package.json b/packages/client/ui-sidebar/package.json index a5e0629f26..0f2cd8c211 100644 --- a/packages/client/ui-sidebar/package.json +++ b/packages/client/ui-sidebar/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-sidebar", "description": "Sidebar plugin: session multi-level tree, search, grouping, state dots", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-skill/package.json b/packages/client/ui-skill/package.json index ba6f20647f..7ee3f76d2f 100644 --- a/packages/client/ui-skill/package.json +++ b/packages/client/ui-skill/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-skill", "description": "Web skill references and the dedicated skill tool row", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-slots/package.json b/packages/client/ui-slots/package.json index 53a9a89abc..0ea200b9f7 100644 --- a/packages/client/ui-slots/package.json +++ b/packages/client/ui-slots/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-slots", "description": "Slot registry pure core: typed ordinary Slots and reusable Component Factories, derived props, Store seats, and renderer installation", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-subagent/package.json b/packages/client/ui-subagent/package.json index 05fa4ab936..fa3ae08ec0 100644 --- a/packages/client/ui-subagent/package.json +++ b/packages/client/ui-subagent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-subagent", "description": "Subagent conversation catalog, continuation routing UI, and '@' reference source", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-theme/package.json b/packages/client/ui-theme/package.json index 9c28ae29dc..6d8979785b 100644 --- a/packages/client/ui-theme/package.json +++ b/packages/client/ui-theme/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-theme", "description": "Theme plugin: Host bootstrap for the pre-plugin palette; DOM-free ThemeRuntime for light/dark/system state; --dsw-* token styles and Appearance settings row", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-tool/README.i18n.yaml b/packages/client/ui-tool/README.i18n.yaml index 01eb372ca9..86d8299c11 100644 --- a/packages/client/ui-tool/README.i18n.yaml +++ b/packages/client/ui-tool/README.i18n.yaml @@ -15,8 +15,8 @@ en: d152484eb41ac6b4 zh: 09388d293f9be9cb /deepseek-ai-dsh-client-ui-tool/use-this-package: - en: 3b9b719ca4e4cc62 - zh: a14b63a16476d684 + en: c6d4e666191521b2 + zh: 2498020e7080e172 /deepseek-ai-dsh-client-ui-tool/use-this-package/registering-a-business-tool-view: en: 2e3c306b13d3bc25 zh: 77127f01763b3976 diff --git a/packages/client/ui-tool/README.md b/packages/client/ui-tool/README.md index d3c98dbeeb..32d394cdaf 100644 --- a/packages/client/ui-tool/README.md +++ b/packages/client/ui-tool/README.md @@ -27,7 +27,7 @@ English | [中文](README.zh.md) Tool calls appear in the conversation as cards: a root call tree with its nested subcalls, each atomic call rendered by its owning view. Every lifecycle state retains the tool's ordinary business glyph; failure and interruption remain explicit through the frozen call/result state, accessible status text, and failure summary. Users can open files or inspect calls through the Host callbacks. A collapsed `web_fetch` row links its http(s) URL, which opens in a new browser tab. -Shared Tool rows and Bash rows retain error and warning colors for failed and stopped summaries, including on hover. Hover darkens only summaries without those states. +Settled foreground commands with a nonzero exit code, a terminating signal, or an explicit missing exit code keep their description in the ordinary gray text and show a neutral capsule with a red dot immediately after it. The capsule displays only the recorded code, signal, or localized missing-code status and stays on the same line; the description truncates with an ellipsis to reserve its space. Running, successful, and background calls omit it. Generic execution errors and stopped summaries retain their error and warning colors, including on hover. Before dispatch, a named model call appears as one non-expandable row with its tool-owned icon and title. A generic row shows `Tool call · `. Read/write/edit show an openable path once `file_path` closes and decodes without errors; write/edit show `NKB` after that path once content arrives, retaining it during execution and after success, before any diff totals. N rounds the decoded input's UTF-16 length up in units of 1024, summing old and new text for edit; it is not file bytes. Error and stopped rows omit the size and diff totals. Command rows show the streamed description. `tool/call` enables the existing call presentation; completing an argument block alone does not start execution. diff --git a/packages/client/ui-tool/README.zh.md b/packages/client/ui-tool/README.zh.md index f898a9eeb1..7f3d0ef79d 100644 --- a/packages/client/ui-tool/README.zh.md +++ b/packages/client/ui-tool/README.zh.md @@ -27,7 +27,7 @@ kind: "package-reference" 工具调用在对话中显示为卡片:一个根调用树带其嵌套子调用,每个原子调用由所属视图渲染。所有生命周期状态都保留工具的普通业务图标;失败与中断仍通过冻结调用/结果状态、无障碍状态文本和失败摘要明确表达。用户可通过宿主回调打开文件或检查调用。折叠的 `web_fetch` 行把其 http(s) URL 显示为链接,在新浏览器标签页中打开。 -共享工具行和 Bash 行的失败、停止摘要在悬停时仍保留错误色和警告色;只有不处于这两种状态的摘要会在悬停时加深。 +已结束的前台命令若返回非零退出码、终止信号,或明确记录为无退出码,描述保持普通灰色,后面紧接带红点的中性胶囊。胶囊仅显示记录的退出码、信号或本地化的“未正常退出”状态,并保持在同一行;描述超出可用宽度时显示省略号,为胶囊预留空间。运行中、成功和后台调用不显示胶囊。通用执行错误与停止摘要仍保留错误色和警告色,悬停时也不改变。 派发前,模型已给出名称的调用显示为不可展开的一行,使用工具自己的图标与标题。通用行显示为 `工具调用 · <工具名>`。read/write/edit 在 `file_path` 闭合且解码无误后显示可打开的路径;write/edit 从内容到达起在该路径后显示 `NKB`,执行中和成功后仍保留,位于差异统计之前。N 按解码后输入的 UTF-16 长度除以 1024 向上取整,edit 合计新旧文本长度;它不是文件字节数。失败和停止的行省略大小与差异统计。命令行显示流入中的描述。`tool/call` 才启用既有调用展示;参数块结束本身不代表开始执行。 diff --git a/packages/client/ui-tool/package.json b/packages/client/ui-tool/package.json index 166a723511..9db958772f 100644 --- a/packages/client/ui-tool/package.json +++ b/packages/client/ui-tool/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-tool", "description": "Client Tool call-tree renderer and keyed per-tool presentation slot", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-tool/src/client/tool/components/ExitCodeBadge.module.css b/packages/client/ui-tool/src/client/tool/components/ExitCodeBadge.module.css new file mode 100644 index 0000000000..48a3e7bfd3 --- /dev/null +++ b/packages/client/ui-tool/src/client/tool/components/ExitCodeBadge.module.css @@ -0,0 +1,14 @@ +.badge { + flex: none; + margin-left: 8px; + gap: 5px; +} + +.dot { + flex: none; + width: 4px; + height: 4px; + border-radius: 50%; + corner-shape: round; + background: var(--dsw-alias-state-error-primary); +} diff --git a/packages/client/ui-tool/src/client/tool/components/ExitCodeBadge.tsx b/packages/client/ui-tool/src/client/tool/components/ExitCodeBadge.tsx new file mode 100644 index 0000000000..0e46d6390e --- /dev/null +++ b/packages/client/ui-tool/src/client/tool/components/ExitCodeBadge.tsx @@ -0,0 +1,17 @@ +/** Inline exit status for a settled command. */ +import { Tag } from '@deepseek-ai/dsh-client-ui-primitives' +import css from './ExitCodeBadge.module.css' + +/** + * Render a neutral capsule with a failure dot and a localized exit status. + * @param props - localized exit code or terminating signal label. + * @returns the exit status capsule. + */ +export function ExitCodeBadge({ label }: { label: string }) { + return ( + + + {label} + + ) +} diff --git a/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx b/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx index 4b4674d452..694ce16446 100644 --- a/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx +++ b/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx @@ -13,7 +13,7 @@ import { CHAT_READ_MAX_LINES, type ReadCardModel } from '../models/read-card-mod import type { ImageCardModel } from '../models/image-card-model.ts' import { CHAT_SEARCH_MAX_LINES, type SearchCardModel } from '../models/search-card-model.ts' import { - localizeTerminalCardModel, terminalBlockLabels, type TerminalCardModel, + localizeTerminalCardModel, terminalBlockLabels, terminalExitLabel, type TerminalCardModel, } from '../models/terminal-card-model.ts' import { codeToolbarLabels, diffBlockLabels, readBlockLabels, searchBlockLabels, webBlockLabels, @@ -25,6 +25,7 @@ import { import type { WebCardModelProps } from '../models/web-card-model.ts' import { AskQuestionCard } from './AskQuestionCard.tsx' import { ToolDetails, type ToolDetailsModel } from './ToolDetails.tsx' +import { ExitCodeBadge } from './ExitCodeBadge.tsx' import css from './ToolRow.module.css' export interface ToolRowProps { @@ -50,7 +51,10 @@ export interface ToolRowProps { output?: string | null | undefined /** Ask-user transcript card; card fields are mutually exclusive and replace text sections. */ askQuestion?: AskQuestionCardModel | null | undefined - /** Error first line shown as the collapsed summary on an error row; null/absent = keep `summary`. */ + /** + * First error line for rows without an exit status capsule; null/absent keeps + * the ordinary summary. Rows with an exit capsule retain the command description. + */ errorSummary?: string | null | undefined /** Terminal card; card fields are mutually exclusive and replace text sections. */ terminal?: TerminalCardModel | null | undefined @@ -176,10 +180,9 @@ export const ToolRow = memo(function ToolRow({ const status = stateStatus(state, t) const running = state === 'running' || state === 'preparing' const normalSummary = terminalBody?.description ?? (open ? detailsBody?.expandedSummary ?? summary : summary) - // A failure keeps its first result line when available and otherwise turns - // the ordinary summary red. An interruption turns the tool-owned summary - // amber while retaining the business icon and hidden state announcement. - const failureLine = state === 'error' ? errorSummary ?? normalSummary : null + const exitLabel = terminalExitLabel(terminal, t) + // Generic failures retain their first result line; command exits keep their description. + const failureLine = state === 'error' && exitLabel === null ? errorSummary ?? normalSummary : null const summaryText = failureLine ?? normalSummary // Diff totals remain visible after the optional summary suffix. const diffStat = useMemo(() => diffBody === null ? null : diffTotals(diffBody.card.diffs), [diffBody]) @@ -210,7 +213,7 @@ export const ToolRow = memo(function ToolRow({ its title shows no trailing dot). */ <> - {openFile !== undefined ? ( + {exitLabel === null && openFile !== undefined ? ( - ) : linkHref !== undefined ? ( + ) : exitLabel === null && linkHref !== undefined ? ( {summaryText} )} + {exitLabel !== null && } {suffix !== null && ( {suffix} )} @@ -250,7 +254,7 @@ export const ToolRow = memo(function ToolRow({ )} - ), [summaryLinkKeyDown, linkHref, openFile, state, suffix, totals, summaryText]) + ), [summaryLinkKeyDown, linkHref, openFile, state, suffix, totals, summaryText, exitLabel]) const expandedContent = useMemo(() => open ? (
{askQuestionBody !== null diff --git a/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts b/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts index 6331f89592..468706c8e0 100644 --- a/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts +++ b/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts @@ -86,11 +86,10 @@ export function localizeTerminalCardModel( } /** - * True when a settled terminal card reports a failing exit — a non-zero code - * or a terminating signal. The bash tool settles a failing command as a - * completed call (`isError` stays false: the exit status is result data), so - * this is the collapsed row's only failure signal; without it the red exit - * pill would be visible only after expanding the card. + * True when a settled terminal card reports a failing exit — a non-zero code, + * an explicitly missing exit code, or a terminating signal. The bash tool settles + * a failing command as a completed call (`isError` stays false: the exit status is result data), so + * the row retains that failure state alongside its exit capsule. * @param model - a derived terminal card. * @returns whether the card's exit status is a failure. */ @@ -99,6 +98,20 @@ export function terminalFailed(model: TerminalCardModel): boolean { return running !== true && ((exitCode !== undefined && exitCode !== 0) || signal !== undefined) } +/** + * Localize a settled foreground command's exit status without inventing a code. + * @param model - terminal card, absent for generic execution errors. + * @param t - conversation locale. + * @returns the capsule label, or null for running, background, or successful calls. + */ +export function terminalExitLabel(model: TerminalCardModel | null | undefined, t: TranslateNS<'conversation'>): string | null { + if (model == null || model.card.background || !terminalFailed(model)) return null + const { exitCode, signal } = model.card + if (signal !== undefined) return t('terminal.signal', { signal }) + if (exitCode == null) return t('terminal.noExitCode') + return t('terminal.exitCode', { code: exitCode }) +} + /** * Resolve a shell call's workdir for display: an absolute path is used as-is, * a relative one joins under the session workspace, and an omitted one is the @@ -277,11 +290,11 @@ function terminalSendCall(name: string, args: Record): Terminal * @param text - rendered shell result text. * @returns output with a trailing exit-code or signal marker extracted. */ -function parseExitStatus(text: string): { output: string; exitCode?: number; signal?: string } { +function parseExitStatus(text: string): { output: string; exitCode?: number | null; signal?: string } { const signal = /\n\[killed by signal: ([^\]\n]+)\]$/.exec(text) if (signal?.[1] !== undefined) return { output: text.slice(0, signal.index), signal: signal[1] } - const exit = /\n\[exit code: (\d+)\]$/.exec(text) - if (exit?.[1] !== undefined) return { output: text.slice(0, exit.index), exitCode: Number(exit[1]) } + const exit = /\n\[exit code: (-?\d+|null)\]$/.exec(text) + if (exit?.[1] !== undefined) return { output: text.slice(0, exit.index), exitCode: exit[1] === 'null' ? null : Number(exit[1]) } return { output: text, exitCode: 0 } } diff --git a/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx b/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx index 52ffd1555a..22f239a308 100644 --- a/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx +++ b/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx @@ -15,9 +15,11 @@ import { terminalBlockLabels, terminalCardModel, terminalFailed, + terminalExitLabel, } from '../models/terminal-card-model.ts' import { formatToolBody, toolRowModel, type ToolRowState } from '../models/tool-call-model.ts' import { CONVERSATION_NS as NS } from '../../locale.ts' +import { ExitCodeBadge } from '../components/ExitCodeBadge.tsx' import css from './bash-sample.module.css' type BashRowProps = ToolCallViewProps & PropsLocale<'conversation'> @@ -49,8 +51,7 @@ export const BashRow = memo(function BashRow({ toolName, block, sessionId, useSe const terminalModel = useMemo(() => terminalCardModel(block, cwd), [block, cwd]) const terminal = useMemo(() => terminalModel === null ? null : localizeTerminalCardModel(terminalModel, t), [terminalModel, t]) const labels = useMemo(() => terminalBlockLabels(t), [t]) - // A failing exit status is the terminal card's own error signal (the call - // itself settles isError:false), surfaced through the row's error summary. + // Exit failures are result data even when the tool call itself succeeds. const state = model.state === 'ok' && terminalModel !== null && terminalFailed(terminalModel) ? 'error' : model.state @@ -71,7 +72,8 @@ export const BashRow = memo(function BashRow({ toolName, block, sessionId, useSe [genericBody, model.bodyRaw, model.variant, open], ) const normalSummary = terminal?.description ?? model.summary - const settlementLine = state === 'error' + const exitLabel = terminalExitLabel(terminalModel, t) + const settlementLine = exitLabel !== null ? null : state === 'error' ? model.errorSummary ?? normalSummary : state === 'stopped' ? t('bash.stopped') : null const running = state === 'running' || state === 'preparing' @@ -111,11 +113,12 @@ export const BashRow = memo(function BashRow({ toolName, block, sessionId, useSe {settlementLine ?? normalSummary} + {exitLabel !== null && }
{open && ( diff --git a/packages/client/ui-tool/tests/terminal-card.client.spec.tsx b/packages/client/ui-tool/tests/terminal-card.client.spec.tsx index 24117339cd..0ef6514c4e 100644 --- a/packages/client/ui-tool/tests/terminal-card.client.spec.tsx +++ b/packages/client/ui-tool/tests/terminal-card.client.spec.tsx @@ -12,7 +12,7 @@ import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime' import { en as commonEn } from '@deepseek-ai/dsh-client-locale/src/locales/en.ts' import { zh as commonZh } from '@deepseek-ai/dsh-client-locale/src/locales/zh.ts' import { - localizeTerminalCardModel, terminalCardModel, terminalFailed, + localizeTerminalCardModel, terminalCardModel, terminalFailed, terminalExitLabel, } from '../src/client/tool/models/terminal-card-model.ts' import { GenericToolCard, type GenericToolCardProps } from '../src/client/tool/toolviews/GenericToolCard.tsx' import { BashRow } from '../src/client/tool/toolviews/bash-sample.tsx' @@ -67,6 +67,40 @@ const settled = (over?: Partial): ToolResultNode => { } describe('terminalCardModel', () => { + it.each([1, -1])('preserves signed exit code %s in the localized capsule', (code) => { + const card = terminalCardModel(settled({ content: [{ type: 'text', text: `failed\n[exit code: ${code}]` }] })) + expect(card?.card).toMatchObject({ output: 'failed', exitCode: code }) + expect(terminalExitLabel(card, enT)).toBe(`exit code ${code}`) + expect(terminalExitLabel(card, t)).toBe(`退出码 ${code}`) + }) + + it('omits exit capsules without a settled foreground failure', () => { + expect(terminalExitLabel(null, enT)).toBeNull() + expect(terminalExitLabel(undefined, enT)).toBeNull() + expect(terminalExitLabel(terminalCardModel(running()), enT)).toBeNull() + expect(terminalExitLabel(terminalCardModel(settled()), enT)).toBeNull() + expect(terminalExitLabel(terminalCardModel(settled({ + call: { name: 'terminal_send', argsRaw: JSON.stringify({ sessionId: 'term-1', text: 'hello' }) }, + })), enT)).toBeNull() + expect(terminalExitLabel(terminalCardModel(settled({ call: { name: 'bash', argsRaw: shellArgs({ run_in_background: true }) } })), enT)).toBeNull() + }) + + it('names a recorded signal without substituting an exit code', () => { + const model = terminalCardModel(settled({ content: [{ type: 'text', text: 'gone\n[killed by signal: SIGTERM]' }] })) + expect(terminalExitLabel(model, enT)).toBe('signal SIGTERM') + }) + + it.each(['bash', 'pwsh'])('preserves a %s timeout without an exit code', (name) => { + const model = terminalCardModel(settled({ + call: { name, argsRaw: ARGS }, + content: [{ type: 'text', text: '(no output)\n[timed out after 100ms]\n[exit code: null]' }], + }))! + expect(model.card).toMatchObject({ output: '(no output)\n[timed out after 100ms]', exitCode: null }) + expect(terminalFailed(model)).toBe(true) + expect(terminalExitLabel(model, enT)).toBe('no exit code') + expect(terminalExitLabel(model, t)).toBe('未正常退出') + }) + it('derives a running standard-shell card from raw arguments', () => { expect(terminalCardModel(running({ argsRaw: shellArgs({ workdir: '/projects/app' }) }))).toEqual({ copy: { kind: 'shell', command: 'ls -la', description: 'List files' }, @@ -455,6 +489,9 @@ describe('chat row terminal body', () => { content: [{ type: 'text', text: 'boom\n[exit code: 2]' }], }))} />) expect(view.container.querySelector('[data-state]')?.getAttribute('data-state')).toBe('error') + expect(view.getByText('退出码 2')).toBeTruthy() + expect(view.getByText('退出码 2').previousElementSibling?.textContent).toBe('List files') + expect(view.container.querySelector('[class*="_errorSummary_"]')).toBeNull() }) }) @@ -512,7 +549,12 @@ describe('BashRow terminal card', () => { content: [{ type: 'text', text: 'boom\n[exit code: 2]' }], }))} />) expect(view.container.querySelector('[data-variant="bash"]')?.getAttribute('data-state')).toBe('error') - expect(view.container.querySelector('[class*="_errorSummary_"]')?.textContent).toBe('List files') + expect(view.container.querySelector('[class*="_errorSummary_"]')).toBeNull() + expect(view.getByText('退出码 2')).toBeTruthy() + expect(view.getByText('List files')).toBeTruthy() + fireEvent.keyDown(view.getByRole('button'), { key: 'Enter' }) + expect(view.getAllByText('退出码 2')).toHaveLength(2) + expect(view.getByText('boom', RAW)).toBeTruthy() }) it('shows the call description as the terminal summary', () => { diff --git a/packages/client/ui-trajectory/package.json b/packages/client/ui-trajectory/package.json index 7d0a38c735..a46d92dd8d 100644 --- a/packages/client/ui-trajectory/package.json +++ b/packages/client/ui-trajectory/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-trajectory", "description": "Trajectory event ledger with an interactive timing overview: pure-consumer plugin registering into the conversation ViewMap (no service)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-user-questions/package.json b/packages/client/ui-user-questions/package.json index 1f95066313..130f136298 100644 --- a/packages/client/ui-user-questions/package.json +++ b/packages/client/ui-user-questions/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-user-questions", "description": "Web ask_user_question composer takeover and plan-review presentation UI", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-workflow-run/package.json b/packages/client/ui-workflow-run/package.json index e5d2c698a7..5cadc49d54 100644 --- a/packages/client/ui-workflow-run/package.json +++ b/packages/client/ui-workflow-run/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-workflow-run", "description": "Durable workflow-run Conversation Node and nested member disclosure for dsh web", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/ui-workspace/package.json b/packages/client/ui-workspace/package.json index 43919e3228..412ad30690 100644 --- a/packages/client/ui-workspace/package.json +++ b/packages/client/ui-workspace/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-workspace", "description": "Workspace picker plugin: one WorkspacePicker registered into the sidebar and empty-state workspace slots", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/client/web/package.json b/packages/client/web/package.json index a07af04e65..eac9e5b8e3 100644 --- a/packages/client/web/package.json +++ b/packages/client/web/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-web", "description": "Web boot kernel: static module table, Cordis loader, framework-free boot page, and UI-renderer handoff", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/compaction/command-compact/package.json b/packages/compaction/command-compact/package.json index 809d2daf04..157e6ffb83 100644 --- a/packages/compaction/command-compact/package.json +++ b/packages/compaction/command-compact/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-command-compact", "description": "Human-facing slash command for explicit session compaction", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/compaction/compaction-basic/package.json b/packages/compaction/compaction-basic/package.json index 0d47fac0fb..b361f747cf 100644 --- a/packages/compaction/compaction-basic/package.json +++ b/packages/compaction/compaction-basic/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-compaction-basic", "description": "Token-meter-driven compaction policy and LLM summarization backend for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/compaction/compaction-image-offload/package.json b/packages/compaction/compaction-image-offload/package.json index 3678d57b68..87ba09ad7c 100644 --- a/packages/compaction/compaction-image-offload/package.json +++ b/packages/compaction/compaction-image-offload/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-compaction-image-offload", "description": "Durable image offload for image-capable routes: replace over-budget request images with placeholders and retry", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/compaction/compaction-tool-result-pruner/package.json b/packages/compaction/compaction-tool-result-pruner/package.json index d499df2e62..6fd3cb6876 100644 --- a/packages/compaction/compaction-tool-result-pruner/package.json +++ b/packages/compaction/compaction-tool-result-pruner/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-compaction-tool-result-pruner", "description": "Replay-safe model-free head/middle/tail pruning for tool-result surface nodes", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/compaction/compaction/package.json b/packages/compaction/compaction/package.json index f7019e49ac..5938c69959 100644 --- a/packages/compaction/compaction/package.json +++ b/packages/compaction/compaction/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-compaction", "description": "Abstract compaction service seam (ctx.compaction) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/computer-use/computer-use/package.json b/packages/computer-use/computer-use/package.json index 65733df35e..41ea4fcca4 100644 --- a/packages/computer-use/computer-use/package.json +++ b/packages/computer-use/computer-use/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-computer-use", "description": "Exclusive named computer-use provider registration", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/agent-instructions/package.json b/packages/context/agent-instructions/package.json index dac4a52208..c451b67521 100644 --- a/packages/context/agent-instructions/package.json +++ b/packages/context/agent-instructions/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-instructions", "description": "Workspace context loader for AGENTS.md/CLAUDE.md instruction files", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/file-reference-local/package.json b/packages/context/file-reference-local/package.json index a58d293fbe..e0a0cd0843 100644 --- a/packages/context/file-reference-local/package.json +++ b/packages/context/file-reference-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-file-reference-local", "description": "Local-filesystem ctx.fileReferences provider with bounded fuzzy indexes", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/file-reference/package.json b/packages/context/file-reference/package.json index c1dfca3a28..f620b96957 100644 --- a/packages/context/file-reference/package.json +++ b/packages/context/file-reference/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-file-reference", "description": "File-reference discovery contract and shared @file grammar", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/harness-dev-context/package.json b/packages/context/harness-dev-context/package.json index b37691f28a..265570855b 100644 --- a/packages/context/harness-dev-context/package.json +++ b/packages/context/harness-dev-context/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-harness-dev-context", "description": "Model context naming the DeepSeek Harness checkout and Web GUI URL without letting either change the system prompt", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/session-reference/package.json b/packages/context/session-reference/package.json index 13a4de7ed9..a63ae81927 100644 --- a/packages/context/session-reference/package.json +++ b/packages/context/session-reference/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-reference", "description": "Cross-session snapshot references and durable untrusted model context (ctx.sessionReferenceResolver)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/time-context/package.json b/packages/context/time-context/package.json index 198bd422f2..a0411f7569 100644 --- a/packages/context/time-context/package.json +++ b/packages/context/time-context/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-time-context", "description": "Durable per-step context with the current time and elapsed time", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/context/tmux-context/package.json b/packages/context/tmux-context/package.json index f6b1f9d91b..edad348b00 100644 --- a/packages/context/tmux-context/package.json +++ b/packages/context/tmux-context/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tmux-context", "description": "Opt-in durable per-step context with this agent's tmux pane and window location", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/agent-default-model/package.json b/packages/core/agent-default-model/package.json index de588ac66b..400182ddcc 100644 --- a/packages/core/agent-default-model/package.json +++ b/packages/core/agent-default-model/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-default-model", "description": "Default model selection shared by Agent entry points", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/agent-loop/package.json b/packages/core/agent-loop/package.json index 46543087d2..6ddc5130ca 100644 --- a/packages/core/agent-loop/package.json +++ b/packages/core/agent-loop/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-loop", "description": "The concrete agent loop plugin for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/agent-tool-presentation/package.json b/packages/core/agent-tool-presentation/package.json index 96753990cd..ddcd12bf32 100644 --- a/packages/core/agent-tool-presentation/package.json +++ b/packages/core/agent-tool-presentation/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-tool-presentation", "description": "Agent-plane presentation selector: composes one agent's tools as PTC mode or native tool calls", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/agent/package.json b/packages/core/agent/package.json index 808a72743f..33fc0e74d5 100644 --- a/packages/core/agent/package.json +++ b/packages/core/agent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent", "description": "Agent interface, registry, initiator scope, and event vocabulary for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/scope/package.json b/packages/core/scope/package.json index 2d7459f53b..e315ecb280 100644 --- a/packages/core/scope/package.json +++ b/packages/core/scope/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-scope", "description": "Scoped-context registration primitive (scope tags, scope-filtered event dispatch) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/session/package.json b/packages/core/session/package.json index 5db6bd3efc..a925ee3e40 100644 --- a/packages/core/session/package.json +++ b/packages/core/session/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session", "description": "Event-sourced session store for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/system-prompt/package.json b/packages/core/system-prompt/package.json index bc9ac7163a..cf09031ac8 100644 --- a/packages/core/system-prompt/package.json +++ b/packages/core/system-prompt/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-system-prompt", "description": "System prompt assembly registry for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/core/tools/package.json b/packages/core/tools/package.json index b5a0509762..c896879886 100644 --- a/packages/core/tools/package.json +++ b/packages/core/tools/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tools", "description": "Tool registry and execution pipeline for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/credentials/authorization/package.json b/packages/credentials/authorization/package.json index 1b40601e0b..cbf9a5221c 100644 --- a/packages/credentials/authorization/package.json +++ b/packages/credentials/authorization/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-authorization", "description": "Authorization seam (ctx.authorization): plugin-owned flows that obtain a credential through a conversation with the human", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/credentials/credentials-local/package.json b/packages/credentials/credentials-local/package.json index 7ccceb46a7..ca6c84e366 100644 --- a/packages/credentials/credentials-local/package.json +++ b/packages/credentials/credentials-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-credentials-local", "description": "File-backed credentials provider ($DSH_HOME/.env under the live process environment) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/credentials/credentials/package.json b/packages/credentials/credentials/package.json index 0a8d34ab5c..caaa90a0bb 100644 --- a/packages/credentials/credentials/package.json +++ b/packages/credentials/credentials/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-credentials", "description": "Abstract credential seam (ctx.credentials): settings carry references to secrets, providers own the values", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/credentials/deepseek-account-platform/package.json b/packages/credentials/deepseek-account-platform/package.json index a097a421e1..c3fd5ad374 100644 --- a/packages/credentials/deepseek-account-platform/package.json +++ b/packages/credentials/deepseek-account-platform/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-deepseek-account-platform", "description": "Authorize DeepSeek accounts through browser PKCE", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/credentials/deepseek-account/package.json b/packages/credentials/deepseek-account/package.json index 7f24f93938..97c66da83c 100644 --- a/packages/credentials/deepseek-account/package.json +++ b/packages/credentials/deepseek-account/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-deepseek-account", "description": "Read account state and resolve official API credentials", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/deliverables/tool-present/package.json b/packages/deliverables/tool-present/package.json index 3dc52e418e..8acb591637 100644 --- a/packages/deliverables/tool-present/package.json +++ b/packages/deliverables/tool-present/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-present", "description": "Explicit workspace file delivery declarations for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/deliverables/workspace-changes/package.json b/packages/deliverables/workspace-changes/package.json index 6022f3ccc2..0e35903786 100644 --- a/packages/deliverables/workspace-changes/package.json +++ b/packages/deliverables/workspace-changes/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-workspace-changes", "description": "Per-turn workspace file changes recorded from git working-tree snapshots and whole-file captures, with per-file comparisons, for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/document/office-to-pdf/package.json b/packages/document/office-to-pdf/package.json index 2b5b4982ea..9d1894d131 100644 --- a/packages/document/office-to-pdf/package.json +++ b/packages/document/office-to-pdf/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-office-to-pdf", "description": "Shared Office-to-PDF conversion with bounded queues and caching", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/agent-team-profile/README.i18n.yaml b/packages/experimental/agent-team-profile/README.i18n.yaml index 26c1a08b06..46c6685a3f 100644 --- a/packages/experimental/agent-team-profile/README.i18n.yaml +++ b/packages/experimental/agent-team-profile/README.i18n.yaml @@ -18,8 +18,8 @@ en: bbff250eba0f1b89 zh: 0a618f05cc88ee2b /deepseek-ai-dsh-experimental-agent-team-profile/use-this-package/install-into-a-profile: - en: a8a37bd633498df0 - zh: c72a802f909e97df + en: 22e022969e8de91b + zh: 5c657e55a8d6b168 /deepseek-ai-dsh-experimental-agent-team-profile/use-this-package/what-you-get: en: 9494ef152ee1ee43 zh: e9c5129490a1fae1 diff --git a/packages/experimental/agent-team-profile/README.md b/packages/experimental/agent-team-profile/README.md index ce32c31cdc..9fcce49bde 100644 --- a/packages/experimental/agent-team-profile/README.md +++ b/packages/experimental/agent-team-profile/README.md @@ -42,7 +42,7 @@ Enable Agent Teams on the Web or Desktop Plugins page to activate both tools and dsh plugin --profile web add @deepseek-ai/dsh-experimental-agent-team-profile ``` -In an existing profile’s `package.json`, keep `@deepseek-ai/dsh-experimental-agent-team-profile` in `dsh.profile.bundles` and remove the separate `@deepseek-ai/dsh-experimental-agent-team-web-profile` entry. User patches targeting `ui-agent-team` still apply. A user patch that sets a removed Team key keeps that Team row from activating; the [upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md) lists the removed keys and tools. +In an existing profile’s `package.json`, keep `@deepseek-ai/dsh-experimental-agent-team-profile` in `dsh.profile.bundles` and remove the separate `@deepseek-ai/dsh-experimental-agent-team-web-profile` entry. User patches targeting `ui-agent-team` still apply. A user patch that sets a removed Team key keeps that Team row from activating; the [upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md) lists the removed keys and tools. ### What you get diff --git a/packages/experimental/agent-team-profile/README.zh.md b/packages/experimental/agent-team-profile/README.zh.md index 32068d8739..69585e9d8d 100644 --- a/packages/experimental/agent-team-profile/README.zh.md +++ b/packages/experimental/agent-team-profile/README.zh.md @@ -42,7 +42,7 @@ profile 必须已经包含 `@deepseek-ai/dsh-base`,本层依赖其中的 Subag dsh plugin --profile web add @deepseek-ai/dsh-experimental-agent-team-profile ``` -已有 profile 的 `package.json` 中,`dsh.profile.bundles` 应只保留 `@deepseek-ai/dsh-experimental-agent-team-profile`,删除独立的 `@deepseek-ai/dsh-experimental-agent-team-web-profile` 条目。用户 patch 中的 `ui-agent-team` 配置仍然有效。设置已移除 Team 键的用户 patch 会使对应 Team 配置行无法激活;[升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md)列出已移除的键与工具。 +已有 profile 的 `package.json` 中,`dsh.profile.bundles` 应只保留 `@deepseek-ai/dsh-experimental-agent-team-profile`,删除独立的 `@deepseek-ai/dsh-experimental-agent-team-web-profile` 条目。用户 patch 中的 `ui-agent-team` 配置仍然有效。设置已移除 Team 键的用户 patch 会使对应 Team 配置行无法激活;[升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md)列出已移除的键与工具。 ### 获得的功能 diff --git a/packages/experimental/agent-team-profile/package.json b/packages/experimental/agent-team-profile/package.json index 7f8394650f..45fd9af0dc 100644 --- a/packages/experimental/agent-team-profile/package.json +++ b/packages/experimental/agent-team-profile/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-agent-team-profile", "description": "Agent Teams collaboration, tools, and Web UI in one experimental bundle", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/agent-team/README.i18n.yaml b/packages/experimental/agent-team/README.i18n.yaml index 8a665b241e..7fd3c2a41a 100644 --- a/packages/experimental/agent-team/README.i18n.yaml +++ b/packages/experimental/agent-team/README.i18n.yaml @@ -21,8 +21,8 @@ en: 12d03bedc2a7be57 zh: 27fc0dc8fef2340b /deepseek-ai-dsh-experimental-agent-team/use-this-package/smallest-working-setup: - en: 037e98afeb998730 - zh: 224b568c2d54cd92 + en: 0eab809f5f1af167 + zh: 21a5960927603bf2 /deepseek-ai-dsh-experimental-agent-team/use-this-package/teammates: en: 6946c12e1482c65c zh: 9320041928c49519 @@ -72,8 +72,8 @@ en: 35f9e2968ab635f5 zh: f599a75e88228d83 /deepseek-ai-dsh-experimental-agent-team/further-exploration: - en: 4ac0e25391358fd3 - zh: bd0c9414fbddf654 + en: bddcdd7f9e1da560 + zh: 442039edb8ee664e /deepseek-ai-dsh-experimental-agent-team/model-experience: en: 215e7ba838619b7b zh: a311da3843709f90 diff --git a/packages/experimental/agent-team/README.md b/packages/experimental/agent-team/README.md index 045b7ea5ff..8cdb8d97c5 100644 --- a/packages/experimental/agent-team/README.md +++ b/packages/experimental/agent-team/README.md @@ -49,7 +49,7 @@ For example, ask the model to start a subagent to review the diff and track its |---|---|---| | `maxTasks` | `256` | Maximum non-deleted tasks on the board | -The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-agent-team) is the exhaustive source for every accepted field and its JSDoc. A row that sets `maxMembers`, `maxMessageBytes`, or `disposalTimeoutMs` does not activate, and dsh names the key in its startup warning; the [upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md) lists the replacements. +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-agent-team) is the exhaustive source for every accepted field and its JSDoc. A row that sets `maxMembers`, `maxMessageBytes`, or `disposalTimeoutMs` does not activate, and dsh names the key in its startup warning; the [upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md) lists the replacements. ### Teammates @@ -161,7 +161,7 @@ Read these pages when the package-level contract is not enough. They move from t - [tool-agent-team package](../tool-agent-team/README.md) — the task and wait tools, policy, and identity every member receives. - [Subagent runtime](../../subagent/subagent/README.md) — continuable children, messaging, interruption, and lifecycle that teammates use. - [Subagent teammates Agent Note](../../../.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.md) — why teammates are subagents and what Team keeps. -- [Upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md) — replacements for the removed Team tools, service methods, and configuration keys. +- [Upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md) — replacements for the removed Team tools, service methods, and configuration keys. - [Experimental package reference](../AGENTS.md) — placement, publication, and dependency isolation. ----- diff --git a/packages/experimental/agent-team/README.zh.md b/packages/experimental/agent-team/README.zh.md index 69e9bfb875..a5b8ddacfb 100644 --- a/packages/experimental/agent-team/README.zh.md +++ b/packages/experimental/agent-team/README.zh.md @@ -49,7 +49,7 @@ kind: "package-reference" |---|---|---| | `maxTasks` | `256` | 任务板上最多的未删除任务数 | -生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-agent-team)是每个受支持字段及其 JSDoc 的穷尽式真源。设置 `maxMembers`、`maxMessageBytes` 或 `disposalTimeoutMs` 的配置行不会激活,dsh 会在启动警告中指出该键;[升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md)列出替代方式。 +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-agent-team)是每个受支持字段及其 JSDoc 的穷尽式真源。设置 `maxMembers`、`maxMessageBytes` 或 `disposalTimeoutMs` 的配置行不会激活,dsh 会在启动警告中指出该键;[升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md)列出替代方式。 ### Teammate @@ -161,7 +161,7 @@ Mailbox 投影与 checkpoint 准入保留本地声明的校验器之外获准内 - [tool-agent-team 包](../tool-agent-team/README.zh.md)——每个成员获得的任务与等待工具、策略和身份。 - [Subagent 运行时](../../subagent/subagent/README.zh.md)——teammate 使用的可继续子级、消息、中断与生命周期。 - [subagent teammate Agent Note](../../../.agents/notes/implemented/architecture/2026-10-15-agent-teams-as-subagents.zh.md)——为何 teammate 是subagent,以及 Team 保留了什么。 -- [升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md)——已移除的 Team 工具、服务方法与配置键的替代方式。 +- [升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md)——已移除的 Team 工具、服务方法与配置键的替代方式。 - [实验包参考](../AGENTS.md)——位置、公开发布与依赖隔离。 ----- diff --git a/packages/experimental/agent-team/package.json b/packages/experimental/agent-team/package.json index 9dadfae197..7fa9a3d399 100644 --- a/packages/experimental/agent-team/package.json +++ b/packages/experimental/agent-team/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-agent-team", "description": "Implicit-root Agent Teams roster of subagents, shared task DAG, and change waits", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/api-speech-to-text/package.json b/packages/experimental/api-speech-to-text/package.json index ad08db7e99..4df0b02f21 100644 --- a/packages/experimental/api-speech-to-text/package.json +++ b/packages/experimental/api-speech-to-text/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-api-speech-to-text", "description": "Authenticated experimental speech transcription for browser clients", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/auto-review/package.json b/packages/experimental/auto-review/package.json index da0438227a..38916cdfd3 100644 --- a/packages/experimental/auto-review/package.json +++ b/packages/experimental/auto-review/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-auto-review", "description": "Per-tool LLM authorization review for the DeepSeek Harness Auto permission preset", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "repository": { "type": "git", diff --git a/packages/experimental/badge-skill-bundle/package.json b/packages/experimental/badge-skill-bundle/package.json index 044386a258..92dc3b13de 100644 --- a/packages/experimental/badge-skill-bundle/package.json +++ b/packages/experimental/badge-skill-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-badge-skill-bundle", "description": "Let agents add a “powered by dsh” badge to documents, pull requests, and other outputs.", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/browser-use-chrome-devtools-mcp/package.json b/packages/experimental/browser-use-chrome-devtools-mcp/package.json index 8173741b1a..fa1ab7375c 100644 --- a/packages/experimental/browser-use-chrome-devtools-mcp/package.json +++ b/packages/experimental/browser-use-chrome-devtools-mcp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-browser-use-chrome-devtools-mcp", "description": "Experimental per-Session Chromium browser tools through chrome-devtools-mcp", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/browser-use-playwright-mcp/package.json b/packages/experimental/browser-use-playwright-mcp/package.json index 982c8f602d..ecaa318f6f 100644 --- a/packages/experimental/browser-use-playwright-mcp/package.json +++ b/packages/experimental/browser-use-playwright-mcp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-browser-use-playwright-mcp", "description": "Experimental per-Session Chromium browser tools through @playwright/mcp", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/browser-use-runtime/package.json b/packages/experimental/browser-use-runtime/package.json index c54f1ab96c..9715e09ddd 100644 --- a/packages/experimental/browser-use-runtime/package.json +++ b/packages/experimental/browser-use-runtime/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-browser-use-runtime", "description": "Session-owned browser resource lifecycles and MCP integration for experimental providers", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/browser-use-stagehand-native/package.json b/packages/experimental/browser-use-stagehand-native/package.json index 51164b5675..a30099c51a 100644 --- a/packages/experimental/browser-use-stagehand-native/package.json +++ b/packages/experimental/browser-use-stagehand-native/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-browser-use-stagehand-native", "description": "Experimental Stagehand browser tools with separately configured native models", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/claude-code-mods/package.json b/packages/experimental/claude-code-mods/package.json index 77762adebf..c2b79d69ef 100644 --- a/packages/experimental/claude-code-mods/package.json +++ b/packages/experimental/claude-code-mods/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-claude-code-mods", "description": "Experimental bridge: load Claude Code mods (hooks modules) and run their hook chains on DeepSeek Harness extension points", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/client-ui-agent-team/package.json b/packages/experimental/client-ui-agent-team/package.json index cd52545450..41fbcda6f9 100644 --- a/packages/experimental/client-ui-agent-team/package.json +++ b/packages/experimental/client-ui-agent-team/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-client-ui-agent-team", "description": "Web Agent Teams roster, task board, and teammate navigation", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/client-ui-claude-code-mods/package.json b/packages/experimental/client-ui-claude-code-mods/package.json index e74d539ca5..c8829cad48 100644 --- a/packages/experimental/client-ui-claude-code-mods/package.json +++ b/packages/experimental/client-ui-claude-code-mods/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-client-ui-claude-code-mods", "description": "Web band above the prompt for Claude Code mods: draws each session's mod tree and sends button presses back to the bridge", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/client-ui-cot-translation/package.json b/packages/experimental/client-ui-cot-translation/package.json index 7f2c9da207..414a7ce6e7 100644 --- a/packages/experimental/client-ui-cot-translation/package.json +++ b/packages/experimental/client-ui-cot-translation/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-client-ui-cot-translation", "description": "Optional machine translation of expanded reasoning, preserving original Session text", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/client-ui-voice-input/package.json b/packages/experimental/client-ui-voice-input/package.json index 04e4761ad8..749d8707c9 100644 --- a/packages/experimental/client-ui-voice-input/package.json +++ b/packages/experimental/client-ui-voice-input/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-client-ui-voice-input", "description": "Record speech and insert editable text into the conversation draft", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/computer-use-cua-driver-mcp/package.json b/packages/experimental/computer-use-cua-driver-mcp/package.json index d0ec97477e..08163c4f0d 100644 --- a/packages/experimental/computer-use-cua-driver-mcp/package.json +++ b/packages/experimental/computer-use-cua-driver-mcp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp", "description": "Experimental computer use through an installed Cua Driver MCP executable", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/computer-use-cua-driver-native/package.json b/packages/experimental/computer-use-cua-driver-native/package.json index 715a6a9a01..41cca2b144 100644 --- a/packages/experimental/computer-use-cua-driver-native/package.json +++ b/packages/experimental/computer-use-cua-driver-native/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-computer-use-cua-driver-native", "description": "Experimental computer-use provider embedding the Cua Driver native npm SDK", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/cot-translation-bundle/package.json b/packages/experimental/cot-translation-bundle/package.json index 12e3a54429..59f7de4890 100644 --- a/packages/experimental/cot-translation-bundle/package.json +++ b/packages/experimental/cot-translation-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-cot-translation-bundle", "description": "Experimental machine translation of expanded reasoning", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/cua-macos/package.json b/packages/experimental/cua-macos/package.json index 10d039043a..38517f608b 100644 --- a/packages/experimental/cua-macos/package.json +++ b/packages/experimental/cua-macos/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-cua-macos", "description": "macOS native provider for shared Computer Use application and window objects", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/cua/package.json b/packages/experimental/cua/package.json index ef4e5cdecc..0309152adb 100644 --- a/packages/experimental/cua/package.json +++ b/packages/experimental/cua/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-cua", "description": "Shared Computer Use application objects, authorization, and logged tool execution", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/hook-protocol/package.json b/packages/experimental/hook-protocol/package.json index 50179fb697..8f0c6a346e 100644 --- a/packages/experimental/hook-protocol/package.json +++ b/packages/experimental/hook-protocol/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-hook-protocol", "description": "Shared Claude Code / Codex hook wire protocol: matcher engine, stdin/exit-code/stdout codec, multi-hook merge, and hook/* session events", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/hooks-claude-code/package.json b/packages/experimental/hooks-claude-code/package.json index fb8243a2bc..1e375f0975 100644 --- a/packages/experimental/hooks-claude-code/package.json +++ b/packages/experimental/hooks-claude-code/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-hooks-claude-code", "description": "Bridge plugin: run a Claude Code hooks.json / settings hook config on the DeepSeek Harness interception seams", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/hooks-codex/package.json b/packages/experimental/hooks-codex/package.json index 397dce8afb..9b32aa78c8 100644 --- a/packages/experimental/hooks-codex/package.json +++ b/packages/experimental/hooks-codex/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-hooks-codex", "description": "Bridge plugin: run a Codex hooks.json hook config on the DeepSeek Harness interception seams", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/inspector-profile/package.json b/packages/experimental/inspector-profile/package.json index 9d2ee515cb..80990bea26 100644 --- a/packages/experimental/inspector-profile/package.json +++ b/packages/experimental/inspector-profile/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-inspector-profile", "description": "Optional Web bundle for raw Session logs and Chat node inspection", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/inspector/package.json b/packages/experimental/inspector/package.json index 40f09932b0..971c157b3b 100644 --- a/packages/experimental/inspector/package.json +++ b/packages/experimental/inspector/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-inspector", "description": "Experimental cross-realm CDP hub for Host debugging and Client Runtime inspection", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/plugin-market-bundle/package.json b/packages/experimental/plugin-market-bundle/package.json index e2664e4eb1..1a8f27ae1a 100644 --- a/packages/experimental/plugin-market-bundle/package.json +++ b/packages/experimental/plugin-market-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-plugin-market-bundle", "description": "Experimental custom plugin markets: a Discover tab that browses and installs bundles from plugin market indexes", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/ptc-runtime-python/package.json b/packages/experimental/ptc-runtime-python/package.json index bdd06086ec..2ee4eba2c6 100644 --- a/packages/experimental/ptc-runtime-python/package.json +++ b/packages/experimental/ptc-runtime-python/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-ptc-runtime-python", "description": "CPython subprocess implementation of the DeepSeek Harness PTC execution seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "repository": { "type": "git", "url": "git+https://github.com/deepseek-ai/deepseek-harness.git", diff --git a/packages/experimental/ralph-bundle/package.json b/packages/experimental/ralph-bundle/package.json index 69be852621..be83ff3020 100644 --- a/packages/experimental/ralph-bundle/package.json +++ b/packages/experimental/ralph-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-ralph-bundle", "description": "Run explicitly requested repeated work with fresh agents.", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/session-inspector/package.json b/packages/experimental/session-inspector/package.json index 496e8fca5a..2bc4b14ad5 100644 --- a/packages/experimental/session-inspector/package.json +++ b/packages/experimental/session-inspector/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-session-inspector", "description": "Experimental virtualized Session log and live Chat group/node inspectors", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/session-search/package.json b/packages/experimental/session-search/package.json index 3e4609271e..3c3b1c36b4 100644 --- a/packages/experimental/session-search/package.json +++ b/packages/experimental/session-search/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-session-search", "description": "Give Agents tools to search and read earlier sessions from the current workspace.", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/session-title-all-prompts-llm/package.json b/packages/experimental/session-title-all-prompts-llm/package.json index 29ce7588be..4acd46b6ea 100644 --- a/packages/experimental/session-title-all-prompts-llm/package.json +++ b/packages/experimental/session-title-all-prompts-llm/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-title-all-prompts-llm", "description": "All-user-messages LLM provider plugin for DeepSeek Harness session titles", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/session-titles-bundle/package.json b/packages/experimental/session-titles-bundle/package.json index bc7f4ffde8..c564c6545c 100644 --- a/packages/experimental/session-titles-bundle/package.json +++ b/packages/experimental/session-titles-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-session-titles-bundle", "description": "Refresh session titles as new human prompts arrive.", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/skill-badge/package.json b/packages/experimental/skill-badge/package.json index c88c28751c..eb85d48da2 100644 --- a/packages/experimental/skill-badge/package.json +++ b/packages/experimental/skill-badge/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-skill-badge", "description": "Bundled dsh badge skill provider for DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/speech-to-text-sensevoice/package.json b/packages/experimental/speech-to-text-sensevoice/package.json index 936f0f4a22..8fad41acd7 100644 --- a/packages/experimental/speech-to-text-sensevoice/package.json +++ b/packages/experimental/speech-to-text-sensevoice/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-speech-to-text-sensevoice", "description": "Local SenseVoice ONNX transcription with a managed sherpa-onnx process", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/speech-to-text/package.json b/packages/experimental/speech-to-text/package.json index b9bc8cacf8..b695adf78a 100644 --- a/packages/experimental/speech-to-text/package.json +++ b/packages/experimental/speech-to-text/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-speech-to-text", "description": "Experimental speech recognition with independently selectable providers", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/terminal-bundle/package.json b/packages/experimental/terminal-bundle/package.json index e2339f73b5..8ed98077da 100644 --- a/packages/experimental/terminal-bundle/package.json +++ b/packages/experimental/terminal-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-terminal-bundle", "description": "Keep shell processes alive across tool calls.", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/tool-agent-team/README.i18n.yaml b/packages/experimental/tool-agent-team/README.i18n.yaml index 5fee8accc6..754eb77591 100644 --- a/packages/experimental/tool-agent-team/README.i18n.yaml +++ b/packages/experimental/tool-agent-team/README.i18n.yaml @@ -21,8 +21,8 @@ en: c27d5457e1183675 zh: 3c36d513e6400446 /deepseek-ai-dsh-experimental-tool-agent-team/use-this-package/smallest-working-example: - en: 9e03ffbf10e77ce4 - zh: 7362b415e9d97238 + en: 8dca5e3fa72a3bb7 + zh: 51cdf2eb834dd746 /deepseek-ai-dsh-experimental-tool-agent-team/use-this-package/what-the-model-can-do: en: de03950e2e41ba1f zh: eeb52afda9bd2249 diff --git a/packages/experimental/tool-agent-team/README.md b/packages/experimental/tool-agent-team/README.md index 037b7b36dd..1433e60b78 100644 --- a/packages/experimental/tool-agent-team/README.md +++ b/packages/experimental/tool-agent-team/README.md @@ -40,7 +40,7 @@ The plugin takes no configuration; mount it beside the Team domain as in the [ag name: '@deepseek-ai/dsh-experimental-tool-agent-team' ``` -A row that sets `interrupt`, `freshProvider`, or `forkProvider` does not activate, and dsh names the key in its startup warning. The [upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.md) maps the removed tools and keys to their replacements, and the generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-tool-agent-team) records the empty configuration. +A row that sets `interrupt`, `freshProvider`, or `forkProvider` does not activate, and dsh names the key in its startup warning. The [upgrade guide](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.md) maps the removed tools and keys to their replacements, and the generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-tool-agent-team) records the empty configuration. Ask the Lead to start a subagent to review the diff, add a review task, and wait for its result. The `description` explains the subagent’s work; the returned id addresses it. diff --git a/packages/experimental/tool-agent-team/README.zh.md b/packages/experimental/tool-agent-team/README.zh.md index d52273921a..54eebee8fd 100644 --- a/packages/experimental/tool-agent-team/README.zh.md +++ b/packages/experimental/tool-agent-team/README.zh.md @@ -40,7 +40,7 @@ kind: "package-reference" name: '@deepseek-ai/dsh-experimental-tool-agent-team' ``` -设置 `interrupt`、`freshProvider` 或 `forkProvider` 的配置行不会激活,dsh 会在启动警告中指出该键。[升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.3/agent-team-subagents/guide.zh.md)把已移除的工具与键对应到替代方式,生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-tool-agent-team)记录空配置。 +设置 `interrupt`、`freshProvider` 或 `forkProvider` 的配置行不会激活,dsh 会在启动警告中指出该键。[升级指南](../../../docs/upgrade-guide/v0.2.1-alpha.4/agent-team-subagents/guide.zh.md)把已移除的工具与键对应到替代方式,生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-tool-agent-team)记录空配置。 让 Lead 启动子代理审查差异、创建审查任务并等待结果。`description` 说明子代理的工作;返回的 id 用于寻址。 diff --git a/packages/experimental/tool-agent-team/package.json b/packages/experimental/tool-agent-team/package.json index 72f8a84e8f..9add7daa25 100644 --- a/packages/experimental/tool-agent-team/package.json +++ b/packages/experimental/tool-agent-team/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-tool-agent-team", "description": "Scoped model-facing Agent Teams tools over ctx.agentTeams", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/tool-ralph/package.json b/packages/experimental/tool-ralph/package.json index 38b3de0440..6a2e7e235f 100644 --- a/packages/experimental/tool-ralph/package.json +++ b/packages/experimental/tool-ralph/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-ralph", "description": "Model-facing fresh-agent Ralph loop over the workflow and subagent seams", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/tool-session-query/package.json b/packages/experimental/tool-session-query/package.json index 6fa3f6637e..62590f4cbb 100644 --- a/packages/experimental/tool-session-query/package.json +++ b/packages/experimental/tool-session-query/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-session-query", "description": "Workspace-authorized model-facing session history search, trace, and event read tools", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/tool-terminal/package.json b/packages/experimental/tool-terminal/package.json index 46ff3ce107..76fce409a6 100644 --- a/packages/experimental/tool-terminal/package.json +++ b/packages/experimental/tool-terminal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-terminal", "description": "Six model-facing persistent PTY tools with owner isolation and generic background-job integration", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/tool-worktree/package.json b/packages/experimental/tool-worktree/package.json index a7c6511d10..e63f78d334 100644 --- a/packages/experimental/tool-worktree/package.json +++ b/packages/experimental/tool-worktree/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-tool-worktree", "description": "Model tool that creates and enters a new Git worktree", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/translator/package.json b/packages/experimental/translator/package.json index 11a6565a2f..03650a27cf 100644 --- a/packages/experimental/translator/package.json +++ b/packages/experimental/translator/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-translator", "description": "Machine translation with reusable Session results", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/voice-input-bundle/package.json b/packages/experimental/voice-input-bundle/package.json index 33976641e5..7e6efbd187 100644 --- a/packages/experimental/voice-input-bundle/package.json +++ b/packages/experimental/voice-input-bundle/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-voice-input-bundle", "description": "Experimental voice input with local SenseVoice; downloads its runtime on first use", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "icon": "./icon.svg", "publishConfig": { "access": "public" diff --git a/packages/experimental/webhook-github/package.json b/packages/experimental/webhook-github/package.json index 06e7c715d9..191dca396d 100644 --- a/packages/experimental/webhook-github/package.json +++ b/packages/experimental/webhook-github/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-webhook-github", "description": "Signed GitHub HTTP webhook adapter for the DeepSeek Harness webhook runtime", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/webhook/package.json b/packages/experimental/webhook/package.json index c0613270f7..3621ab6a5c 100644 --- a/packages/experimental/webhook/package.json +++ b/packages/experimental/webhook/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-webhook", "description": "Fire-and-forget webhook rule runtime that creates Workspace-backed DeepSeek Harness Sessions", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/webworker-packer/package.json b/packages/experimental/webworker-packer/package.json index dbdb17d3fa..a1d1336328 100644 --- a/packages/experimental/webworker-packer/package.json +++ b/packages/experimental/webworker-packer/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-webworker-packer", "description": "Build-time packer for the browser runtime's base VFS image and ordered data-overlay archives", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/webworker-runtime/package.json b/packages/experimental/webworker-runtime/package.json index cbb3883ad9..2222ec4a7a 100644 --- a/packages/experimental/webworker-runtime/package.json +++ b/packages/experimental/webworker-runtime/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-webworker-runtime", "description": "Browser-only harness runtime: in-memory VFS, module transform and loader, postMessage tunnel, and the dedicated Web Worker assembly, with the Node-compatibility layer that lets the host tree run unchanged", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/experimental/worktree/package.json b/packages/experimental/worktree/package.json index 31ca444f54..57edad51a5 100644 --- a/packages/experimental/worktree/package.json +++ b/packages/experimental/worktree/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-experimental-worktree", "description": "Creates a named Git worktree and changes the calling Session working directory", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/extensions/cordis-client-runner/package.json b/packages/extensions/cordis-client-runner/package.json index 5c5c70fc92..c3429ae652 100644 --- a/packages/extensions/cordis-client-runner/package.json +++ b/packages/extensions/cordis-client-runner/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-cordis-client-runner", "description": "Browser half of dynamic dual-half plugin packages: event subscription, closure evaluation, guard facade, and loader entries", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/extensions/cordis-host-runner/package.json b/packages/extensions/cordis-host-runner/package.json index 8367bd2c71..b27e6de52b 100644 --- a/packages/extensions/cordis-host-runner/package.json +++ b/packages/extensions/cordis-host-runner/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-cordis-host-runner", "description": "Dynamic package definition registry, host-half sandbox lifecycle, and invoke handler table for model-mounted dual-half packages", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/extensions/tool-cordis/package.json b/packages/extensions/tool-cordis/package.json index e272126221..7a4bfdbf5a 100644 --- a/packages/extensions/tool-cordis/package.json +++ b/packages/extensions/tool-cordis/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-cordis", "description": "Read-only runtime API inspection for Harness plugin development", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/extensions/ui-cordis/package.json b/packages/extensions/ui-cordis/package.json index 71039ffd0d..3119e71056 100644 --- a/packages/extensions/ui-cordis/package.json +++ b/packages/extensions/ui-cordis/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-ui-cordis", "description": "Cordis dynamic-plugin definition card: the keyed cordis_define tool row with its run/stop switch", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/feedback/command-feedback/package.json b/packages/feedback/command-feedback/package.json index 1c194f78c3..98c144267a 100644 --- a/packages/feedback/command-feedback/package.json +++ b/packages/feedback/command-feedback/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-command-feedback", "description": "Log-only session feedback: the record event, the sessionFeedback Host Remote, and the human-facing slash command", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/feedback/message-feedback/package.json b/packages/feedback/message-feedback/package.json index b32bfc6d78..b093d5f89c 100644 --- a/packages/feedback/message-feedback/package.json +++ b/packages/feedback/message-feedback/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-message-feedback", "description": "Canonical Session-log ratings and notes for finalized assistant messages", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/fs-local/package.json b/packages/fs/fs-local/package.json index 8d73bdd2b2..64c3782bf8 100644 --- a/packages/fs/fs-local/package.json +++ b/packages/fs/fs-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-fs-local", "description": "Local-filesystem implementation of the DeepSeek Harness filesystem seam (ctx.fs)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/fs-observation-policy/package.json b/packages/fs/fs-observation-policy/package.json index 8782c71c43..474ca8d4ea 100644 --- a/packages/fs/fs-observation-policy/package.json +++ b/packages/fs/fs-observation-policy/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-fs-observation-policy", "description": "File-context policy plugin for the DeepSeek Harness — observed-state, read-before-edit, and version-guarded write/edit added over the ctx.fs provider seam through the fs/* event gate (no service API)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/fs-sandbox/package.json b/packages/fs/fs-sandbox/package.json index f61022db4a..fab456f4a9 100644 --- a/packages/fs/fs-sandbox/package.json +++ b/packages/fs/fs-sandbox/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-fs-sandbox", "description": "Sandbox-enforcing implementation of the DeepSeek Harness filesystem seam: fences write/edit by the per-call sandbox mode (read-only denies mutation, workspace-write contains it to the workspace + temp roots) while reads pass through", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/fs/package.json b/packages/fs/fs/package.json index c82673f004..0c0fbf774c 100644 --- a/packages/fs/fs/package.json +++ b/packages/fs/fs/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-fs", "description": "Abstract filesystem capability seam (ctx.fs) for the DeepSeek Harness — vocabulary types, the FileSystem service (text IO + optional version-guarded atomic mutations), and the fs/* policy event vocabulary", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/tool-fs-search/package.json b/packages/fs/tool-fs-search/package.json index 5ec831cae9..b6434e890c 100644 --- a/packages/fs/tool-fs-search/package.json +++ b/packages/fs/tool-fs-search/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-fs-search", "description": "Model-facing filesystem discovery tools (glob, grep) backed by the packaged ripgrep binary (@vscode/ripgrep)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/tool-fs/package.json b/packages/fs/tool-fs/package.json index 92631b6382..946c32bb4b 100644 --- a/packages/fs/tool-fs/package.json +++ b/packages/fs/tool-fs/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-fs", "description": "Model-facing filesystem tools (read, write, edit) over the DeepSeek Harness filesystem seam (ctx.fs)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/fs/tool-str-replace-editor/package.json b/packages/fs/tool-str-replace-editor/package.json index 7d0194c3ec..6f7fe48547 100644 --- a/packages/fs/tool-str-replace-editor/package.json +++ b/packages/fs/tool-str-replace-editor/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-str-replace-editor", "description": "Model-facing view, create, literal replace, and line insert tool over the Harness filesystem service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/goal/command-goal/package.json b/packages/goal/command-goal/package.json index b27391de0e..0e0f04b171 100644 --- a/packages/goal/command-goal/package.json +++ b/packages/goal/command-goal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-command-goal", "description": "Human-facing slash command for persisted same-session goals", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/goal/goal-round-driver/package.json b/packages/goal/goal-round-driver/package.json index 6c70f48c52..b51c188750 100644 --- a/packages/goal/goal-round-driver/package.json +++ b/packages/goal/goal-round-driver/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-goal-round-driver", "description": "Race-fenced same-session goal-round driver", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/goal/goal/package.json b/packages/goal/goal/package.json index b30cb4abda..8f1a960439 100644 --- a/packages/goal/goal/package.json +++ b/packages/goal/goal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-goal", "description": "Event-sourced same-session goal state and lifecycle service for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/goal/goal/tests/fixtures/domain/seed-goal.ts b/packages/goal/goal/tests/fixtures/domain/seed-goal.ts index 9776438079..9cc4cd8c2c 100644 --- a/packages/goal/goal/tests/fixtures/domain/seed-goal.ts +++ b/packages/goal/goal/tests/fixtures/domain/seed-goal.ts @@ -1,4 +1,4 @@ -/** Test-only Loader plugin that creates a goal at the first real step edge. */ +/** Test-only Loader plugin that creates a disarmed goal at the first real step edge. */ import type { Context } from '@deepseek-ai/cordis' import type {} from '@deepseek-ai/dsh-goal' @@ -13,6 +13,10 @@ export function apply(ctx: Context): void { objective: 'Prove the composed goal survives in the session log', maxGoalRounds: 7, }) + // An armed goal makes the shipped goal-round-driver start a round at + // idle, racing the test driver's disposal. Activation is process-local, + // so disarming leaves the persisted snapshot unchanged. + ctx.goals.disarm(agent) } return next() }) diff --git a/packages/goal/goal/tests/goal.e2e.ts b/packages/goal/goal/tests/goal.e2e.ts index b3f1f4e3c0..7a902ac5b3 100644 --- a/packages/goal/goal/tests/goal.e2e.ts +++ b/packages/goal/goal/tests/goal.e2e.ts @@ -48,6 +48,7 @@ describe('goal domain through the production headless profile and process', () = }) expect(result['output']).toBeTypeOf('string') expect(result['output']).toContain('CLI tool round trip complete') + expect(events.filter(event => event.type === 'turn/start')).toHaveLength(1) expect(events.filter(event => event.type === 'turn/end')).toHaveLength(1) const changes = events.filter(event => event.type === 'goal/change') diff --git a/packages/goal/tool-goal/package.json b/packages/goal/tool-goal/package.json index 395ab995d8..868c577377 100644 --- a/packages/goal/tool-goal/package.json +++ b/packages/goal/tool-goal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-goal", "description": "Model-facing same-session goal tools with execution-time authority checks", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/guard/repeat-tool-reminder/package.json b/packages/guard/repeat-tool-reminder/package.json index 327bdbaa80..17ab614206 100644 --- a/packages/guard/repeat-tool-reminder/package.json +++ b/packages/guard/repeat-tool-reminder/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-repeat-tool-reminder", "description": "Repeat-tool-call guard plugin: advisory reminders when an agent loops on identical tool calls", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/guard/timeout-policy/package.json b/packages/guard/timeout-policy/package.json index 91d41cc5e6..d4f798f478 100644 --- a/packages/guard/timeout-policy/package.json +++ b/packages/guard/timeout-policy/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-call-timeout-policy", "description": "Tool-call timeout policy: a tools/execute wrapper that arms a per-tool deadline on exec.signal and returns TOOL_TIMEOUT when it wins", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/directory-picker-auto/package.json b/packages/host/directory-picker-auto/package.json index dddb197b1c..7330a1df88 100644 --- a/packages/host/directory-picker-auto/package.json +++ b/packages/host/directory-picker-auto/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-directory-picker-auto", "description": "Adaptive chooser of the directory-picker seam: resolves the host situation at boot and mounts the native or browse backend for the DeepSeek Harness web GUI host", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/directory-picker-browse/package.json b/packages/host/directory-picker-browse/package.json index 1281617b9f..39bd53f702 100644 --- a/packages/host/directory-picker-browse/package.json +++ b/packages/host/directory-picker-browse/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-directory-picker-browse", "description": "In-app browsing backend of the directory-picker seam (listing/creation primitives over the host filesystem)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/directory-picker-native/package.json b/packages/host/directory-picker-native/package.json index c521202209..7404e7f35e 100644 --- a/packages/host/directory-picker-native/package.json +++ b/packages/host/directory-picker-native/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-directory-picker-native", "description": "Native-OS-chooser backend of the directory-picker seam for the DeepSeek Harness web GUI host", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/directory-picker/package.json b/packages/host/directory-picker/package.json index bf842ab4b4..dbe295059c 100644 --- a/packages/host/directory-picker/package.json +++ b/packages/host/directory-picker/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-directory-picker", "description": "Abstract workspace-directory picking seam (ctx.directoryPicker) for the DeepSeek Harness web GUI host", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/frontend-static/package.json b/packages/host/frontend-static/package.json index 63438329d5..56c3f758c6 100644 --- a/packages/host/frontend-static/package.json +++ b/packages/host/frontend-static/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-frontend-static", "description": "SPA dist server for the Web shell: owns the webserver fallback seat, serving explicit index entries and static assets with traversal rejection and 404 misses", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/open-in-app/package.json b/packages/host/open-in-app/package.json index ed25fee90c..197809be00 100644 --- a/packages/host/open-in-app/package.json +++ b/packages/host/open-in-app/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-open-in-app", "description": "Host half of open-in-app: resolved application catalog, icons, and the launch endpoint as three webServer routes", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/plugin-inventory/package.json b/packages/host/plugin-inventory/package.json index 0d62a746d9..19f402019b 100644 --- a/packages/host/plugin-inventory/package.json +++ b/packages/host/plugin-inventory/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-plugin-inventory", "description": "Read-only Remote projection of current Cordis Loader plugin state", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/product-telemetry-otel/package.json b/packages/host/product-telemetry-otel/package.json index ba6601efaf..1f19f186ec 100644 --- a/packages/host/product-telemetry-otel/package.json +++ b/packages/host/product-telemetry-otel/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-product-telemetry-otel", "description": "Explicit product usage events exported through OpenTelemetry HTTP logs", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/host/webserver/package.json b/packages/host/webserver/package.json index 736e583865..5bdee3e5ae 100644 --- a/packages/host/webserver/package.json +++ b/packages/host/webserver/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-host-webserver", "description": "Web route-registration plugin: HTTP and upgrade routes, index transform taps, and static dist fallback; knows no harness concepts", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/identity/anonymous-user-id/package.json b/packages/identity/anonymous-user-id/package.json index 7eea234fec..00cd0da5a9 100644 --- a/packages/identity/anonymous-user-id/package.json +++ b/packages/identity/anonymous-user-id/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-anonymous-user-id", "description": "Shared anonymous user identity for DeepSeek Harness telemetry and feedback correlation", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/interaction/commands/package.json b/packages/interaction/commands/package.json index c10093fcee..36acaabb15 100644 --- a/packages/interaction/commands/package.json +++ b/packages/interaction/commands/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-commands", "description": "Plugin-owned human command registry for DeepSeek Harness UIs", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/interaction/permission-presets/package.json b/packages/interaction/permission-presets/package.json index f26c128c59..f38ce78c27 100644 --- a/packages/interaction/permission-presets/package.json +++ b/packages/interaction/permission-presets/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-permission-presets", "description": "User-facing permission presets (ctx.permissionPresets) for the DeepSeek Harness: one product-level Permissions select bundling the sandbox-mode and approval-policy knobs, written through to their own session events", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/interaction/tool-ask-user/package.json b/packages/interaction/tool-ask-user/package.json index f7acb9c798..65719ca8f1 100644 --- a/packages/interaction/tool-ask-user/package.json +++ b/packages/interaction/tool-ask-user/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-ask-user", "description": "Model-facing ask_user_question tool over the ctx.userQuestions seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/interaction/user-approval/package.json b/packages/interaction/user-approval/package.json index dcbbb3a921..1fbdfa7929 100644 --- a/packages/interaction/user-approval/package.json +++ b/packages/interaction/user-approval/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-user-approval", "description": "User-approval seam (ctx.approval) for the DeepSeek Harness: one-shot permission decisions dispatched to composed answerers over the approval/request waterfall, fail-closed by default", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/interaction/user-questions/package.json b/packages/interaction/user-questions/package.json index 445ed2a861..4bd9437d00 100644 --- a/packages/interaction/user-questions/package.json +++ b/packages/interaction/user-questions/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-user-questions", "description": "Abstract user-questions seam (ctx.userQuestions) for asking the human during agent runs", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/jobs/jobs-local/package.json b/packages/jobs/jobs-local/package.json index 84c688d2ce..d740b03c8e 100644 --- a/packages/jobs/jobs-local/package.json +++ b/packages/jobs/jobs-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-jobs-local", "description": "Process-local implementation of the DeepSeek Harness background job registry seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/jobs/jobs/package.json b/packages/jobs/jobs/package.json index df4092b0f2..49769beee8 100644 --- a/packages/jobs/jobs/package.json +++ b/packages/jobs/jobs/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-jobs", "description": "Background job registry (ctx.jobs) for the DeepSeek Harness — shared ids, owner isolation, polling, cancellation, and completion listeners for long-running tool work", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/jobs/tool-jobs/package.json b/packages/jobs/tool-jobs/package.json index ef9023e0b6..853bad705b 100644 --- a/packages/jobs/tool-jobs/package.json +++ b/packages/jobs/tool-jobs/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-jobs", "description": "Model-facing background job control tools (job_output, job_list, job_kill) over the ctx.jobs registry", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/deepseek-llm-api-extensions/package.json b/packages/llm/deepseek-llm-api-extensions/package.json index ccefb03261..d53ca93c43 100644 --- a/packages/llm/deepseek-llm-api-extensions/package.json +++ b/packages/llm/deepseek-llm-api-extensions/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-deepseek-llm-api-extensions", "description": "Additive request-field registry for the official DeepSeek LLM API adapter", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/llm-deepseek-account/package.json b/packages/llm/llm-deepseek-account/package.json index a5caa143ac..80eb137897 100644 --- a/packages/llm/llm-deepseek-account/package.json +++ b/packages/llm/llm-deepseek-account/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-deepseek-account", "description": "DeepSeek account provider authentication and discovery", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/llm-deepseek-api-key/package.json b/packages/llm/llm-deepseek-api-key/package.json index 9b46787bf3..82510819ad 100644 --- a/packages/llm/llm-deepseek-api-key/package.json +++ b/packages/llm/llm-deepseek-api-key/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-deepseek-api-key", "description": "DeepSeek api-key provider authentication and discovery", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/llm-deepseek/package.json b/packages/llm/llm-deepseek/package.json index d5c13eb48e..762301b366 100644 --- a/packages/llm/llm-deepseek/package.json +++ b/packages/llm/llm-deepseek/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-deepseek", "description": "DeepSeek Messages adapter", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/llm-pi-ai/package.json b/packages/llm/llm-pi-ai/package.json index 928e122335..5c3698ba78 100644 --- a/packages/llm/llm-pi-ai/package.json +++ b/packages/llm/llm-pi-ai/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-pi-ai", "description": "pi-ai-backed DeepSeek adapter for the DeepSeek Harness LLM seam (design-verification twin of dsh-llm-deepseek)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/llm-retry/package.json b/packages/llm/llm-retry/package.json index b371ebba4a..dca3593891 100644 --- a/packages/llm/llm-retry/package.json +++ b/packages/llm/llm-retry/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-retry", "description": "Provider-routed LLM request retry policy for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/llm/package.json b/packages/llm/llm/package.json index 797c4d6d43..3d107ea528 100644 --- a/packages/llm/llm/package.json +++ b/packages/llm/llm/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm", "description": "Provider-neutral LLM service interface for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/plugin-package-inventory-deepseek/package.json b/packages/llm/plugin-package-inventory-deepseek/package.json index a0aef21fd9..57a18fbad9 100644 --- a/packages/llm/plugin-package-inventory-deepseek/package.json +++ b/packages/llm/plugin-package-inventory-deepseek/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-plugin-package-inventory-deepseek", "description": "Active Loader-backed plugin package inventory for official DeepSeek LLM API requests", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/llm/token-meter/package.json b/packages/llm/token-meter/package.json index 80790e024c..7e211f247e 100644 --- a/packages/llm/token-meter/package.json +++ b/packages/llm/token-meter/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-token-meter", "description": "Replay-aware token measurement service (ctx.tokenMeter) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/lsp/lsp-stdio/package.json b/packages/lsp/lsp-stdio/package.json index 6504316352..3361826448 100644 --- a/packages/lsp/lsp-stdio/package.json +++ b/packages/lsp/lsp-stdio/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-lsp-stdio", "description": "Generic stdio language-server provider for the DeepSeek Harness LSP capability seam (ctx.lsp) — spawns configured servers, translates JSON-RPC, and serves transient-open goToDefinition/findReferences/goToImplementation/hover queries in the host filesystem namespace", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/lsp/lsp/package.json b/packages/lsp/lsp/package.json index 3ce334058f..88c70bdaa1 100644 --- a/packages/lsp/lsp/package.json +++ b/packages/lsp/lsp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-lsp", "description": "Abstract LSP capability seam (ctx.lsp) for the DeepSeek Harness — language-server provider registry keyed by branded id and extension mapping, order-independent per-query selection, normalized definition/references/implementation/hover requests and results, and the LspError taxonomy", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/lsp/tool-lsp/package.json b/packages/lsp/tool-lsp/package.json index f3ca3e7504..e147b997d2 100644 --- a/packages/lsp/tool-lsp/package.json +++ b/packages/lsp/tool-lsp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-lsp", "description": "Model-facing lsp tool over the DeepSeek Harness LSP capability seam (ctx.lsp) \u2014 one read-only tool with goToDefinition/findReferences/goToImplementation/hover operations, one-based UTF-16 cursor coordinates, bounded location rendering, and hover normalization", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/mcp/mcp-client/package.json b/packages/mcp/mcp-client/package.json index c792a50900..d9fcece76d 100644 --- a/packages/mcp/mcp-client/package.json +++ b/packages/mcp/mcp-client/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-mcp-client", "description": "MCP client bridge: connects to MCP servers and registers their tools on ctx.tools", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/mcp/mcp-resources/package.json b/packages/mcp/mcp-resources/package.json index 482cf586e7..0676ad68ed 100644 --- a/packages/mcp/mcp-resources/package.json +++ b/packages/mcp/mcp-resources/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-mcp-resources", "description": "Scoped MCP resource discovery and reading through shared model tools", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/plan/plan-mode/package.json b/packages/plan/plan-mode/package.json index 78f31dc964..7d91ed1602 100644 --- a/packages/plan/plan-mode/package.json +++ b/packages/plan/plan-mode/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-plan-mode", "description": "Logged per-agent plan mode with deployment guidance, a direct slash command, and a user-reviewed exit", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/preset/agent-preset-registry/package.json b/packages/preset/agent-preset-registry/package.json index 31b2314211..3f259cfdeb 100644 --- a/packages/preset/agent-preset-registry/package.json +++ b/packages/preset/agent-preset-registry/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-preset-registry", "description": "Declarative Agent preset registry and profile-backed editing", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/preset/agent-preset/package.json b/packages/preset/agent-preset/package.json index 6c634fe27a..9c604f66ae 100644 --- a/packages/preset/agent-preset/package.json +++ b/packages/preset/agent-preset/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-preset", "description": "Declare an Agent capability composition in Cordis YAML", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/preset/persona/package.json b/packages/preset/persona/package.json index 9377b18f9c..e0a0328bce 100644 --- a/packages/preset/persona/package.json +++ b/packages/preset/persona/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-persona", "description": "Composition-authored deployment persona section for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/ptc-runtime/ptc-runtime-node/package.json b/packages/ptc-runtime/ptc-runtime-node/package.json index 93e5655f7e..1115706424 100644 --- a/packages/ptc-runtime/ptc-runtime-node/package.json +++ b/packages/ptc-runtime/ptc-runtime-node/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-ptc-runtime-node", "description": "Node PTC execution with per-call sandboxed processes or in-memory REPL state", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/ptc-runtime/ptc-runtime/package.json b/packages/ptc-runtime/ptc-runtime/package.json index 701c77dfaa..dee480ff69 100644 --- a/packages/ptc-runtime/ptc-runtime/package.json +++ b/packages/ptc-runtime/ptc-runtime/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-ptc-runtime", "description": "Abstract PTC execution seam (ctx.ptcRuntime) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sandbox/sandbox-local/package.json b/packages/sandbox/sandbox-local/package.json index 2e3fa70731..b168c81841 100644 --- a/packages/sandbox/sandbox-local/package.json +++ b/packages/sandbox/sandbox-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sandbox-local", "description": "Local process-sandbox backends for the DeepSeek Harness sandbox seam: bwrap, the npm-distributed landlock-run launcher, macOS Seatbelt, or the Windows ACL restricted-token runner — functionally probed, fail-closed", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sandbox/sandbox-policy/package.json b/packages/sandbox/sandbox-policy/package.json index 497d5dec75..bd9dc62423 100644 --- a/packages/sandbox/sandbox-policy/package.json +++ b/packages/sandbox/sandbox-policy/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sandbox-policy", "description": "Per-call sandbox policy resolver and current model context: deployment fallbacks plus each session's mode and workspace root, shared by every enforcing capability family", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sandbox/sandbox-windows-acl/package.json b/packages/sandbox/sandbox-windows-acl/package.json index 8c5adcf764..5a5f3a3ef9 100644 --- a/packages/sandbox/sandbox-windows-acl/package.json +++ b/packages/sandbox/sandbox-windows-acl/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sandbox-windows-acl", "description": "Windows ACL write-restriction sandbox backend (restricted-token spawn with capability-SID write allowlist) for the DeepSeek Harness sandbox seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sandbox/sandbox/package.json b/packages/sandbox/sandbox/package.json index a70edb6e14..690184d0d0 100644 --- a/packages/sandbox/sandbox/package.json +++ b/packages/sandbox/sandbox/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sandbox", "description": "Abstract process-sandbox seam (ctx.sandbox) for the DeepSeek Harness: same-world confinement vocabulary and the SandboxProvider contract", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/schedule/schedule/package.json b/packages/schedule/schedule/package.json index cc50c0f48f..74cccd5a0b 100644 --- a/packages/schedule/schedule/package.json +++ b/packages/schedule/schedule/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-schedule", "description": "Host-wide durable reminders with shared management and original-Session delivery", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/schedule/tool-schedule/package.json b/packages/schedule/tool-schedule/package.json index 7e769c6fb8..3068cfe2de 100644 --- a/packages/schedule/tool-schedule/package.json +++ b/packages/schedule/tool-schedule/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-schedule", "description": "Model-facing reminder management tools (schedule_create, schedule_list, schedule_update, schedule_delete) over the Host ctx.schedule service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sdk/client/package.json b/packages/sdk/client/package.json index 46b9b06f8b..32560a78bd 100644 --- a/packages/sdk/client/package.json +++ b/packages/sdk/client/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sdk-client", "description": "TypeScript client SDK for driving a DeepSeek Harness runtime subprocess over stdio JSON-RPC: the DeepSeekHarness high-level turns API and the lower-level HarnessClient", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sdk/protocol/package.json b/packages/sdk/protocol/package.json index b37eebc9af..36e1a687cc 100644 --- a/packages/sdk/protocol/package.json +++ b/packages/sdk/protocol/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sdk-protocol", "description": "Shared wire protocol for the DeepSeek Harness SDK runtime: the newline-delimited JSON-RPC stdio transport and the named request, result, and notification types spoken between the runtime server and SDK clients", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/sdk/server/package.json b/packages/sdk/server/package.json index 7fa0ab2138..272fb47150 100644 --- a/packages/sdk/server/package.json +++ b/packages/sdk/server/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sdk-jsonrpc-server", "description": "Stdio JSON-RPC server plugin for out-of-process DeepSeek Harness SDK clients", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session-query/session-log-export/package.json b/packages/session-query/session-log-export/package.json index 163224efa8..404de34148 100644 --- a/packages/session-query/session-log-export/package.json +++ b/packages/session-query/session-log-export/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-log-export", "description": "Web Session-log export command and shared download dialog", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session-query/session-query-sqlite/package.json b/packages/session-query/session-query-sqlite/package.json index 7090c0d7e1..0ca9381a49 100644 --- a/packages/session-query/session-query-sqlite/package.json +++ b/packages/session-query/session-query-sqlite/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-query-sqlite", "description": "Concrete ctx.sessionQuery backend with SQLite FTS5 search", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session-query/session-query/package.json b/packages/session-query/session-query/package.json index 0a01b5bcd3..aedb8bbfa3 100644 --- a/packages/session-query/session-query/package.json +++ b/packages/session-query/session-query/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-query", "description": "Combined session query service contract with concrete reads, traces, and filters", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-checkpoint-policy/package.json b/packages/session/session-checkpoint-policy/package.json index eb2e5fd57d..2673fcd02b 100644 --- a/packages/session/session-checkpoint-policy/package.json +++ b/packages/session/session-checkpoint-policy/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-checkpoint-policy", "description": "Semantic session durability checkpoints before model requests and tool side effects", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-format-catalog/package.json b/packages/session/session-format-catalog/package.json index 401942de8d..32767841e8 100644 --- a/packages/session/session-format-catalog/package.json +++ b/packages/session/session-format-catalog/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-format-catalog", "description": "Build-static first-party Session format codec and migration catalog", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-format-v0-to-v1/package.json b/packages/session/session-format-v0-to-v1/package.json index 5d30d5c5e4..7222125ea3 100644 --- a/packages/session/session-format-v0-to-v1/package.json +++ b/packages/session/session-format-v0-to-v1/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-format-v0-to-v1", "description": "Frozen released-v0 Session codec and identity migration to v1", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-format-v1-to-v2/package.json b/packages/session/session-format-v1-to-v2/package.json index 08a85810f2..9829b94adb 100644 --- a/packages/session/session-format-v1-to-v2/package.json +++ b/packages/session/session-format-v1-to-v2/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-format-v1-to-v2", "description": "Frozen released-v1 Session codec and assistant-stream migration to v2", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-format-v2-to-v3/package.json b/packages/session/session-format-v2-to-v3/package.json index 693b2368e3..5c912d402b 100644 --- a/packages/session/session-format-v2-to-v3/package.json +++ b/packages/session/session-format-v2-to-v3/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-format-v2-to-v3", "description": "Streaming system-prompt, canonical-envelope and PTC migration into V3", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-format-v3-to-v4/package.json b/packages/session/session-format-v3-to-v4/package.json index 6929a7637d..56b64bfe0a 100644 --- a/packages/session/session-format-v3-to-v4/package.json +++ b/packages/session/session-format-v3-to-v4/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-format-v3-to-v4", "description": "Streaming tool-role migration and delivery validation from Session V3 to V4", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-format/package.json b/packages/session/session-format/package.json index 45c937b578..8de29742a7 100644 --- a/packages/session/session-format/package.json +++ b/packages/session/session-format/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-format", "description": "Streaming adjacent Session format migration machinery", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-log-deepseek/package.json b/packages/session/session-log-deepseek/package.json index 8a836fac1e..c22beb4544 100644 --- a/packages/session/session-log-deepseek/package.json +++ b/packages/session/session-log-deepseek/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-log-deepseek", "description": "Incremental lossless session-log request extension for the official DeepSeek LLM API", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-persistence-jsonl/package.json b/packages/session/session-persistence-jsonl/package.json index 8d5f40ee80..3d78433aff 100644 --- a/packages/session/session-persistence-jsonl/package.json +++ b/packages/session/session-persistence-jsonl/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-persistence-jsonl", "description": "JSONL durable session persistence backend for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-persistence/package.json b/packages/session/session-persistence/package.json index b095376d43..10478000ac 100644 --- a/packages/session/session-persistence/package.json +++ b/packages/session/session-persistence/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-persistence", "description": "Abstract durable session persistence seam (ctx.sessionPersistence) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-projection-cache/package.json b/packages/session/session-projection-cache/package.json index eb7b46590f..b611138920 100644 --- a/packages/session/session-projection-cache/package.json +++ b/packages/session/session-projection-cache/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-projection-cache", "description": "Persisted projection cache (ctx.sessionProjectionCache): durable per-session checkpoint records on the session_projcache storage domain (per-record layout), throttled write-behind, and the cached listing read", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-projection/package.json b/packages/session/session-projection/package.json index eee5012258..edf7dc38a4 100644 --- a/packages/session/session-projection/package.json +++ b/packages/session/session-projection/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-projection", "description": "Session-projection seam: the merge-extensible projection type table, the provider contract, and the ctx.sessionProjections registry serving whole current values of log-derived per-session state", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-stats/package.json b/packages/session/session-stats/package.json index d8ffb97bae..5f26bd6183 100644 --- a/packages/session/session-stats/package.json +++ b/packages/session/session-stats/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-stats", "description": "Whole-log conversation counts and wall times projection (sessionStats) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-telemetry-otel/package.json b/packages/session/session-telemetry-otel/package.json index 19808aa273..f20f617b66 100644 --- a/packages/session/session-telemetry-otel/package.json +++ b/packages/session/session-telemetry-otel/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-telemetry-otel", "description": "Feedback-authorized Session logs over byte-bounded OpenTelemetry HTTP requests", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-telemetry/package.json b/packages/session/session-telemetry/package.json index 90d6029f0a..54ed052543 100644 --- a/packages/session/session-telemetry/package.json +++ b/packages/session/session-telemetry/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-telemetry", "description": "SessionTelemetryBackend seam for the DeepSeek Harness: session-event capture, projection, redaction, and handoff to a reporting backend", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-title-first-prompt-llm/package.json b/packages/session/session-title-first-prompt-llm/package.json index 2f90293dfe..4a9682a696 100644 --- a/packages/session/session-title-first-prompt-llm/package.json +++ b/packages/session/session-title-first-prompt-llm/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-title-first-prompt-llm", "description": "First-message LLM provider plugin for DeepSeek Harness session titles", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-title-llm/package.json b/packages/session/session-title-llm/package.json index 55273ffda4..58a6860731 100644 --- a/packages/session/session-title-llm/package.json +++ b/packages/session/session-title-llm/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-title-llm", "description": "Shared LLM generation policy for DeepSeek Harness session-title providers", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-title/package.json b/packages/session/session-title/package.json index 4f45d794d3..be692e609e 100644 --- a/packages/session/session-title/package.json +++ b/packages/session/session-title/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-title", "description": "Log-backed session title service and provider registry for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/session-turn-outline/package.json b/packages/session/session-turn-outline/package.json index 8c4e11b50d..43d10802f5 100644 --- a/packages/session/session-turn-outline/package.json +++ b/packages/session/session-turn-outline/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-turn-outline", "description": "Whole-log turn outline projection (turnOutline) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/tool-working-directory/package.json b/packages/session/tool-working-directory/package.json index b6149daca6..36250ec5f4 100644 --- a/packages/session/tool-working-directory/package.json +++ b/packages/session/tool-working-directory/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-working-directory", "description": "Read and change the active Session working directory", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/session/working-directory/package.json b/packages/session/working-directory/package.json index a1a87aebbd..300d0c47bc 100644 --- a/packages/session/working-directory/package.json +++ b/packages/session/working-directory/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-working-directory", "description": "Session working directories with durable changes and model context", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/settings/settings/package.json b/packages/settings/settings/package.json index 10b59d8cf5..177b769b92 100644 --- a/packages/settings/settings/package.json +++ b/packages/settings/settings/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-settings", "description": "Abstract user-settings seam (ctx.settings) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/bash-local/package.json b/packages/shell/bash-local/package.json index cc6995bb81..1df5aa2bb6 100644 --- a/packages/shell/bash-local/package.json +++ b/packages/shell/bash-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-bash-local", "description": "Local-subprocess implementation of the DeepSeek Harness bash executor seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/bash-sandbox/package.json b/packages/shell/bash-sandbox/package.json index 219206a060..10f2751c74 100644 --- a/packages/shell/bash-sandbox/package.json +++ b/packages/shell/bash-sandbox/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-bash-sandbox", "description": "Sandbox-consuming implementation of the DeepSeek Harness bash executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/pwsh-local/package.json b/packages/shell/pwsh-local/package.json index 79c49fdcb7..600775d5ae 100644 --- a/packages/shell/pwsh-local/package.json +++ b/packages/shell/pwsh-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-pwsh-local", "description": "Local PowerShell implementation of the DeepSeek Harness bash executor seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/pwsh-sandbox/package.json b/packages/shell/pwsh-sandbox/package.json index 8074eb502f..948dbd83c6 100644 --- a/packages/shell/pwsh-sandbox/package.json +++ b/packages/shell/pwsh-sandbox/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-pwsh-sandbox", "description": "Sandbox-consuming implementation of the DeepSeek Harness PowerShell executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/shell-env/package.json b/packages/shell/shell-env/package.json index c4bd3ed363..ab3ea4b194 100644 --- a/packages/shell/shell-env/package.json +++ b/packages/shell/shell-env/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-shell-env", "description": "Tool-independent managed DSH_* shell environment registry", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/shell/package.json b/packages/shell/shell/package.json index ba216271ec..bfde1a648c 100644 --- a/packages/shell/shell/package.json +++ b/packages/shell/shell/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-shell", "description": "Abstract bash executor seam (ctx.shell) for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/tool-bash-persistent/package.json b/packages/shell/tool-bash-persistent/package.json index d3385db772..3c4c9ff736 100644 --- a/packages/shell/tool-bash-persistent/package.json +++ b/packages/shell/tool-bash-persistent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-bash-persistent", "description": "Model-facing owner-scoped persistent Bash tool backed by the Harness PTY service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/tool-bash/package.json b/packages/shell/tool-bash/package.json index e7e8a153e3..781f4facdd 100644 --- a/packages/shell/tool-bash/package.json +++ b/packages/shell/tool-bash/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-bash", "description": "Model-facing bash tool with optional generic background-job and sandbox-escalation support", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/tool-pwsh-persistent/package.json b/packages/shell/tool-pwsh-persistent/package.json index b1d008637f..44a6c7a2ef 100644 --- a/packages/shell/tool-pwsh-persistent/package.json +++ b/packages/shell/tool-pwsh-persistent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-pwsh-persistent", "description": "Model-facing owner-scoped persistent PowerShell tool backed by the Harness PTY service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/shell/tool-pwsh/package.json b/packages/shell/tool-pwsh/package.json index 48ceba375f..4d9572bef3 100644 --- a/packages/shell/tool-pwsh/package.json +++ b/packages/shell/tool-pwsh/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-pwsh", "description": "Model-facing pwsh tool over the bash executor seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/skill/skill-filesystem/package.json b/packages/skill/skill-filesystem/package.json index beae26cfe2..4799dfb506 100644 --- a/packages/skill/skill-filesystem/package.json +++ b/packages/skill/skill-filesystem/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-skill-filesystem", "description": "Local filesystem skill provider for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/skill/skill-office/package.json b/packages/skill/skill-office/package.json index db7285dac2..fe751bfab6 100644 --- a/packages/skill/skill-office/package.json +++ b/packages/skill/skill-office/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-skill-office", "description": "Bundled Word, PowerPoint, and Excel workflows and LibreOffice Kit operations", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/skill/skill/package.json b/packages/skill/skill/package.json index 6b3f86e447..a7a247c1f6 100644 --- a/packages/skill/skill/package.json +++ b/packages/skill/skill/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-skill", "description": "Agent skill provider registry for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/skill/tool-skill/package.json b/packages/skill/tool-skill/package.json index 1ac3330722..82051a8171 100644 --- a/packages/skill/tool-skill/package.json +++ b/packages/skill/tool-skill/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-skill", "description": "Model-facing skill loading tool for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/skill/tool-workspace-dependencies/package.json b/packages/skill/tool-workspace-dependencies/package.json index f097ac007f..3ac0f04645 100644 --- a/packages/skill/tool-workspace-dependencies/package.json +++ b/packages/skill/tool-workspace-dependencies/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-workspace-dependencies", "description": "The load_workspace_dependencies tool: absolute paths into a bundled Python, Node.js, and pnpm payload", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/spill/spill-local/package.json b/packages/spill/spill-local/package.json index b199115e61..91295d4b17 100644 --- a/packages/spill/spill-local/package.json +++ b/packages/spill/spill-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-spill-local", "description": "Local-filesystem implementation of the DeepSeek Harness spill storage seam (private session-scoped files)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/spill/spill-policy/package.json b/packages/spill/spill-policy/package.json index adafa167c7..68ea4830ba 100644 --- a/packages/spill/spill-policy/package.json +++ b/packages/spill/spill-policy/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-spill-policy", "description": "Token-budgeted tool-result retention with recoverable text and image paths", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/spill/spill/package.json b/packages/spill/spill/package.json index b5ca7bce9a..3293e8ff1b 100644 --- a/packages/spill/spill/package.json +++ b/packages/spill/spill/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-spill", "description": "Abstract spill storage seam (ctx.spillStore) for the DeepSeek Harness — save oversized tool text and return a retrieval locator", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/ssh/fs-ssh/package.json b/packages/ssh/fs-ssh/package.json index 2dbbb3c4df..6ea7ac87c9 100644 --- a/packages/ssh/fs-ssh/package.json +++ b/packages/ssh/fs-ssh/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-fs-ssh", "description": "Filesystem provider over the shared POSIX SSH helper", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/ssh/sandbox-ssh/package.json b/packages/ssh/sandbox-ssh/package.json index 361620348e..c757b4ec19 100644 --- a/packages/ssh/sandbox-ssh/package.json +++ b/packages/ssh/sandbox-ssh/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-sandbox-ssh", "description": "Remote POSIX sandbox argv provider over the shared SSH helper", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/ssh/ssh-helper-runtime/package.json b/packages/ssh/ssh-helper-runtime/package.json index 8579ddfbe7..1fbdeccea2 100644 --- a/packages/ssh/ssh-helper-runtime/package.json +++ b/packages/ssh/ssh-helper-runtime/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-ssh-helper-runtime", "description": "Private executable carrier for the POSIX SSH helper and managed workers", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "private": true, "type": "module", "main": "lib/index.js", diff --git a/packages/ssh/ssh/package.json b/packages/ssh/ssh/package.json index 5330f3bf41..36cab186d0 100644 --- a/packages/ssh/ssh/package.json +++ b/packages/ssh/ssh/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-ssh", "description": "Shared OpenSSH connection and versioned POSIX remote helper", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/ssh/subprocess-ssh/package.json b/packages/ssh/subprocess-ssh/package.json index 1c3cd9d170..46d70838fc 100644 --- a/packages/ssh/subprocess-ssh/package.json +++ b/packages/ssh/subprocess-ssh/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subprocess-ssh", "description": "Subprocess and terminal provider over the shared POSIX SSH helper", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/storage/storage-domain/package.json b/packages/storage/storage-domain/package.json index 0cdba8d9f4..05a67fc324 100644 --- a/packages/storage/storage-domain/package.json +++ b/packages/storage/storage-domain/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-storage-domain", "description": "Domain data form (ctx.storage.domain): schema-validated, event-emitting KV domains over storage backends for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/storage/storage-json/package.json b/packages/storage/storage-json/package.json index 70a1378f91..2569659752 100644 --- a/packages/storage/storage-json/package.json +++ b/packages/storage/storage-json/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-storage-json", "description": "JSON file KV storage backend for the DeepSeek Harness storage hub", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/storage/storage-sqlite/package.json b/packages/storage/storage-sqlite/package.json index 382187da93..e137775577 100644 --- a/packages/storage/storage-sqlite/package.json +++ b/packages/storage/storage-sqlite/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-storage-sqlite", "description": "SQLite storage backend (kv facet) for the DeepSeek Harness storage hub", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/storage/storage/package.json b/packages/storage/storage/package.json index b2129a7e0f..3b7b4aefc6 100644 --- a/packages/storage/storage/package.json +++ b/packages/storage/storage/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-storage", "description": "Storage hub (ctx.storage): named backend registry plus mounted data-form facilities for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent-acp/package.json b/packages/subagent/subagent-acp/package.json index f86c54c5de..5d9324fd1d 100644 --- a/packages/subagent/subagent-acp/package.json +++ b/packages/subagent/subagent-acp/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent-acp", "description": "Out-of-process ACP subagent backend: drives a child agent in a spawned subprocess over the Agent Client Protocol", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent-claude-code/package.json b/packages/subagent/subagent-claude-code/package.json index 9ced501c1f..5dc2d5fec4 100644 --- a/packages/subagent/subagent-claude-code/package.json +++ b/packages/subagent/subagent-claude-code/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent-claude-code", "description": "One-shot Claude Code subagent provider over the official Agent SDK", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent-codex/README.i18n.yaml b/packages/subagent/subagent-codex/README.i18n.yaml index 2534f90e70..1b6d354c28 100644 --- a/packages/subagent/subagent-codex/README.i18n.yaml +++ b/packages/subagent/subagent-codex/README.i18n.yaml @@ -9,8 +9,8 @@ en: c3f2c833cb91228d zh: 3267d24936c5973f /deepseek-ai-dsh-subagent-codex/summary: - en: 6eade95bb21eae52 - zh: 72de7d6fcdc0c31a + en: 895eb3910428c51e + zh: c5eb0d32c789520d /deepseek-ai-dsh-subagent-codex/table-of-contents: en: d152484eb41ac6b4 zh: 09388d293f9be9cb @@ -27,8 +27,8 @@ en: a36399c7ac98724d zh: d43815e08689da6b /deepseek-ai-dsh-subagent-codex/use-this-package/what-you-get: - en: 4b271f17f16fb162 - zh: 154c909023f1366b + en: abd75016d4c7f61e + zh: 904158c896bd21ce /deepseek-ai-dsh-subagent-codex/use-this-package/failure-and-recovery: en: 4ffe0312c5a8f722 zh: 532a1997e9ae825b @@ -42,8 +42,8 @@ en: 36647129f061610f zh: 42947c700d1d9202 /deepseek-ai-dsh-subagent-codex/understand-the-implementation/run-flow: - en: 19b10ff145644564 - zh: c015d530f1bd227e + en: b8244d26ba25f468 + zh: 6cb77dfece19cce7 /deepseek-ai-dsh-subagent-codex/further-exploration: en: e2e35a40060c1ba8 zh: c1676579ebac7c7f @@ -66,17 +66,17 @@ en: 4c391bb16e3cd380 zh: ab3847efe35fa6dc /deepseek-ai-dsh-subagent-codex/model-experience/parent-scheduling-and-results-indirectly/what-the-model-sees: - en: 18dcb74d6a52e96a - zh: 068c57f9fd959ecb + en: 9ca324fb0b034a6c + zh: 83ff78167bd31a14 /deepseek-ai-dsh-subagent-codex/model-experience/parent-scheduling-and-results-indirectly/token-effect: - en: 0c436fb0897a6eca - zh: 7ec0a953d0232af2 + en: f618e05fde7c8b57 + zh: 7306c4535b6fa4c0 /deepseek-ai-dsh-subagent-codex/model-experience/parent-scheduling-and-results-indirectly/kv-cache-effect: en: 19f1316ea4bb1bb9 zh: 181231ddffdc8494 /deepseek-ai-dsh-subagent-codex/known-limitations-and-deferred-work: - en: 46cbb0a3f4f81a5a - zh: a27faaaefe61158f + en: e55a466c946cbce4 + zh: 96dfb5c8f8210927 /deepseek-ai-dsh-subagent-codex/known-limitations-and-deferred-work/dev-note: en: cacc98025ed8913e zh: e7673a3087ec0c62 diff --git a/packages/subagent/subagent-codex/README.md b/packages/subagent/subagent-codex/README.md index ee5930344d..376e6e9fe4 100644 --- a/packages/subagent/subagent-codex/README.md +++ b/packages/subagent/subagent-codex/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Install `@deepseek-ai/dsh-subagent-codex` into a Profile when delegated work should run in a genuine, unattended Codex session in the parent Session's workspace. Each delegation uses a fresh isolated Codex thread for one self-contained text task and returns only its final answer or a safe failure diagnostic. Native Codex configuration and authentication remain authoritative, while `permissionMode` selects the non-interactive approval and sandbox behavior. The Bundle supplies a compatible native Codex payload and adds its delegation tool as a global tool. +Install `@deepseek-ai/dsh-subagent-codex` into a Profile when delegated work should run in a genuine, unattended Codex session in the parent Session's workspace. Each delegation uses a fresh isolated Codex thread for one self-contained text task and returns only the selected answer or a safe failure diagnostic. Native Codex configuration and authentication remain authoritative, while `permissionMode` selects the non-interactive approval and sandbox behavior. The Bundle supplies a compatible native Codex payload and adds its delegation tool as a global tool. ## Table of Contents @@ -74,7 +74,7 @@ The tool returns a child id after accepting the task and sends its result to the ### What you get -The completion notice contains the final Codex answer, or the stop reason and optional safe diagnostic. The parent Session also retains the external task identity and complete terminal result independently of the notice; no local child Session is created. Product reasoning, tool activity, raw stderr, and workspace diffs do not enter the parent Session. +The completion notice contains the selected Codex answer, or the stop reason and optional safe diagnostic. The parent Session also retains the external task identity and complete terminal result independently of the notice; no local child Session is created. Product reasoning, tool activity, raw stderr, and workspace diffs do not enter the parent Session. ### Failure and recovery @@ -107,7 +107,7 @@ This section explains how the provider drives a real Codex app-server and where ### Run flow -A start accepts only a non-empty sequence of text blocks and uses the child cwd selected by the subagent service. It spawns the fixed command through the subprocess seam, performs the `initialize` → `initialized` handshake, maps the Profile-selected mode and optional model into official `thread/start` fields beside `{ cwd, ephemeral: true }`, and publishes the run only after Codex returns a valid ephemeral thread. The published result starts exactly one turn, accepts only notifications for that run's thread and turn, and waits for the authoritative `turn/completed` terminal. The latest `agentMessage` with `phase: "final_answer"` wins; when Codex emits no explicit final phase, the latest message with `phase: null` is the compatibility fallback. A successful turn with no nonblank answer settles as an error. Failed turns use the coarse categories `limit`, `access-policy`, `service`, `transport`, `product-error`, `invalid-result`, or `unknown`; an early app-server exit uses `process`, and applicable connection and stream failures retain a numeric `httpStatusCode`. +A start accepts only a non-empty sequence of text blocks and uses the child cwd selected by the subagent service. It spawns the fixed command through the subprocess seam, performs the `initialize` → `initialized` handshake, maps the Profile-selected mode and optional model into official `thread/start` fields beside `{ cwd, ephemeral: true }`, and publishes the run only after Codex returns a valid ephemeral thread. The published result starts exactly one turn, accepts only notifications for that run's thread and turn, and waits for the authoritative `turn/completed` terminal. The latest `agentMessage` with `phase: "final_answer"` wins; without one, the latest `phase: "partial_answer"` message (stable answer text that later assistant output or tool calls may follow) is selected; when Codex emits neither explicit answer phase, the latest message with `phase: null` is the compatibility fallback. `commentary` messages are never selected, and any other phase fails the run. A successful turn with no nonblank answer settles as an error. Failed turns use the coarse categories `limit`, `access-policy`, `service`, `transport`, `product-error`, `invalid-result`, or `unknown`; an early app-server exit uses `process`, and applicable connection and stream failures retain a numeric `httpStatusCode`. @@ -147,11 +147,11 @@ Independent of the parent request cache. Reuse depends only on Codex's own provi #### What the model sees -Through `dsh-tool-subagent`, the parent model first receives a child id, then the final Codex answer or a failure notice with its stop reason and safe diagnostic. Diagnostics contain only fixed stage, category, and observed protocol or process facts. Product reasoning, intermediate messages, tool activity, stderr, usage, product identifiers, commands, paths, and raw protocol payloads are not copied into the parent Session. +Through `dsh-tool-subagent`, the parent model first receives a child id, then the selected Codex answer (final, partial, or unphased, in that order of precedence) or a failure notice with its stop reason and safe diagnostic. Diagnostics contain only fixed stage, category, and observed protocol or process facts. Product reasoning, intermediate messages, tool activity, stderr, usage, product identifiers, commands, paths, and raw protocol payloads are not copied into the parent Session. #### Token effect -Parent input grows by the start acknowledgement and completion notice, including the final answer or failure detail. Child tokens do not enter the parent context. This provider adds no parent tool schema by itself. +Parent input grows by the start acknowledgement and completion notice, including the selected answer or failure detail. Child tokens do not enter the parent context. This provider adds no parent tool schema by itself. #### KV Cache effect @@ -170,7 +170,7 @@ These limits define when this provider is a poor fit or needs special operationa - **The native platform payload is required at delegation time** — installs that omit optional dependencies, unsupported platforms, and missing or damaged payloads fail at the first run; there is no host-CLI fallback. - **Compatibility is pinned by development evidence** — upgrading from the verified 0.153.4 protocol baseline requires regenerating upstream schema evidence and rerunning handshake, answer-selection, approval, cancellation, keyless real-product, and credentialed DeepSeek nonce tests. - **No human approval path** — known unattended approval requests are denied and unknown server requests fail closed; the three Profile modes never create a DSH interaction channel or per-call allow policy. -- **Assistant payload is final text only** — failed runs can also expose a separate safe diagnostic; reasoning, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain outside the parent Session. Task identity and terminal results are retained in the parent log. +- **Assistant payload is the selected answer text only** — failed runs can also expose a separate safe diagnostic; reasoning, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain outside the parent Session. Task identity and terminal results are retained in the parent log. - **No optional shared capabilities** — `agentPreset`, `agentOptions`, output schemas, and harness depth enforcement are rejected by the shared service for this provider. - **No wall-clock timeout or side-effect rollback** — the caller cancels long work, and files or external systems changed before cancellation are not restored. diff --git a/packages/subagent/subagent-codex/README.zh.md b/packages/subagent/subagent-codex/README.zh.md index 91803ef3cb..5e488daaf8 100644 --- a/packages/subagent/subagent-codex/README.zh.md +++ b/packages/subagent/subagent-codex/README.zh.md @@ -9,7 +9,7 @@ kind: "package-bundle" ## 概述 -当委派工作需要在父会话工作区中的真实无人值守 Codex 会话内运行时,把 `@deepseek-ai/dsh-subagent-codex` 安装进 Profile。每次委派都会为一个自包含文本任务使用全新且隔离的 Codex 线程,并且只返回其最终答案或安全失败诊断。原生 Codex 配置和身份验证继续作为权威来源,而 `permissionMode` 选择非交互式审批和沙箱行为。Bundle 提供兼容的原生 Codex 载荷,并以全局工具形式添加委派工具。 +当委派工作需要在父会话工作区中的真实无人值守 Codex 会话内运行时,把 `@deepseek-ai/dsh-subagent-codex` 安装进 Profile。每次委派都会为一个自包含文本任务使用全新且隔离的 Codex 线程,并且只返回所选答案或安全失败诊断。原生 Codex 配置和身份验证继续作为权威来源,而 `permissionMode` 选择非交互式审批和沙箱行为。Bundle 提供兼容的原生 Codex 载荷,并以全局工具形式添加委派工具。 ## 目录 @@ -74,7 +74,7 @@ Bundle 将 `tool-subagent-codex` 作为 Host 条目插入,因此 `subagent_cod ### 你会得到什么 -完成通知包含 Codex 的最终答案,或停止原因与可选的安全诊断。父会话还独立保存外部任务的身份和完整终态结果;无需创建本地子 Session。产品推理、工具活动、原始 stderr 与工作区差异不会进入父会话。 +完成通知包含所选的 Codex 答案,或停止原因与可选的安全诊断。父会话还独立保存外部任务的身份和完整终态结果;无需创建本地子 Session。产品推理、工具活动、原始 stderr 与工作区差异不会进入父会话。 ### 失败与恢复 @@ -107,7 +107,7 @@ Bundle 将 `tool-subagent-codex` 作为 Host 条目插入,因此 `subagent_cod ### 运行流程 -一次启动只接受非空的文本块序列,并使用子代理服务选择的子级 cwd。它经子进程 seam spawn 固定命令,完成 `initialize` → `initialized` 握手,把 Profile 选择的模式与可选模型映射为官方 `thread/start` 字段并与 `{ cwd, ephemeral: true }` 一起发送,且仅在 Codex 返回有效的临时线程后发布运行。已发布的结果恰好启动一个轮次,只接受与此次运行的线程和轮次匹配的通知,并等待权威的 `turn/completed` 终态。以最后一条 `phase: "final_answer"` 的 `agentMessage` 为准;若 Codex 没有发出明确的最终阶段,则以最后一条 `phase: null` 的消息作为兼容性回退。成功完成的轮次若没有非空白答案,结果也会判为错误。失败轮次使用粗粒度类别 `limit`、`access-policy`、`service`、`transport`、`product-error`、`invalid-result` 或 `unknown`;app-server 提前退出使用 `process`,适用的连接与 stream 失败保留数值 `httpStatusCode`。 +一次启动只接受非空的文本块序列,并使用子代理服务选择的子级 cwd。它经子进程 seam spawn 固定命令,完成 `initialize` → `initialized` 握手,把 Profile 选择的模式与可选模型映射为官方 `thread/start` 字段并与 `{ cwd, ephemeral: true }` 一起发送,且仅在 Codex 返回有效的临时线程后发布运行。已发布的结果恰好启动一个轮次,只接受与此次运行的线程和轮次匹配的通知,并等待权威的 `turn/completed` 终态。以最后一条 `phase: "final_answer"` 的 `agentMessage` 为准;若没有,则选最后一条 `phase: "partial_answer"` 的消息(稳定的回答文本,其后仍可能有助手输出或工具调用);若 Codex 两种明确的回答阶段都没有发出,则以最后一条 `phase: null` 的消息作为兼容性回退。`commentary` 消息绝不会被选中,其它任何阶段都会使运行失败。成功完成的轮次若没有非空白答案,结果也会判为错误。失败轮次使用粗粒度类别 `limit`、`access-policy`、`service`、`transport`、`product-error`、`invalid-result` 或 `unknown`;app-server 提前退出使用 `process`,适用的连接与 stream 失败保留数值 `httpStatusCode`。 @@ -147,11 +147,11 @@ Codex 子级会在一个全新的临时线程中,以单个轮次接收这些 #### 模型看到什么 -通过 `dsh-tool-subagent`,父模型先收到 child id,随后收到 Codex 最终答案或带停止原因与安全诊断的失败通知。诊断只包含固定的阶段、类别和已观测的协议或进程事实。产品推理、中间消息、工具活动、stderr、用量、产品标识符、命令、路径与原始协议载荷不会复制到父 Session。 +通过 `dsh-tool-subagent`,父模型先收到 child id,随后收到所选的 Codex 答案(按 final、partial、未标阶段的优先次序)或带停止原因与安全诊断的失败通知。诊断只包含固定的阶段、类别和已观测的协议或进程事实。产品推理、中间消息、工具活动、stderr、用量、产品标识符、命令、路径与原始协议载荷不会复制到父 Session。 #### Token 影响 -父级输入会增加启动确认与完成通知,包括最终答案或失败信息。子任务 token 不进入父级上下文。本提供方自身不添加父级工具 schema。 +父级输入会增加启动确认与完成通知,包括所选答案或失败信息。子任务 token 不进入父级上下文。本提供方自身不添加父级工具 schema。 #### KV Cache 影响 @@ -170,7 +170,7 @@ Codex 子级会在一个全新的临时线程中,以单个轮次接收这些 - **委派时必须存在原生平台载荷**——省略 optional dependencies 的安装、不受支持的平台以及缺失或损坏的载荷都会在第一次运行时失败;不会回退到宿主 CLI。 - **兼容性由开发证据锁定**——若要从已验证的 0.153.4 协议基线升级,必须重新生成上游 schema 证据,并重新运行握手、答案选择、审批、取消、无密钥真实产品以及带密钥的 DeepSeek 随机数测试。 - **没有人工审批路径**——已知的无人值守审批请求会被拒绝,未知服务器请求会以默认拒绝方式使运行失败;三种 Profile 模式都不会创建 DSH 交互通道或逐次调用 allow 策略。 -- **assistant 载荷仅包含最终文本**——失败运行还可公开独立的安全诊断;推理、中间消息、工具通信、用量、stderr 与工作区差异不会进入父 Session。任务身份与终态结果保存在父日志中。 +- **assistant 载荷仅包含所选回答文本**——失败运行还可公开独立的安全诊断;推理、中间消息、工具通信、用量、stderr 与工作区差异不会进入父 Session。任务身份与终态结果保存在父日志中。 - **没有可选的共享能力**——对于本提供方,共享服务会拒绝 `agentPreset`、`agentOptions`、输出 schema 和 harness 深度强制约束。 - **没有按实际经过时间触发的超时或副作用回滚**——长时间运行的工作由调用方取消,且取消前已更改的文件或外部系统不会恢复原状。 diff --git a/packages/subagent/subagent-codex/package.json b/packages/subagent/subagent-codex/package.json index 64671f6cbe..f28edf4f18 100644 --- a/packages/subagent/subagent-codex/package.json +++ b/packages/subagent/subagent-codex/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent-codex", "description": "One-shot Codex subagent provider over the official app-server protocol", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent-codex/src/wire.ts b/packages/subagent/subagent-codex/src/wire.ts index 9a9f284eed..b4e0f662b1 100644 --- a/packages/subagent/subagent-codex/src/wire.ts +++ b/packages/subagent/subagent-codex/src/wire.ts @@ -205,6 +205,7 @@ export class CodexAppServerWire { readonly order: number }> = [] private lastFinalAnswer: string | undefined + private lastPartialAnswer: string | undefined private lastUnphasedAnswer: string | undefined private diagnostic: string | undefined private failure: CodexWireFailureFacts | undefined @@ -368,7 +369,7 @@ export class CodexAppServerWire { const output = this.collectOutput() if (output.length === 0) { this.recordFailure({ stage: 'turn', category: 'invalid-result' }) - throw new Error('subagent-codex: Codex completed without a final answer') + throw new Error('subagent-codex: Codex completed without an answer') } return { output, stopReason: 'completed' } } @@ -386,11 +387,13 @@ export class CodexAppServerWire { } /** - * The best non-commentary answer observed so far, preserving exact bytes. - * @returns the selected final or nullable-phase text block, if any. + * The best non-commentary answer observed so far, preserving exact bytes: + * the latest `final_answer`, else the latest `partial_answer`, else the + * latest nullable-phase message. + * @returns the selected text block, if any. */ collectOutput(): ContentBlock[] { - const selected = this.lastFinalAnswer ?? this.lastUnphasedAnswer + const selected = this.lastFinalAnswer ?? this.lastPartialAnswer ?? this.lastUnphasedAnswer return selected !== undefined && selected.trim().length > 0 ? [{ type: 'text', text: selected }] : [] @@ -667,6 +670,11 @@ export class CodexAppServerWire { : (() => { throw new Error('subagent-codex: app-server returned an invalid agent message') })() if (item.phase === 'final_answer') { this.lastFinalAnswer = text + } else if (item.phase === 'partial_answer') { + // `MessagePhase::PartialAnswer` (openai/codex codex-rs/protocol/src/models.rs, + // app-server 0.162+): stable answer text that later assistant output + // or tool calls may follow; it outranks only unphased text. + this.lastPartialAnswer = text } else if (item.phase === null) { this.lastUnphasedAnswer = text } else if (item.phase !== 'commentary') { diff --git a/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts b/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts index 529ef96199..80607bca6c 100644 --- a/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts +++ b/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts @@ -821,6 +821,55 @@ describe('CodexAppServerWire', () => { wire.close() }) + it('ranks final_answer above partial_answer above nullable-phase answers and never selects commentary', async () => { + const scenarios: Array<{ + readonly frames: JsonObject[] + readonly text: string + }> = [ + { + frames: [ + agentMessage('partial', 'partial_answer'), + agentMessage('final', 'final_answer'), + ], + text: 'final', + }, + { + frames: [ + agentMessage('final', 'final_answer'), + agentMessage('partial after final', 'partial_answer'), + ], + text: 'final', + }, + { + frames: [agentMessage('only partial', 'partial_answer')], + text: 'only partial', + }, + { + frames: [ + agentMessage('unphased', null), + agentMessage('commentary before partial', 'commentary'), + agentMessage('first partial', 'partial_answer'), + agentMessage('last partial', 'partial_answer'), + agentMessage('commentary after partial', 'commentary'), + agentMessage('unphased after partial', null), + ], + text: 'last partial', + }, + ] + for (const scenario of scenarios) { + const { child, wire } = await initializeWire() + const result = wire.runTurn(['task'], new AbortController().signal) + const turnStart = await child.peer.nextMethod('turn/start') + child.peer.respond(turnStart, { turn: { id: 'turn-1' } }) + child.peer.send(...scenario.frames, turnCompleted('completed')) + await expect(result).resolves.toEqual({ + output: [{ type: 'text', text: scenario.text }], + stopReason: 'completed', + }) + wire.close() + } + }) + it('groups representative string errors without changing stop reasons', async () => { const scenarios = [ ['contextWindowExceeded', 'limit', 'max-tokens'], @@ -958,7 +1007,7 @@ describe('CodexAppServerWire', () => { }> = [ { frames: [turnCompleted('completed')], - message: 'without a final answer', + message: 'without an answer', category: 'invalid-result', }, { @@ -967,7 +1016,7 @@ describe('CodexAppServerWire', () => { agentMessage(' \n ', 'final_answer'), turnCompleted('completed'), ], - message: 'without a final answer', + message: 'without an answer', category: 'invalid-result', }, { diff --git a/packages/subagent/subagent-dsh-sdk/package.json b/packages/subagent/subagent-dsh-sdk/package.json index ff61f6de97..67733db85d 100644 --- a/packages/subagent/subagent-dsh-sdk/package.json +++ b/packages/subagent/subagent-dsh-sdk/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent-dsh-sdk", "description": "Out-of-process SDK subagent backend: drives a child DeepSeek Harness runtime subprocess over stdio JSON-RPC through the TypeScript SDK client", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent-fork-in-process/package.json b/packages/subagent/subagent-fork-in-process/package.json index 1385911366..1bf4ea561a 100644 --- a/packages/subagent/subagent-fork-in-process/package.json +++ b/packages/subagent/subagent-fork-in-process/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent-fork-in-process", "description": "In-process fork subagent backend: runs a child agent seeded with a prefix of the parent's log", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent-spawn-in-process/package.json b/packages/subagent/subagent-spawn-in-process/package.json index 9a119d4f62..d34961cbc3 100644 --- a/packages/subagent/subagent-spawn-in-process/package.json +++ b/packages/subagent/subagent-spawn-in-process/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent-spawn-in-process", "description": "In-process spawn subagent backend: runs a fresh child agent on ctx.agents", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/subagent/package.json b/packages/subagent/subagent/package.json index f44e116b46..e324f19c17 100644 --- a/packages/subagent/subagent/package.json +++ b/packages/subagent/subagent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subagent", "description": "Abstract subagent seam (ctx.subagents): named-provider registry for delegating to child agents", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/tool-subagent-control/package.json b/packages/subagent/tool-subagent-control/package.json index cae25399d2..75e2d890e9 100644 --- a/packages/subagent/tool-subagent-control/package.json +++ b/packages/subagent/tool-subagent-control/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-subagent-control", "description": "Globally named send_message, interrupt_agent, and list_agents tools over ctx.subagents continuations", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subagent/tool-subagent/package.json b/packages/subagent/tool-subagent/package.json index eb22e9c882..1492dc1fd5 100644 --- a/packages/subagent/tool-subagent/package.json +++ b/packages/subagent/tool-subagent/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-subagent", "description": "Model-facing subagent delegation tool over the ctx.subagents seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subprocess/subprocess-local/package.json b/packages/subprocess/subprocess-local/package.json index a96cba4790..704671e842 100644 --- a/packages/subprocess/subprocess-local/package.json +++ b/packages/subprocess/subprocess-local/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subprocess-local", "description": "Local-subprocess implementation of the DeepSeek Harness subprocess seam", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subprocess/subprocess/package.json b/packages/subprocess/subprocess/package.json index 6d520270d6..baa2ad7700 100644 --- a/packages/subprocess/subprocess/package.json +++ b/packages/subprocess/subprocess/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-subprocess", "description": "Subprocess seam (ctx.subprocess) for the DeepSeek Harness \u2014 managed process groups, bounded spill-backed output, and escalated kills behind one abstract service", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/subprocess/win32-process/package.json b/packages/subprocess/win32-process/package.json index 87f10fbae8..cc948111f4 100644 --- a/packages/subprocess/win32-process/package.json +++ b/packages/subprocess/win32-process/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-win32-process", "description": "Shared low-level Win32 process, stdio, and Job Object primitives", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/telemetry/otel/package.json b/packages/telemetry/otel/package.json index 0f9a6c133a..f367f75d97 100644 --- a/packages/telemetry/otel/package.json +++ b/packages/telemetry/otel/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-otel", "description": "Cordis service for independent ordinary-event and byte-bounded Session-log OTLP channels", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/terminal/terminal-bash/package.json b/packages/terminal/terminal-bash/package.json index bec256dbd5..fe3bc06ed7 100644 --- a/packages/terminal/terminal-bash/package.json +++ b/packages/terminal/terminal-bash/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-terminal-bash", "description": "Persistent shell PTY backend over the DeepSeek Harness subprocess terminal primitive", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/terminal/terminal/package.json b/packages/terminal/terminal/package.json index e19b4a13e4..24eff07c8f 100644 --- a/packages/terminal/terminal/package.json +++ b/packages/terminal/terminal/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-terminal", "description": "Persistent PTY session seam for the DeepSeek Harness — owner-scoped ids, backend registry, interactive sends, reads, signals, and awaited cleanup", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/agent-loop-testkit/package.json b/packages/test-support/agent-loop-testkit/package.json index f598721f67..98b49ad354 100644 --- a/packages/test-support/agent-loop-testkit/package.json +++ b/packages/test-support/agent-loop-testkit/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-agent-loop-testkit", "description": "Prerequisite mounting, production AgentLoop drivers, and Inbox stubs for tests", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/client-runtime/package.json b/packages/test-support/client-runtime/package.json index 0de1d4b273..cacc342816 100644 --- a/packages/test-support/client-runtime/package.json +++ b/packages/test-support/client-runtime/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-client-test-runtime", "description": "Browser test runtimes: a jsdom slot bench with test-owned Session and Workspace doubles, and a whole-client tier that boots the web roster through the production bootClient over an endpoint-named Remote mock", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/llm-mock-server/package.json b/packages/test-support/llm-mock-server/package.json index ad14114e6a..fd7f12ab75 100644 --- a/packages/test-support/llm-mock-server/package.json +++ b/packages/test-support/llm-mock-server/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-mock-server", "description": "Scriptable Messages HTTP/SSE fault server for LLM recovery tests", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/llm-replay/package.json b/packages/test-support/llm-replay/package.json index 649afb4206..616e1f297c 100644 --- a/packages/test-support/llm-replay/package.json +++ b/packages/test-support/llm-replay/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-llm-replay", "description": "Replay LLM plugin: short-circuits llm/stream with model chunks reconstructed from a recorded session JSONL (keyless snapshot tests)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/loader-smoke/package.json b/packages/test-support/loader-smoke/package.json index 580893e93a..824bdaa0d2 100644 --- a/packages/test-support/loader-smoke/package.json +++ b/packages/test-support/loader-smoke/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-loader-smoke", "description": "Shared subprocess and direct-agent harness for keyless real-Loader example smoke tests", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/remote-mock/package.json b/packages/test-support/remote-mock/package.json index 5ad0f57366..60f2089b27 100644 --- a/packages/test-support/remote-mock/package.json +++ b/packages/test-support/remote-mock/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-remote-mock", "description": "Endpoint-named mock for Typert Remote traffic: unary answers and stream scripts per /, live stream control, a log, and the Connection carrier face whole-client specs install", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/test-support/session-snapshot/package.json b/packages/test-support/session-snapshot/package.json index 96a6b58efd..e1ea5f569b 100644 --- a/packages/test-support/session-snapshot/package.json +++ b/packages/test-support/session-snapshot/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-session-snapshot", "description": "Session-log snapshot core with an ACP protocol adapter, expected-output normalization, and fixture invariants", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/todo/tool-todo/package.json b/packages/todo/tool-todo/package.json index 0f13e4b1a3..bc4568a6bd 100644 --- a/packages/todo/tool-todo/package.json +++ b/packages/todo/tool-todo/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-todo", "description": "Model-facing todo_write tool over the DeepSeek Harness event-sourced session log", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/typert/generator/package.json b/packages/typert/generator/package.json index 4b682f8dfd..4f4c55c598 100644 --- a/packages/typert/generator/package.json +++ b/packages/typert/generator/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-typert-generator", "description": "TypeScript project analyzer and model-driven Typert artifact generator", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/typert/loader/package.json b/packages/typert/loader/package.json index 68da66a367..bebda1ae55 100644 --- a/packages/typert/loader/package.json +++ b/packages/typert/loader/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-typert-loader", "description": "Loader integration for generated Typert package contributions", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/typert/protocol/package.json b/packages/typert/protocol/package.json index b81beb7f59..119969449e 100644 --- a/packages/typert/protocol/package.json +++ b/packages/typert/protocol/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-typert-protocol", "description": "Compiler-independent Remote metadata and Typert provider protocols", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/typert/registry/package.json b/packages/typert/registry/package.json index 047655e238..d318be92b6 100644 --- a/packages/typert/registry/package.json +++ b/packages/typert/registry/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-typert-registry", "description": "Runtime registry for generated package reflection and Zod schemas", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/atomic-write/package.json b/packages/util/atomic-write/package.json index 9ca02587b2..5c9f6ee0bd 100644 --- a/packages/util/atomic-write/package.json +++ b/packages/util/atomic-write/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-atomic-write", "description": "Zero-dependency atomic file replacement: exclusive-create random-suffix temp + rename carrying the caller-stated permissions (writeFileAtomic)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/brand/package.json b/packages/util/brand/package.json index 68b5ac5aa5..f2f958e8fa 100644 --- a/packages/util/brand/package.json +++ b/packages/util/brand/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-brand", "description": "Stateless branded primitive types for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/chunked-list/package.json b/packages/util/chunked-list/package.json index 698de00de2..06d0b57185 100644 --- a/packages/util/chunked-list/package.json +++ b/packages/util/chunked-list/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-chunked-list", "description": "Persistent append-only chunked lists with bounded copying and JSON checkpoint validation", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/code-language/package.json b/packages/util/code-language/package.json index 86289f391b..d51035cf18 100644 --- a/packages/util/code-language/package.json +++ b/packages/util/code-language/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-util-code-language", "description": "Single file-extension to syntax-highlighting language table shared by Client code surfaces and the Host read card", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/crypto/package.json b/packages/util/crypto/package.json index 9b1563dfe1..1db2c1519c 100644 --- a/packages/util/crypto/package.json +++ b/packages/util/crypto/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-util-crypto", "description": "Zero-dependency browser-safe UUID and byte-encoding helpers", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/deque/package.json b/packages/util/deque/package.json index 6b9a431a4c..d3aca83625 100644 --- a/packages/util/deque/package.json +++ b/packages/util/deque/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-deque", "description": "Zero-dependency circular deque with amortized constant-time end operations and bounded vacant storage", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/home-paths/package.json b/packages/util/home-paths/package.json index 23168f0cdd..92df4d3a42 100644 --- a/packages/util/home-paths/package.json +++ b/packages/util/home-paths/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-home-paths", "description": "Shared filesystem path helpers for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/http-proxy/package.json b/packages/util/http-proxy/package.json index 0ec53615b3..14109ff53d 100644 --- a/packages/util/http-proxy/package.json +++ b/packages/util/http-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-http-proxy", "description": "Process-wide outbound HTTP proxy policy for DeepSeek Harness: resolve it from the launch environment and install it as undici's global dispatcher", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/launch-environment/package.json b/packages/util/launch-environment/package.json index a819122171..7cfad74127 100644 --- a/packages/util/launch-environment/package.json +++ b/packages/util/launch-environment/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-launch-environment", "description": "Immutable DeepSeek Harness launch environment that records which layer supplied each value", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/lazy-require/package.json b/packages/util/lazy-require/package.json index e3e4085776..7925a55879 100644 --- a/packages/util/lazy-require/package.json +++ b/packages/util/lazy-require/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-lazy-require", "description": "Caller-relative, success-cached lazy loading for CommonJS-compatible Host dependencies", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/native-command/package.json b/packages/util/native-command/package.json index e31078f6ec..10a40a2e92 100644 --- a/packages/util/native-command/package.json +++ b/packages/util/native-command/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-native-command", "description": "Host-native command and path-opening utilities with shell-free execution, cancellation, desktop detection, and WSL handoff", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/output-retention/package.json b/packages/util/output-retention/package.json index 68dbc65e48..a0b2254976 100644 --- a/packages/util/output-retention/package.json +++ b/packages/util/output-retention/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-output-retention", "description": "Zero-dependency bounded-retention primitive: ItemRetainer/TextRetainer + neutral notice helpers (what did we keep, what did we omit)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/package-manifest/package.json b/packages/util/package-manifest/package.json index 6644b38c10..e3110015ca 100644 --- a/packages/util/package-manifest/package.json +++ b/packages/util/package-manifest/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-package-manifest", "description": "Shared type declarations for package.json.dsh configuration fields", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/plugin-market-index/package.json b/packages/util/plugin-market-index/package.json index 2e589eecfc..3d231e1f8a 100644 --- a/packages/util/plugin-market-index/package.json +++ b/packages/util/plugin-market-index/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-plugin-market-index", "description": "Plugin market index format 1: JSON validation, relative icon resolution, and pinned install arguments", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/time/package.json b/packages/util/time/package.json index 919dbc3f3b..7afb5c03b7 100644 --- a/packages/util/time/package.json +++ b/packages/util/time/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-util-time", "description": "Zero-dependency time vocabulary shared by wire boundaries: canonicalClientTimeZone (IANA zone validation and canonicalization only, no formatting)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/timeout/package.json b/packages/util/timeout/package.json index 38120798e7..cf867362c1 100644 --- a/packages/util/timeout/package.json +++ b/packages/util/timeout/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-timeout", "description": "Zero-dependency timeout/deadline primitive: clampTimeout, deadline, timeoutOf, TimeoutReason (timing + classification only, no termination)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/values/package.json b/packages/util/values/package.json index 0b33a64bb0..bf810acadc 100644 --- a/packages/util/values/package.json +++ b/packages/util/values/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-util-values", "description": "Duplicate-install-safe value primitives for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/util/workspace-path/package.json b/packages/util/workspace-path/package.json index 050349d85b..0531208362 100644 --- a/packages/util/workspace-path/package.json +++ b/packages/util/workspace-path/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-util-workspace-path", "description": "Browser-safe Workspace path and display helpers", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/web/tool-web/package.json b/packages/web/tool-web/package.json index 2914ad9f0e..8443aef20c 100644 --- a/packages/web/tool-web/package.json +++ b/packages/web/tool-web/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-web", "description": "Model-facing web tools (web_search, web_fetch) over the DeepSeek Harness web capability seam (ctx.web)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/web/web-fetch-http/package.json b/packages/web/web-fetch-http/package.json index 041d939bf4..6bdf64d84b 100644 --- a/packages/web/web-fetch-http/package.json +++ b/packages/web/web-fetch-http/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web-fetch-http", "description": "Anonymous public HTTP(S) fetch provider for the DeepSeek Harness web capability seam (ctx.web)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/web/web-search-deepseek/package.json b/packages/web/web-search-deepseek/package.json index d413c115ef..a796434338 100644 --- a/packages/web/web-search-deepseek/package.json +++ b/packages/web/web-search-deepseek/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web-search-deepseek", "description": "DeepSeek-backed search provider (native web_search via the Anthropic-compatible API) for the DeepSeek Harness web capability seam (ctx.web)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/web/web-search-exa/package.json b/packages/web/web-search-exa/package.json index 45281663a9..1d6def3660 100644 --- a/packages/web/web-search-exa/package.json +++ b/packages/web/web-search-exa/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web-search-exa", "description": "Exa-backed search provider for the DeepSeek Harness web capability seam (ctx.web)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/web/web-search-perplexity/package.json b/packages/web/web-search-perplexity/package.json index c86670fe76..63533ce079 100644 --- a/packages/web/web-search-perplexity/package.json +++ b/packages/web/web-search-perplexity/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web-search-perplexity", "description": "Perplexity-backed search provider for the DeepSeek Harness web capability seam (ctx.web)", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/web/web/package.json b/packages/web/web/package.json index 6086a4d70d..50dbf461e0 100644 --- a/packages/web/web/package.json +++ b/packages/web/web/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-web", "description": "Abstract web access capability seam (ctx.web) for the DeepSeek Harness — search/fetch provider registry, registration-order-independent selection, request/result vocabulary, and the WebError taxonomy", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/workflow/tool-workflow/package.json b/packages/workflow/tool-workflow/package.json index 7c76199bc1..5ec803ebf6 100644 --- a/packages/workflow/tool-workflow/package.json +++ b/packages/workflow/tool-workflow/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-tool-workflow", "description": "Model-facing workflow tool: run a JavaScript orchestration script over ctx.workflowEngine", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/workflow/workflow-ptc/package.json b/packages/workflow/workflow-ptc/package.json index e3616dbab0..9bf1c83d51 100644 --- a/packages/workflow/workflow-ptc/package.json +++ b/packages/workflow/workflow-ptc/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-workflow-ptc", "description": "Workflow orchestration in the shared sandboxed Node PTC runtime", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/workflow/workflow/package.json b/packages/workflow/workflow/package.json index 1a77bbce2a..82f22b6165 100644 --- a/packages/workflow/workflow/package.json +++ b/packages/workflow/workflow/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-workflow", "description": "Workflow capability seam: ctx.workflowEngine service, run vocabulary, and workflow/* events", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/packages/workspace/workspace/package.json b/packages/workspace/workspace/package.json index d56e00829c..982d73cfee 100644 --- a/packages/workspace/workspace/package.json +++ b/packages/workspace/workspace/package.json @@ -1,7 +1,7 @@ { "name": "@deepseek-ai/dsh-workspace", "description": "Workspace entity registry (ctx.workspaceRegistry): durable workspace records with validated session attachment over the domain data form for the DeepSeek Harness", - "version": "0.2.1-alpha.3", + "version": "0.2.1-alpha.4", "publishConfig": { "access": "public" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5de58cc2e..c5eab46400 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1706,6 +1706,9 @@ importers: '@deepseek-ai/dsh-brand': specifier: workspace:* version: link:../../util/brand + '@deepseek-ai/dsh-client-connection': + specifier: workspace:* + version: link:../../client/connection '@deepseek-ai/dsh-client-store': specifier: workspace:* version: link:../../client/store diff --git a/snapshots/web/goal-multi-turn-actions/ui-expanded.expected.md b/snapshots/web/goal-multi-turn-actions/ui-expanded.expected.md index f1729da55e..ee5f3347fd 100644 --- a/snapshots/web/goal-multi-turn-actions/ui-expanded.expected.md +++ b/snapshots/web/goal-multi-turn-actions/ui-expanded.expected.md @@ -23,7 +23,7 @@ - 'button "Think There''s a `packages` directory. Let me look inside to see what packages are available."' - button "Bash List packages directory contents" - button "Think There are 4 packages:" -- button "Failed Bash Randomly pick one package directory" +- button "Failed Bash Randomly pick one package directory exit code 127" - 'button "Think shuf is not available on macOS. I''ll use a different approach for random selection. Let me use $RANDOM in bash or use `sort -R` or Python."' - button "Bash Randomly select one package with Python" - 'button "Think The random pick for turn 1 is `packages/context/session-reference`. Now I''ll output its file structure. Let me get the file tree."'