refactor(gui): projection keys import the domain packages' client outlets (single source)
The consumer-side restated declare-merges retire (user ruling: one home per projection key): TodoPanel imports the todos merge and TodoItem through @deepseek-ai/dsh-tool-todo/client, and the manager takes the title merge through @deepseek-ai/dsh-session-title/client — both pure-type outlets re-exporting the domain's single-source types.ts, so no host value import or Context merge enters the client program (type-only edges, exempt from the plugin value-import ban). Workspace deps and tsconfig references added. Also aligns the fixture's empty-log tail block with the host convention (asOfSeq -1 with empty values, block always present on tail requests).
This commit is contained in:
@@ -652,9 +652,9 @@ export function createFixtureApi(options: FixtureOptions = {}): ApiProxy {
|
|||||||
// Snapshot at request time, deliver after the transit delay (mirrors a real host under latency).
|
// Snapshot at request time, deliver after the transit delay (mirrors a real host under latency).
|
||||||
const page = pageOf(log, request.payload.beforeSeq, request.payload.maxMessages ?? 50)
|
const page = pageOf(log, request.payload.beforeSeq, request.payload.maxMessages ?? 50)
|
||||||
// Tail page carries the projections block (host parallel: one consistent
|
// Tail page carries the projections block (host parallel: one consistent
|
||||||
// cut over the registered units, asOfSeq = window tail seq); an empty
|
// cut over the registered units; asOfSeq = window tail seq, -1 on an
|
||||||
// log has no cut to stamp, so the block stays absent.
|
// empty log — the host's session.seq-1 convention).
|
||||||
const projections = request.payload.beforeSeq === undefined && log.length > 0
|
const projections = request.payload.beforeSeq === undefined
|
||||||
? { asOfSeq: log.length - 1, values: projectionValuesOf(log) }
|
? { asOfSeq: log.length - 1, values: projectionValuesOf(log) }
|
||||||
: undefined
|
: undefined
|
||||||
const doomed = failNextHistory
|
const doomed = failNextHistory
|
||||||
|
|||||||
@@ -65,10 +65,11 @@ describe('createFixtureApi', () => {
|
|||||||
const clamped = await api.sessions.history(req({ sessionId: sid('fx-alpha'), beforeSeq: -5, maxMessages: 10 }))
|
const clamped = await api.sessions.history(req({ sessionId: sid('fx-alpha'), beforeSeq: -5, maxMessages: 10 }))
|
||||||
if (!clamped.result.ok) throw new Error('clamped failed')
|
if (!clamped.result.ok) throw new Error('clamped failed')
|
||||||
expect(clamped.result.value.events).toEqual([])
|
expect(clamped.result.value.events).toEqual([])
|
||||||
// Unknown session: empty page, not an error (history of a bare id).
|
// Unknown session: empty page, not an error (history of a bare id). The
|
||||||
|
// tail block still rides it — empty-log cut at -1, the host convention.
|
||||||
const empty = await api.sessions.history(req({ sessionId: sid('no-such'), maxMessages: 10 }))
|
const empty = await api.sessions.history(req({ sessionId: sid('no-such'), maxMessages: 10 }))
|
||||||
if (!empty.result.ok) throw new Error('empty failed')
|
if (!empty.result.ok) throw new Error('empty failed')
|
||||||
expect(empty.result.value).toEqual({ events: [], hasMore: false })
|
expect(empty.result.value).toEqual({ events: [], hasMore: false, projections: { asOfSeq: -1, values: {} } })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits the todo/write snapshot at the real tool boundary: between tool/call and tool/result, timestamps monotonic', async () => {
|
it('emits the todo/write snapshot at the real tool boundary: between tool/call and tool/result, timestamps monotonic', async () => {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||||
"@deepseek-ai/dsh-session": "workspace:^",
|
"@deepseek-ai/dsh-session": "workspace:^",
|
||||||
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-session-title": "workspace:^",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"zustand": "~4.4.7"
|
"zustand": "~4.4.7"
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import { transportError } from '@deepseek-ai/dsh-host-apiproxy/api'
|
|||||||
import { mergeOrderedBaseline } from '../ordered-baseline.ts'
|
import { mergeOrderedBaseline } from '../ordered-baseline.ts'
|
||||||
import type { SessionListEntry, TitledSessionSummary } from './lineage.ts'
|
import type { SessionListEntry, TitledSessionSummary } from './lineage.ts'
|
||||||
import { flattenLineage } from './lineage.ts'
|
import { flattenLineage } from './lineage.ts'
|
||||||
|
// Type-only merge edge: the title domain's client-namespace outlet declares
|
||||||
|
// the 'title' projection key this manager projects into list rows (and any
|
||||||
|
// useProjection('title') consumer reads). Zero value imports by construction.
|
||||||
|
import type {} from '@deepseek-ai/dsh-session-title/client'
|
||||||
import { Notifier } from './notifier.ts'
|
import { Notifier } from './notifier.ts'
|
||||||
import { ProjectionValueStore } from './projection-store.ts'
|
import { ProjectionValueStore } from './projection-store.ts'
|
||||||
import { Session } from './session.ts'
|
import { Session } from './session.ts'
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
{
|
{
|
||||||
"path": "../../session-projection/session-projection"
|
"path": "../../session-projection/session-projection"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "../../session-title/session-title"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "../../llm/llm"
|
"path": "../../llm/llm"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||||
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-todo": "workspace:^",
|
||||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
||||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||||
"@deepseek-ai/dsh-client-ui-slash": "workspace:^",
|
"@deepseek-ai/dsh-client-ui-slash": "workspace:^",
|
||||||
|
|||||||
@@ -8,18 +8,11 @@
|
|||||||
import { useId, useState } from 'react'
|
import { useId, useState } from 'react'
|
||||||
import type { Context } from 'cordis'
|
import type { Context } from 'cordis'
|
||||||
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||||
import type { TodoItem } from '@deepseek-ai/dsh-client-runtime/client'
|
// The domain's client-namespace pure-type outlet: one import edge delivers
|
||||||
|
// the `todos` projection-key merge (single source, no consumer-side restated
|
||||||
// Client-side view of the todos projection key. The authoritative merge lives
|
// declare) and the payload type. Type-only by construction — the outlet is
|
||||||
// with the domain host unit (tool-todo), whose program never overlaps the
|
// free of host value imports, so no host Context merge enters this program.
|
||||||
// client's, so this consumer restates the identical member through the same
|
import type { TodoItem } from '@deepseek-ai/dsh-tool-todo/client'
|
||||||
// pure-type outlet (any program holding both merges rejects drift).
|
|
||||||
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
||||||
interface SessionProjectionMap {
|
|
||||||
/** The agent's current whole todo list (latest `todo/write` snapshot), or `null` before the first write. */
|
|
||||||
todos: TodoItem[] | null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
import { IconChevronDownOutline14, IconChevronUpOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
import { IconChevronDownOutline14, IconChevronUpOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||||
import css from './TodoPanel.module.css'
|
import css from './TodoPanel.module.css'
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
{
|
{
|
||||||
"path": "../../session-projection/session-projection"
|
"path": "../../session-projection/session-projection"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "../../todo/tool-todo"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "../ui-slash"
|
"path": "../ui-slash"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user