mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-09-11 11:18:36 +00:00
fix(web-shell): make the transcript entry's "no daemon providers" claim true
Review found the entry docblock over-claiming. `WebShellTranscript.tsx` value-imported `transcriptBlocksToLocalizedMessages` from `hooks/useMessages`, and that module value-imports `useConnection` / `useTranscriptBlocks` / `useWorkspace` from the `daemon-react-sdk` barrel — so `dist/transcript.js` still carried the provider guards, including `useDaemonActions must be used within DaemonSessionProvider`. Gives that helper the same leaf-module treatment as the composer tags: the pure projection (and the `Translator` type it needs) moves to `adapters/localizedMessages.ts`, which imports nothing from the daemon SDK barrel, and `useMessages.ts` re-exports both so existing callers and its own test are unaffected. `build-artifact.test.ts` now asserts `dist/transcript.js` contains neither `DaemonSessionProvider` nor `DaemonWorkspaceProvider`, so the docblock's claim is pinned rather than asserted in prose. Also fixes the read-only recipe in `packages/web-shell/README.md`, which still taught the root import this PR makes wrong — a host following it would ship the interactive shell, which the reviewer measured as landing a document at ~8.8 MB, past the new cap. Same for the example in the read-only transcript design doc. Tightens the runtime budget to 7,400,000 / 7,300,000 warning, from the reviewer's local measurement of 7,275,173 bytes with the echarts stub in place (CI's 8,456,076 predates it).
This commit is contained in:
parent
1837026a9d
commit
1d94060f5b
7 changed files with 86 additions and 30 deletions
|
|
@ -137,7 +137,7 @@ Notes:
|
|||
Example:
|
||||
|
||||
```tsx
|
||||
import { WebShellTranscript } from '@qwen-code/web-shell';
|
||||
import { WebShellTranscript } from '@qwen-code/web-shell/transcript';
|
||||
import type { DaemonTranscriptBlock } from '@qwen-code/sdk/daemon';
|
||||
|
||||
export function HistoryView({
|
||||
|
|
|
|||
|
|
@ -181,9 +181,12 @@ export function App() {
|
|||
审批或 session mutation。浏览器宿主可以逐行解析 JSONL,再通过 SDK 的 opt-in facade
|
||||
投影:
|
||||
|
||||
> 只渲染 transcript 的宿主请从 `@qwen-code/web-shell/transcript` 子路径导入。包根会连带
|
||||
> `App`、daemon providers 和编辑器/终端相关代码,不要依赖 tree shaking 把它们摇掉。
|
||||
|
||||
```tsx
|
||||
import { projectChatRecordsToDaemonTranscript } from '@qwen-code/sdk/daemon/transcript';
|
||||
import { WebShellTranscript } from '@qwen-code/web-shell';
|
||||
import { WebShellTranscript } from '@qwen-code/web-shell/transcript';
|
||||
|
||||
const records = jsonl
|
||||
.split(/\r?\n/)
|
||||
|
|
|
|||
47
packages/web-shell/client/adapters/localizedMessages.ts
Normal file
47
packages/web-shell/client/adapters/localizedMessages.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2026 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Leaf module for projecting transcript blocks into localized messages.
|
||||
*
|
||||
* This lives outside `hooks/useMessages.ts` on purpose. That module is a daemon
|
||||
* consumer — it value-imports `useConnection` / `useTranscriptBlocks` /
|
||||
* `useWorkspace` from the `daemon-react-sdk` barrel — so importing this one
|
||||
* function from it dragged the daemon provider stack into the read-only
|
||||
* transcript entry, which advertises the opposite. Same treatment as
|
||||
* `utils/composerTag.ts`: the pure projection lives in a module with no daemon
|
||||
* or editor imports, and `useMessages.ts` re-exports it for existing callers.
|
||||
*
|
||||
* Keep this module free of React hooks and of anything reaching
|
||||
* `@qwen-code/web-shell/daemon-react-sdk`. `client/build-artifact.test.ts`
|
||||
* asserts that `dist/transcript.js` carries no daemon provider code.
|
||||
*/
|
||||
|
||||
import type { DaemonTranscriptBlock } from '@qwen-code/sdk/daemon';
|
||||
import { transcriptBlocksToDaemonMessages } from './transcriptToMessages';
|
||||
import type { Message } from './types';
|
||||
|
||||
export type Translator = (
|
||||
key: string,
|
||||
vars?: Record<string, string | number>,
|
||||
) => string;
|
||||
|
||||
export function transcriptBlocksToLocalizedMessages(
|
||||
blocks: readonly DaemonTranscriptBlock[],
|
||||
t: Translator,
|
||||
safeToolProjection = false,
|
||||
): Message[] {
|
||||
return transcriptBlocksToDaemonMessages(blocks, {
|
||||
safeToolProjection,
|
||||
includeSourceIdentity: true,
|
||||
labels: {
|
||||
promptCancelled: t('request.cancelled'),
|
||||
branchSuccess: (name) => t('branch.success', { name }),
|
||||
modelStreamInterrupted: t('error.modelStreamInterrupted'),
|
||||
loopDetected: t('error.loopDetected'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -334,6 +334,22 @@ describe('build artifact — transcript entry (#11031)', () => {
|
|||
expect(js).not.toContain('vaul');
|
||||
});
|
||||
|
||||
it('does not pull the daemon provider stack into the transcript entry', () => {
|
||||
// The entry advertises "no daemon providers". It did not hold: the
|
||||
// transcript imported transcriptBlocksToLocalizedMessages from
|
||||
// hooks/useMessages, which value-imports useConnection /
|
||||
// useTranscriptBlocks / useWorkspace from the daemon-react-sdk barrel, so
|
||||
// dist/transcript.js still carried the provider guards. The projection now
|
||||
// lives in adapters/localizedMessages.ts. These strings are provider
|
||||
// invariant messages, which survive minification verbatim.
|
||||
const js = readTranscriptBundle().replace(
|
||||
/^const __qwenWebShellCss=[^\n]*\n/,
|
||||
'',
|
||||
);
|
||||
expect(js).not.toContain('DaemonSessionProvider');
|
||||
expect(js).not.toContain('DaemonWorkspaceProvider');
|
||||
});
|
||||
|
||||
it('still carries what a transcript actually renders', () => {
|
||||
const bundle = readTranscriptBundle();
|
||||
expect(bundle).toContain('react-markdown');
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import {
|
|||
normalizeLanguage,
|
||||
type WebShellLanguage,
|
||||
} from '../i18n';
|
||||
import { transcriptBlocksToLocalizedMessages } from '../hooks/useMessages';
|
||||
import { transcriptBlocksToLocalizedMessages } from '../adapters/localizedMessages';
|
||||
import { WebShellPortalRootContext } from '../portalRoot';
|
||||
import { computeTodoDetails, computeTodoTimeline } from '../utils/todos';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ import {
|
|||
useWorkspace,
|
||||
} from '@qwen-code/web-shell/daemon-react-sdk';
|
||||
import { transcriptBlocksToDaemonMessages } from '../adapters/transcriptToMessages';
|
||||
import {
|
||||
transcriptBlocksToLocalizedMessages,
|
||||
type Translator,
|
||||
} from '../adapters/localizedMessages';
|
||||
import type { Message } from '../adapters/types';
|
||||
import {
|
||||
isActiveToolStatus,
|
||||
|
|
@ -20,10 +24,13 @@ import {
|
|||
projectTerminalBackgroundAgentTool,
|
||||
} from '../adapters/toolClassification';
|
||||
|
||||
type Translator = (
|
||||
key: string,
|
||||
vars?: Record<string, string | number>,
|
||||
) => string;
|
||||
// Re-exported for existing callers. The projection itself lives in a leaf module
|
||||
// so the read-only transcript entry does not pull this file's daemon imports —
|
||||
// see adapters/localizedMessages.ts.
|
||||
export {
|
||||
transcriptBlocksToLocalizedMessages,
|
||||
type Translator,
|
||||
} from '../adapters/localizedMessages';
|
||||
|
||||
const BACKGROUND_AGENT_RECONCILIATION_RETRY_BASE_MS = 3_000;
|
||||
const BACKGROUND_AGENT_RECONCILIATION_RETRY_MAX_MS = 60_000;
|
||||
|
|
@ -66,23 +73,6 @@ interface ReconciliationRound {
|
|||
succeeded: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
export function transcriptBlocksToLocalizedMessages(
|
||||
blocks: readonly DaemonTranscriptBlock[],
|
||||
t: Translator,
|
||||
safeToolProjection = false,
|
||||
): Message[] {
|
||||
return transcriptBlocksToDaemonMessages(blocks, {
|
||||
safeToolProjection,
|
||||
includeSourceIdentity: true,
|
||||
labels: {
|
||||
promptCancelled: t('request.cancelled'),
|
||||
branchSuccess: (name) => t('branch.success', { name }),
|
||||
modelStreamInterrupted: t('error.modelStreamInterrupted'),
|
||||
loopDetected: t('error.loopDetected'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function reuseUnchangedProjectedPrefix(
|
||||
previous: MessageProjection | undefined,
|
||||
blocks: readonly DaemonTranscriptBlock[],
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ const exportTranscriptMaxEnvelopeBytes = 32 * 1024 * 1024;
|
|||
// cd packages/web-templates && node src/export-html/build.mjs
|
||||
// (the build prints `Document export runtime is N bytes`.)
|
||||
//
|
||||
// Last measured at 8,456,076 bytes, by CI on this branch's merge with main
|
||||
// (run 33959200199, "Lint & Static"), before the echarts stub below landed —
|
||||
// so the real number is now lower and these two are still loose. Tighten them
|
||||
// from the next green CI run rather than from a local guess.
|
||||
const DOCUMENT_RUNTIME_WARNING_BYTES = 8_500_000;
|
||||
const MAX_DOCUMENT_RUNTIME_BYTES = 8_700_000;
|
||||
// Last measured at 7,275,173 bytes, with the echarts stub below in place, by a
|
||||
// reviewer building this branch locally (PR #11038). The prior CI measurement
|
||||
// on the same branch without that stub was 8,456,076. Re-measure and lower
|
||||
// these two again after any change to the document entry's dependencies.
|
||||
const DOCUMENT_RUNTIME_WARNING_BYTES = 7_300_000;
|
||||
const MAX_DOCUMENT_RUNTIME_BYTES = 7_400_000;
|
||||
|
||||
// Modules that must not be reachable from the document entry, checked against
|
||||
// the esbuild metafile inputs after the bundle is produced.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue