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:
imccyu
2026-07-27 22:32:36 +08:00
parent dcd9789226
commit a91f908e11
10 changed files with 23 additions and 17 deletions

View File

@@ -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).
const page = pageOf(log, request.payload.beforeSeq, request.payload.maxMessages ?? 50)
// Tail page carries the projections block (host parallel: one consistent
// cut over the registered units, asOfSeq = window tail seq); an empty
// log has no cut to stamp, so the block stays absent.
const projections = request.payload.beforeSeq === undefined && log.length > 0
// cut over the registered units; asOfSeq = window tail seq, -1 on an
// empty log — the host's session.seq-1 convention).
const projections = request.payload.beforeSeq === undefined
? { asOfSeq: log.length - 1, values: projectionValuesOf(log) }
: undefined
const doomed = failNextHistory

View File

@@ -65,10 +65,11 @@ describe('createFixtureApi', () => {
const clamped = await api.sessions.history(req({ sessionId: sid('fx-alpha'), beforeSeq: -5, maxMessages: 10 }))
if (!clamped.result.ok) throw new Error('clamped failed')
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 }))
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 () => {