Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input
# Conflicts: # apps/cli/README.md # apps/cli/src/web.ts # apps/web/tests/smoke-fixture.e2e.ts # docs/architecture.i18n.yaml # docs/module-graph.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl # packages/client/connection/src/client/api.ts # packages/client/connection/src/client/fixture.ts # packages/client/runtime/README.md # packages/client/runtime/src/client/index.ts # packages/client/runtime/src/client/sessions/conversation.ts # packages/client/runtime/src/client/sessions/service.ts # packages/client/runtime/src/client/sessions/session.ts # packages/client/ui-conversation/src/client/apply.ts # packages/client/ui-conversation/src/client/contract/slots.ts # packages/client/ui-conversation/src/client/index.ts # packages/client/ui-conversation/src/client/service.ts # packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx # packages/client/ui-conversation/src/client/skeleton/EmptyState.tsx # packages/client/ui-conversation/src/client/skeleton/InputBar.tsx # packages/client/ui-conversation/src/client/stores.ts # packages/client/ui-conversation/tests/apply-inject.spec.tsx # packages/client/ui-conversation/tests/service-orchestration.spec.ts # packages/client/ui-conversation/tests/skeleton-branches.spec.tsx # packages/client/ui-conversation/tests/skeleton.spec.tsx # packages/cordis/tool-cordis/src/api-catalog.ts # packages/host/apiproxy/package.json # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/sessions.schema.ts # packages/host/runtime/package.json # packages/host/runtime/src/boot.ts # packages/host/runtime/tests/host-runtime.spec.ts # packages/host/runtime/tsconfig.json # packages/host/webserver/README.md # packages/host/webserver/src/index.ts # packages/host/webserver/tests/webserver.spec.ts # packages/llm/llm-pi-ai/tests/convert.spec.ts # packages/ui/acp/src/codec.ts # packages/ui/acp/tests/codec.spec.ts # pnpm-lock.yaml
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
/**
|
||||
* Trajectory/Waterfall plugin, browser half: contributes the two placeholder
|
||||
* views into the conversation view ring (the 'conversation.view' list slot
|
||||
* declared by ui-conversation). Pure consumer — no ctx service, no Context
|
||||
* declaration merge; the minimal-plugin exemplar. Contract: api-contracts v3
|
||||
* section 8.
|
||||
* Browser trajectory plugin contributing two entries to the conversation
|
||||
* view slot without defining a service.
|
||||
*/
|
||||
import type { Context } from 'cordis'
|
||||
// Type-only: the 'conversation.view' SlotMap row (declared by the slot's
|
||||
@@ -24,8 +21,7 @@ export const inject = ['slots', 'conversation']
|
||||
/**
|
||||
* Client plugin body: register the trajectory and waterfall view tabs. The
|
||||
* registrations ride the slot service's effect wrapper (plugin unload
|
||||
* removes both tabs). Trajectory owns its turn list in-body; Waterfall keeps
|
||||
* the span stats header inside its body (chrome attachment retired).
|
||||
* removes both tabs).
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
/**
|
||||
* Trajectory plugin, node half. Pure UI plugin: the empty apply exists so
|
||||
* the plugin appears in the host cordis.yml / Loader (load and lifecycle
|
||||
* follow the host; the browser half ships via exports["./client"], discovered
|
||||
* through the package.json dshClient declaration). Contract: api-contracts
|
||||
* v3 sections 0.3 and 8.
|
||||
*/
|
||||
/** Host loader entry for the browser-only trajectory plugin. */
|
||||
|
||||
/** Host plugin body — no host-side behavior for the trajectory plugin. */
|
||||
/** Provides no host-side behavior. */
|
||||
export function apply(): void {}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { ConversationSnapshot, SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { ConversationSnapshot, SessionId, SessionListState, WorkspaceListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { ConvViewProps, ViewTab } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
// Export discipline: packages/client/AGENTS.md.
|
||||
import { ConversationRoot, type ConversationRootProps } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/ConversationRoot.tsx'
|
||||
@@ -59,10 +59,18 @@ function fakeSession(nodes: ConversationSnapshot['nodes']) {
|
||||
return { store, useSession: bindSnapshotSelector(store) as unknown as UseSession<ConversationSnapshot> }
|
||||
}
|
||||
|
||||
/** Empty sessions-list hook stub (breadcrumbs fall back to the raw id; engines carry no hook since the store migration — bind here). */
|
||||
/** Empty sessions-list hook; breadcrumbs therefore fall back to the raw id. */
|
||||
function emptySessions() {
|
||||
const store = createSnapshotStore<SessionListState>(
|
||||
{ ids: [], byId: {}, current: undefined } as SessionListState)
|
||||
{ ids: [], byId: {}, current: undefined, intent: undefined, phase: 'ready' })
|
||||
return bindSnapshotSelector(store)
|
||||
}
|
||||
|
||||
function emptyWorkspaces() {
|
||||
const store = createSnapshotStore<WorkspaceListState>({
|
||||
items: [], intent: undefined, state: 'idle', phase: 'ready', error: null, baselinesReady: true,
|
||||
recentWorkspaceId: undefined,
|
||||
})
|
||||
return bindSnapshotSelector(store)
|
||||
}
|
||||
|
||||
@@ -75,6 +83,7 @@ function standaloneProps(nodes: ConversationSnapshot['nodes']): ConvViewProps {
|
||||
sessionId: SID,
|
||||
useSession: fakeSession(nodes).useSession,
|
||||
useSessions: emptySessions(),
|
||||
useWorkspaces: emptyWorkspaces(),
|
||||
} as unknown as ConvViewProps
|
||||
}
|
||||
|
||||
@@ -121,7 +130,7 @@ function mount(slots: SlotsService, nodes: ConversationSnapshot['nodes'] = NODES
|
||||
const View = entry.component as FC<ConvViewProps>
|
||||
return (
|
||||
<View
|
||||
{...({ sessionId: SID, useSession, useSessions: emptySessions() } as unknown as ConvViewProps)}
|
||||
{...({ sessionId: SID, useSession, useSessions: emptySessions(), useWorkspaces: emptyWorkspaces() } as unknown as ConvViewProps)}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
@@ -131,6 +140,7 @@ function mount(slots: SlotsService, nodes: ConversationSnapshot['nodes'] = NODES
|
||||
sessionId={SID}
|
||||
useSession={useSession}
|
||||
useSessions={emptySessions()}
|
||||
useWorkspaces={emptyWorkspaces()}
|
||||
useStore={bindSnapshotSelector(chat)}
|
||||
actions={chat.actions}
|
||||
renderSlot={renderSlot}
|
||||
@@ -148,6 +158,8 @@ function mount(slots: SlotsService, nodes: ConversationSnapshot['nodes'] = NODES
|
||||
send={vi.fn()}
|
||||
stop={vi.fn()}
|
||||
open={vi.fn()}
|
||||
updateSessionPrompt={vi.fn()}
|
||||
retrySessionPrompt={vi.fn()}
|
||||
/>,
|
||||
)
|
||||
}
|
||||
@@ -177,7 +189,6 @@ describe('tab switching in ConversationRoot', () => {
|
||||
expect(screen.getAllByRole('tab').map((t) => t.textContent)).toEqual(['Chat', 'Trajectory', 'Waterfall'])
|
||||
|
||||
fireEvent.click(screen.getByRole('tab', { name: 'Trajectory' }))
|
||||
// Trajectory no longer mounts the span stats bar; the turn-list chrome owns the body.
|
||||
expect(screen.queryByText(/turns ·/)).toBeNull()
|
||||
expect(screen.getByText('Turn 1')).toBeTruthy()
|
||||
expect(screen.getByText('Turn 2')).toBeTruthy()
|
||||
|
||||
Reference in New Issue
Block a user