Merge remote-tracking branch 'origin/master' into web2-todo
# Conflicts: # .agents/notes/implemented/feature/2026-06-29-todo-write-tool.i18n.yaml # .agents/notes/implemented/feature/2026-06-29-todo-write-tool.md # .agents/notes/implemented/feature/2026-06-29-todo-write-tool.zh.md # apps/web/tests/smoke-fixture.e2e.ts # examples/acp-agent/tests/snapshots/model-switching/tool-schemas.expected.json # examples/acp-agent/tests/snapshots/permission-switching/tool-schemas.expected.json # examples/acp-agent/tests/snapshots/plan-mode/tool-schemas.expected.json # packages/client/runtime/README.md # packages/client/runtime/src/client/index.ts # packages/client/runtime/src/client/sessions/conversation.ts # packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx # packages/client/ui-conversation/tests/chat-stats-bash-sample.spec.tsx # packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx # packages/client/ui-conversation/tests/chat-view.spec.tsx # packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx # packages/client/ui-conversation/tests/skeleton-branches.spec.tsx # packages/client/ui-conversation/tests/skeleton.spec.tsx # packages/todo/tool-todo/README.md
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
|
||||
}
|
||||
|
||||
@@ -122,7 +131,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}
|
||||
/>
|
||||
)
|
||||
@@ -132,6 +141,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}
|
||||
@@ -145,6 +155,8 @@ function mount(slots: SlotsService, nodes: ConversationSnapshot['nodes'] = NODES
|
||||
send={vi.fn()}
|
||||
stop={vi.fn()}
|
||||
open={vi.fn()}
|
||||
updateSessionPrompt={vi.fn()}
|
||||
retrySessionPrompt={vi.fn()}
|
||||
/>,
|
||||
)
|
||||
}
|
||||
@@ -174,7 +186,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