From 027cbdfe5d50cf91b2c7a6ddd2763c1a760b007d Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:11:08 +0800 Subject: [PATCH] refactor(faces): keep the generated contributions out of the Host aggregate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gateway and the carrier each compiled both halves under one tsconfig, so the Host aggregate built their browser faces — including the face that owns `ctx.remote`, the most likely future consumer of a generated `/remote` contribution. Both packages now expose a host and a client face, and each aggregate references only its own; three modules the halves share appear in both file lists, as api/remotes already does. The two apps/web specs in the Host aggregate restate the conversation engine's Context key format instead of importing the Client runtime for it. A drift makes the key miss its rendered node, so the assertion fails loudly. The Host aggregate now reaches one Client project, the carrier's host face, which the Gateway's own dispatch face needs; no generated contribution is reachable from it. --- .../tests/chat-continuous-conversation.e2e.ts | 3 +- apps/web/tests/chat-long-interactions.e2e.ts | 3 +- apps/web/tests/support.ts | 14 ++++++ packages/api/gateway/tsconfig.client.json | 22 +++++++++ packages/api/gateway/tsconfig.host.json | 30 ++++++++++++ packages/api/gateway/tsconfig.json | 22 ++------- packages/api/remotes/tsconfig.client.json | 5 +- .../client/connection/tsconfig.client.json | 49 +++++++++++++++++++ packages/client/connection/tsconfig.host.json | 33 +++++++++++++ packages/client/connection/tsconfig.json | 41 ++-------------- tsconfig.client.json | 19 +++---- tsconfig.host.json | 10 +--- 12 files changed, 170 insertions(+), 81 deletions(-) create mode 100644 packages/api/gateway/tsconfig.client.json create mode 100644 packages/api/gateway/tsconfig.host.json create mode 100644 packages/client/connection/tsconfig.client.json create mode 100644 packages/client/connection/tsconfig.host.json diff --git a/apps/web/tests/chat-continuous-conversation.e2e.ts b/apps/web/tests/chat-continuous-conversation.e2e.ts index a7b659dab2..cd15f2e054 100644 --- a/apps/web/tests/chat-continuous-conversation.e2e.ts +++ b/apps/web/tests/chat-continuous-conversation.e2e.ts @@ -12,14 +12,13 @@ import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest' import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm' import type { ReplayEntry, ReplayOverrideDoc } from '@deepseek-ai/dsh-llm-replay' import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session' -import { conversationContextKey } from '@deepseek-ai/dsh-client-runtime/client' import { launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold, } from './scaffold.ts' -import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' +import { connectFreshWorkspace, conversationContextKey, newEnglishPage, saveFailureShot } from './support.ts' const MODE = webSnapshotMode() const TURN_COUNT = 12 diff --git a/apps/web/tests/chat-long-interactions.e2e.ts b/apps/web/tests/chat-long-interactions.e2e.ts index aca85146b5..58d97e5e3e 100644 --- a/apps/web/tests/chat-long-interactions.e2e.ts +++ b/apps/web/tests/chat-long-interactions.e2e.ts @@ -11,7 +11,6 @@ import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest' import type { StreamChunk } from '@deepseek-ai/dsh-llm' import type { ReplayEntry, ReplayOverrideDoc } from '@deepseek-ai/dsh-llm-replay' import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' -import { conversationContextKey } from '@deepseek-ai/dsh-client-runtime/client' import { createChatScrollFixture } from './chat-scroll-fixture.ts' import { launchWebScaffold, @@ -20,7 +19,7 @@ import { webSnapshotMode, type WebScaffold, } from './scaffold.ts' -import { newEnglishPage, saveFailureShot } from './support.ts' +import { conversationContextKey, newEnglishPage, saveFailureShot } from './support.ts' const MODE = webSnapshotMode() const SESSION_ID = 'chat-long-interactions-e2e' diff --git a/apps/web/tests/support.ts b/apps/web/tests/support.ts index ee2a1a1a62..38d0849784 100644 --- a/apps/web/tests/support.ts +++ b/apps/web/tests/support.ts @@ -119,3 +119,17 @@ export async function saveFailureShot(page: Page, name: string): Promise { // Best-effort evidence: a dead page/browser at failure time must not mask the real assertion error. } } + +/** + * The conversation engine's Context key format, restated here rather than + * imported: these specs live in the Host compiler aggregate, which must not + * reach the Client plane. The engine's own copy is + * `conversationContextKey` in dsh-client-runtime; a drift between them makes + * the key miss its rendered node, so the assertion fails loudly. + * @param kind - Definition kind. + * @param id - Definition-local business identity. + * @returns the engine-owned Context key. + */ +export function conversationContextKey(kind: string, id: string): string { + return `${kind.length}:${kind}${id}` +} diff --git a/packages/api/gateway/tsconfig.client.json b/packages/api/gateway/tsconfig.client.json new file mode 100644 index 0000000000..de257c951a --- /dev/null +++ b/packages/api/gateway/tsconfig.client.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.client.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/types", + "tsBuildInfoFile": "lib/tsconfig.client.tsbuildinfo" + }, + "files": [ + "src/client/index.ts" + ], + "references": [ + { + "path": "../../../vendor/cordis" + }, + { + "path": "../../client/connection/tsconfig.client.json" + }, + { + "path": "../../typert/type-meta" + } + ] +} diff --git a/packages/api/gateway/tsconfig.host.json b/packages/api/gateway/tsconfig.host.json new file mode 100644 index 0000000000..5c12e43cc2 --- /dev/null +++ b/packages/api/gateway/tsconfig.host.json @@ -0,0 +1,30 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/types", + "tsBuildInfoFile": "lib/tsconfig.host.tsbuildinfo" + }, + "files": [ + "src/index.ts", + "src/invariant.ts", + "src/types.ts" + ], + "references": [ + { + "path": "../../../vendor/cosmokit" + }, + { + "path": "../../../vendor/cordis" + }, + { + "path": "../../support/invariants" + }, + { + "path": "../../client/connection/tsconfig.host.json" + }, + { + "path": "../../typert/type-meta" + } + ] +} diff --git a/packages/api/gateway/tsconfig.json b/packages/api/gateway/tsconfig.json index fea39663f7..2eca820546 100644 --- a/packages/api/gateway/tsconfig.json +++ b/packages/api/gateway/tsconfig.json @@ -1,27 +1,11 @@ { - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib/types" - }, - "include": [ - "src" - ], + "files": [], "references": [ { - "path": "../../../vendor/cosmokit" + "path": "./tsconfig.host.json" }, { - "path": "../../../vendor/cordis" - }, - { - "path": "../../support/invariants" - }, - { - "path": "../../client/connection" - }, - { - "path": "../../typert/type-meta" + "path": "./tsconfig.client.json" } ] } diff --git a/packages/api/remotes/tsconfig.client.json b/packages/api/remotes/tsconfig.client.json index 256258c668..8f28b83a6e 100644 --- a/packages/api/remotes/tsconfig.client.json +++ b/packages/api/remotes/tsconfig.client.json @@ -15,7 +15,10 @@ "path": "../../../vendor/cordis" }, { - "path": "../gateway" + "path": "../gateway/tsconfig.client.json" + }, + { + "path": "../../client/connection/tsconfig.client.json" }, { "path": "../../credentials/credentials" diff --git a/packages/client/connection/tsconfig.client.json b/packages/client/connection/tsconfig.client.json new file mode 100644 index 0000000000..91ec8b42c7 --- /dev/null +++ b/packages/client/connection/tsconfig.client.json @@ -0,0 +1,49 @@ +{ + "extends": "../../../tsconfig.base.client.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/types", + "tsBuildInfoFile": "lib/tsconfig.client.tsbuildinfo" + }, + "files": [ + "src/api-path.ts", + "src/client/api.ts", + "src/client/connection.ts", + "src/client/fixture.ts", + "src/client/index.ts", + "src/client/random-uuid.ts", + "src/client/rpc.ts", + "src/client/web-api-client.ts", + "src/loopback-hostname.ts", + "src/rpc.ts" + ], + "references": [ + { + "path": "../../../vendor/cordis" + }, + { + "path": "../../attachment/attachment" + }, + { + "path": "../../core/session" + }, + { + "path": "../../core/tools" + }, + { + "path": "../../host/apiproxy" + }, + { + "path": "../../interaction/commands" + }, + { + "path": "../../llm/llm" + }, + { + "path": "../../support/invariants" + }, + { + "path": "../../util/brand" + } + ] +} diff --git a/packages/client/connection/tsconfig.host.json b/packages/client/connection/tsconfig.host.json new file mode 100644 index 0000000000..dca9b1a1b2 --- /dev/null +++ b/packages/client/connection/tsconfig.host.json @@ -0,0 +1,33 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/types", + "tsBuildInfoFile": "lib/tsconfig.host.tsbuildinfo" + }, + "files": [ + "src/api-path.ts", + "src/api-request-trust.ts", + "src/http-bridge.ts", + "src/index.ts", + "src/invariant.ts", + "src/loopback-hostname.ts", + "src/rpc-host.ts", + "src/rpc.ts", + "src/websocket-downlink.ts" + ], + "references": [ + { + "path": "../../attachment/attachment" + }, + { + "path": "../../host/apiproxy" + }, + { + "path": "../../host/webserver" + }, + { + "path": "../../support/invariants" + } + ] +} diff --git a/packages/client/connection/tsconfig.json b/packages/client/connection/tsconfig.json index 7bf86ffefe..2eca820546 100644 --- a/packages/client/connection/tsconfig.json +++ b/packages/client/connection/tsconfig.json @@ -1,46 +1,11 @@ { - "extends": "../../../tsconfig.base.client.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib/types", - "types": ["node"] - }, - "include": [ - "src" - ], + "files": [], "references": [ { - "path": "../../attachment/attachment" + "path": "./tsconfig.host.json" }, { - "path": "../../llm/llm" - }, - { - "path": "../../core/session" - }, - { - "path": "../../interaction/commands" - }, - { - "path": "../../util/brand" - }, - { - "path": "../../host/apiproxy" - }, - { - "path": "../../host/webserver" - }, - { - "path": "../../interaction/user-approval" - }, - { - "path": "../../interaction/user-interaction" - }, - { - "path": "../../support/invariants" + "path": "./tsconfig.client.json" } - ], - "exclude": [ - "**/*.legacy.*" ] } diff --git a/tsconfig.client.json b/tsconfig.client.json index a054cb8751..9e2a7778d7 100644 --- a/tsconfig.client.json +++ b/tsconfig.client.json @@ -19,10 +19,7 @@ "packages/client/*/src/css-modules.d.ts", "packages/client/*/tests/**/*.ts", "packages/client/*/tests/**/*.tsx", - "packages/host/directory-picker-browse/tests/**/*.ts", - "packages/host/directory-picker-browse/tests/**/*.tsx", - "packages/host/directory-picker-native/tests/**/*.ts", - "packages/host/directory-picker-native/tests/**/*.tsx", + "packages/api/gateway/tests/client.spec.ts", "packages/client/tsdown.client.ts", "scripts/client-bundle-css.spec.ts", "scripts/client-bundle-purity.spec.ts" @@ -32,11 +29,6 @@ // smoke policy). webserver has zero workspace deps and no cordis merge, // so it cannot drag host-side Context augmentation into this program. { "path": "./packages/host/webserver" }, - // Dual-face host leaf: the node half is the native picking backend, the - // browser half registers the picking flow into ui-workspace's slot — - // client-side Context merges keep it out of the host program. - { "path": "./packages/host/directory-picker-native" }, - { "path": "./packages/host/directory-picker-browse" }, // Compaction seam: the client-runtime pin test value-imports the canonical // checkpoint const from the cordis-free dsh-compact/checkpoint leaf and // deliberately never loads the dsh-compact package root or the host-side @@ -51,9 +43,12 @@ { "path": "./packages/client/web-react" }, { "path": "./packages/client/modules" }, { "path": "./packages/client/hmr" }, - { "path": "./packages/client/connection" }, + { "path": "./packages/client/connection/tsconfig.client.json" }, + // The carrier's node-half spec rides this aggregate's package test glob, so + // its Host face is referenced here too — the mirror of the webserver leaf. + { "path": "./packages/client/connection/tsconfig.host.json" }, { "path": "./packages/typert/registry" }, - { "path": "./packages/api/gateway" }, + { "path": "./packages/api/gateway/tsconfig.client.json" }, { "path": "./packages/api/remotes/tsconfig.client.json" }, { "path": "./packages/client/runtime" }, { "path": "./packages/client/test-runtime" }, @@ -69,6 +64,8 @@ { "path": "./packages/client/ui-skill" }, { "path": "./packages/client/ui-subagent" }, { "path": "./packages/client/ui-task" }, + { "path": "./packages/client/ui-directory-picker" }, + { "path": "./packages/client/ui-directory-picker-native" }, { "path": "./packages/client/ui-goal" }, { "path": "./packages/client/ui-model" }, { "path": "./packages/client/ui-agent-preset" }, diff --git a/tsconfig.host.json b/tsconfig.host.json index 90a254f72a..ef02fc557f 100644 --- a/tsconfig.host.json +++ b/tsconfig.host.json @@ -89,8 +89,7 @@ ], "exclude": [ "packages/client/**", - "packages/host/directory-picker-browse/**", - "packages/host/directory-picker-native/**", + "packages/api/gateway/tests/client.spec.ts", "scripts/client-bundle-css.spec.ts", "packages/typert/generator/tests/fixtures/**", "scripts/client-bundle-purity.spec.ts" @@ -120,7 +119,7 @@ { "path": "./packages/core/scope" }, { "path": "./packages/typert/type-meta" }, { "path": "./packages/typert/registry" }, - { "path": "./packages/api/gateway" }, + { "path": "./packages/api/gateway/tsconfig.host.json" }, { "path": "./packages/api/remotes/tsconfig.host.json" }, { "path": "./packages/typert/loader" }, { "path": "./packages/session/session-persistence" }, @@ -270,11 +269,6 @@ { "path": "./packages/host/apiproxy" }, { "path": "./packages/host/directory-picker" }, { "path": "./packages/host/directory-picker-auto" }, - // Dual-face backend leaves stay client-registered (their tests and client - // halves are excluded above); these references only let the adaptive - // chooser's composition test import each backend's NODE entry, whose - // declarations carry no client-side Context merge — the mirror of the - // client aggregate's webserver reference. { "path": "./packages/host/directory-picker-browse" }, { "path": "./packages/host/directory-picker-native" }, { "path": "./packages/host/frontend-static" },