Merge remote-tracking branch 'origin/master' into worktree/web-background-tasks-display-258f7e
# Conflicts: # docs/subsystems/tasks.i18n.yaml # docs/subsystems/tasks.md # docs/subsystems/tasks.zh.md # packages/host/apiproxy/README.i18n.yaml # packages/host/apiproxy/README.md # packages/host/apiproxy/README.zh.md # packages/host/apiproxy/src/api-proxy.ts # packages/tasks/tasks-local/src/index.ts # packages/tasks/tasks/README.i18n.yaml # packages/tasks/tasks/README.md # packages/tasks/tasks/README.zh.md # packages/tasks/tasks/src/index.ts
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-client-ui-subagent",
|
||||
"description": "Subagent conversation catalog, continuation routing UI, and '@' reference source",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"version": "0.0.1-rc.1",
|
||||
"publishConfig": {
|
||||
"access": "restricted"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
||||
"directory": "packages/client/ui-subagent"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
@@ -22,15 +29,17 @@
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dshClient": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-locale",
|
||||
"@deepseek-ai/dsh-client-runtime",
|
||||
"@deepseek-ai/dsh-client-ui-conversation",
|
||||
"@deepseek-ai/dsh-client-ui-primitives",
|
||||
"@deepseek-ai/dsh-client-ui-slash"
|
||||
],
|
||||
"platform": "web"
|
||||
"dsh": {
|
||||
"client": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-locale",
|
||||
"@deepseek-ai/dsh-client-runtime",
|
||||
"@deepseek-ai/dsh-client-ui-conversation",
|
||||
"@deepseek-ai/dsh-client-ui-primitives",
|
||||
"@deepseek-ai/dsh-client-ui-slash"
|
||||
],
|
||||
"platform": "web"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"bundle": "tsdown",
|
||||
@@ -41,16 +50,16 @@
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-runtime": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-slash": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1",
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-token-meter": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slash": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -64,7 +73,7 @@
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
max-height: min(560px, calc(100vh - 140px));
|
||||
padding: 4px;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--dsw-alias-border-l2);
|
||||
border-radius: 12px;
|
||||
background: var(--dsw-specific-menu);
|
||||
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
||||
|
||||
@@ -519,8 +519,14 @@ export function SubagentCatalogAction({
|
||||
observedCatalogs.current.clear()
|
||||
}, [])
|
||||
|
||||
// Visibility needs evidence of children (entries, summary-known descendants,
|
||||
// or a failed load worth retrying). A bare loading catalog is not evidence:
|
||||
// selecting any session schedules a refresh whose loading snapshot would
|
||||
// otherwise flash the action in and out on childless sessions.
|
||||
const visible = presentedCatalog !== undefined
|
||||
&& (presentedCatalog.state !== 'ready' || presentedCatalog.entries.length > 0)
|
||||
&& (presentedCatalog.state === 'error'
|
||||
|| presentedCatalog.entries.length > 0
|
||||
|| descendantCount > 0)
|
||||
useEffect(() => {
|
||||
if (visible || !open) return
|
||||
setOpen(false)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Subagent reference plugin, node half. Pure UI plugin: the empty apply
|
||||
* exists so the plugin appears in the host cordis.yml / Loader; the browser
|
||||
* half ships via exports["./client"], discovered through the package.json
|
||||
* dshClient declaration.
|
||||
* dsh.client declaration.
|
||||
*/
|
||||
|
||||
/** Host plugin body — no host-side behavior for this source plugin. */
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
/* jscpd:ignore-start */
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-client-ui-subagent'
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* projections. Direct driving is deliberate: this spec owns only the
|
||||
* source's own contract.
|
||||
*/
|
||||
import { Context } from 'cordis'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
SlotsService, type ConversationSnapshot, type SessionId, type SessionListState,
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
import type { ComposerChainProps } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { SlashService } from '@deepseek-ai/dsh-client-ui-slash/client'
|
||||
import type { ClientSessionContext, SlashSource } from '@deepseek-ai/dsh-client-ui-slash/client'
|
||||
import { apply as applyLocale } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { apply as applyLocale, inject as localeInject } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import {
|
||||
SubagentCatalogAction, type SubagentCatalogInjected,
|
||||
} from '../src/client/SubagentCatalogAction.tsx'
|
||||
@@ -86,8 +86,9 @@ async function fullBench(sessions: SessionSummary[]) {
|
||||
const face = sessionsWith(sessions)
|
||||
ctx.provide('slash', { registerSource: (src: SlashSource) => { captured = src; return () => {} } })
|
||||
ctx.provide('sessions', face)
|
||||
ctx.provide('connection', { api: { settings: {} }, isLoopback: false } as never)
|
||||
await provideSlotFaces(ctx)
|
||||
await ctx.plugin({ inject: ['slots'], apply: applyLocale }).await()
|
||||
await ctx.plugin({ inject: localeInject, apply: applyLocale }).await()
|
||||
await ctx.plugin({ inject: [...inject], apply }).await()
|
||||
return { source: captured!, face, ctx }
|
||||
}
|
||||
@@ -121,8 +122,9 @@ describe('apply', () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SlashService).await()
|
||||
ctx.provide('sessions', sessionsWith(FAMILY))
|
||||
ctx.provide('connection', { api: { settings: {} }, isLoopback: false } as never)
|
||||
await provideSlotFaces(ctx)
|
||||
await ctx.plugin({ inject: ['slots'], apply: applyLocale }).await()
|
||||
await ctx.plugin({ inject: localeInject, apply: applyLocale }).await()
|
||||
const fiber = ctx.plugin({ inject: [...inject], apply })
|
||||
await fiber.await()
|
||||
const slash = ctx.get('slash') as SlashService
|
||||
|
||||
@@ -523,22 +523,23 @@ describe('SubagentCatalogAction', () => {
|
||||
expect(staleEmpty.openChild).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('renders empty loading and fallback error states without focusable rows', async () => {
|
||||
it('hides a bare loading catalog and keeps the error fallback without focusable rows', async () => {
|
||||
// Selecting any session schedules a catalog refresh; a loading snapshot
|
||||
// with no other evidence of children must not flash the action in.
|
||||
const loading = props(catalog({ entries: [], state: 'loading' }))
|
||||
const view = render(<SubagentCatalogAction {...loading} />)
|
||||
const trigger = screen.getByRole('button', { name: /0 个子代理/ })
|
||||
fireEvent.click(trigger)
|
||||
expect(screen.getByText('正在加载子代理…')).toBeTruthy()
|
||||
fireEvent.keyDown(trigger, { key: 'ArrowDown' })
|
||||
await Promise.resolve()
|
||||
expect(screen.getByRole('tree')).toBeTruthy()
|
||||
fireEvent.keyDown(screen.getByRole('tree'), { key: 'ArrowUp' })
|
||||
expect(screen.queryByRole('button')).toBeNull()
|
||||
view.unmount()
|
||||
|
||||
const failed = props(catalog({ entries: [], state: 'error', error: null }))
|
||||
render(<SubagentCatalogAction {...failed} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: /0 个子代理/ }))
|
||||
const trigger = screen.getByRole('button', { name: /0 个子代理/ })
|
||||
fireEvent.click(trigger)
|
||||
expect(screen.getByText('无法加载子代理')).toBeTruthy()
|
||||
fireEvent.keyDown(trigger, { key: 'ArrowDown' })
|
||||
await Promise.resolve()
|
||||
expect(screen.getByRole('tree')).toBeTruthy()
|
||||
fireEvent.keyDown(screen.getByRole('tree'), { key: 'ArrowUp' })
|
||||
})
|
||||
|
||||
it('navigates from outside the tree and tolerates a deferred focus after unmount', async () => {
|
||||
|
||||
Reference in New Issue
Block a user