dsh-context-compression-improved 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +1 -0
- package/.github/workflows/ci.yml +39 -0
- package/CHANGELOG.ja.md +39 -0
- package/CHANGELOG.ko.md +39 -0
- package/CHANGELOG.md +135 -0
- package/CHANGELOG.zh.md +39 -0
- package/CONTRIBUTING.md +22 -0
- package/README.ja.md +104 -0
- package/README.ko.md +103 -0
- package/README.md +89 -12
- package/README.zh.md +87 -12
- package/SECURITY.md +18 -0
- package/THIRD_PARTY_NOTICES.md +7 -31
- package/docs/installation.ja.md +76 -0
- package/docs/installation.ko.md +76 -0
- package/docs/installation.md +76 -0
- package/docs/installation.zh.md +76 -0
- package/docs/repair-log.md +582 -0
- package/eslint.config.js +30 -0
- package/package.json +84 -81
- package/packages/selector/LICENSE +21 -0
- package/packages/selector/README.md +26 -0
- package/packages/selector/README.zh.md +26 -0
- package/packages/selector/THIRD_PARTY_NOTICES.md +38 -0
- package/packages/selector/docs/history-tool-call-working-set-spec.md +112 -0
- package/packages/selector/docs/native-tool-result-selector-spec.md +34 -0
- package/packages/selector/docs/subagent-cache-reuse-spec.md +46 -0
- package/packages/selector/lib/style.css +308 -0
- package/packages/selector/package.json +115 -0
- package/{screenshots.json → packages/selector/screenshots.json} +6 -6
- package/packages/selector/src/client/CompressionProfileControls.tsx +229 -0
- package/packages/selector/src/client/CompressionProfileSelector.module.css +170 -0
- package/packages/selector/src/client/CompressionProfileSelector.tsx +79 -0
- package/packages/selector/src/client/CustomPolicyEditor.tsx +216 -0
- package/packages/selector/src/client/EstimatorControls.tsx +281 -0
- package/packages/selector/src/client/decode.ts +49 -0
- package/packages/selector/src/client/index.ts +111 -0
- package/packages/selector/src/client/locales.ts +198 -0
- package/packages/selector/src/client/preset-options.ts +70 -0
- package/packages/selector/src/client/settings-section.tsx +126 -0
- package/packages/selector/src/css-modules.d.ts +6 -0
- package/packages/selector/src/deepseek-v4-tokenizer.ts +210 -0
- package/packages/selector/src/estimator-catalog.ts +104 -0
- package/packages/selector/src/index.ts +327 -0
- package/packages/selector/src/invariant.ts +113 -0
- package/packages/selector/src/preset-overlay.ts +567 -0
- package/packages/selector/src/profiles.ts +342 -0
- package/packages/selector/src/pruner/content.ts +188 -0
- package/packages/selector/src/pruner/session.ts +94 -0
- package/packages/selector/src/pruner/state.ts +43 -0
- package/packages/selector/src/pruner/tuning.ts +23 -0
- package/packages/selector/src/pruner/types.ts +60 -0
- package/packages/selector/src/pruner.ts +2144 -0
- package/packages/selector/src/runtime/adaptive-cost.ts +194 -0
- package/packages/selector/src/runtime/audit.ts +215 -0
- package/packages/selector/src/runtime/config.ts +613 -0
- package/packages/selector/src/runtime/custom-policy.ts +278 -0
- package/packages/selector/src/runtime/deepseek-official-pricing.ts +298 -0
- package/packages/selector/src/runtime/deepseek-v4-vision-tokens.ts +254 -0
- package/packages/selector/src/runtime/measurement.ts +403 -0
- package/packages/selector/src/runtime/reducers.ts +656 -0
- package/packages/selector/src/runtime/retrieve.ts +457 -0
- package/packages/selector/src/runtime/session-events.ts +17 -0
- package/packages/selector/src/runtime/tail-trim.ts +166 -0
- package/packages/selector/src/runtime/token-count.ts +72 -0
- package/packages/selector/src/runtime/tokenpilot/dedup.ts +81 -0
- package/packages/selector/src/runtime/tokenpilot/estimator.ts +183 -0
- package/packages/selector/src/runtime/tokenpilot/locator.ts +128 -0
- package/packages/selector/src/runtime/tokenpilot/read-state.ts +77 -0
- package/packages/selector/src/runtime/types.ts +309 -0
- package/packages/selector/src/runtime/value.ts +48 -0
- package/packages/selector/tests/auto-compact.client.spec.tsx +226 -0
- package/packages/selector/tests/built/client-artifact.spec.ts +51 -0
- package/packages/selector/tests/cache-prefix-audit.spec.ts +123 -0
- package/packages/selector/tests/code-skeleton.client.spec.ts +88 -0
- package/packages/selector/tests/custom-contract.client.spec.ts +202 -0
- package/packages/selector/tests/estimator-catalog.spec.ts +70 -0
- package/packages/selector/tests/estimator-channel.client.spec.tsx +247 -0
- package/packages/selector/tests/estimator-route-registration.host.spec.ts +176 -0
- package/packages/selector/tests/host-preset-overlay.host.spec.ts +204 -0
- package/packages/selector/tests/preset-options-write.client.spec.ts +181 -0
- package/packages/selector/tests/preset-overlay-loader.e2e.host.spec.ts +196 -0
- package/packages/selector/tests/preset-overlay.host.spec.ts +243 -0
- package/packages/selector/tests/profiles.client.spec.tsx +434 -0
- package/packages/selector/tests/public/package-contract.client.spec.ts +33 -0
- package/packages/selector/tests/runtime/adaptive-cost.spec.ts +167 -0
- package/packages/selector/tests/runtime/audit.spec.ts +129 -0
- package/packages/selector/tests/runtime/auto-compact-config.spec.ts +523 -0
- package/packages/selector/tests/runtime/code-skeleton.spec.ts +141 -0
- package/packages/selector/tests/runtime/deepseek-official-pricing.spec.ts +186 -0
- package/packages/selector/tests/runtime/deepseek-v4-tokenizer.spec.ts +122 -0
- package/packages/selector/tests/runtime/deepseek-v4-vision-tokens.spec.ts +122 -0
- package/packages/selector/tests/runtime/fixtures/profile-baseline.json +273 -0
- package/packages/selector/tests/runtime/fixtures/tokenizer-golden.json +106 -0
- package/packages/selector/tests/runtime/fixtures/vision-golden.json +459 -0
- package/packages/selector/tests/runtime/public/public-runtime.spec.ts +2531 -0
- package/packages/selector/tests/runtime/session-events.spec.ts +27 -0
- package/packages/selector/tests/runtime/tokenizer-golden.spec.ts +53 -0
- package/packages/selector/tests/runtime/tokenpilot/dedup.spec.ts +52 -0
- package/packages/selector/tests/runtime/tokenpilot/estimator.spec.ts +56 -0
- package/packages/selector/tests/runtime/tokenpilot/locator.spec.ts +76 -0
- package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +100 -0
- package/packages/selector/tests/runtime/tokenpilot/read-state.spec.ts +58 -0
- package/packages/selector/tests/runtime/value.spec.ts +23 -0
- package/packages/selector/tests/standing-generation.host.spec.ts +631 -0
- package/packages/selector/tests/subagent-cache-reuse.host.spec.ts +250 -0
- package/packages/selector/tests/support/cache-prefix-audit.ts +105 -0
- package/packages/selector/tests/support/mock-adapter.ts +37 -0
- package/packages/selector/tests/support/ui-primitives.tsx +34 -0
- package/packages/selector/tsconfig.json +11 -0
- package/packages/selector/tsdown.client.config.ts +102 -0
- package/packages/selector/tsdown.config.ts +20 -0
- package/pnpm-workspace.yaml +19 -0
- package/scripts/capture-profile-baseline.ts +80 -0
- package/scripts/generate-tokenizer-fixtures.py +81 -0
- package/scripts/generate-vision-fixtures.py +208 -0
- package/scripts/packed-components-smoke.ts +713 -0
- package/scripts/packed-install-e2e.ts +1072 -0
- package/scripts/verify-release.ts +300 -0
- package/tests/TEST_INVENTORY.md +42 -0
- package/tsconfig.base.json +18 -0
- package/tsconfig.json +7 -0
- package/tsconfig.scripts.json +13 -0
- package/tsconfig.tests.json +15 -0
- package/vitest.built.config.ts +9 -0
- package/vitest.config.ts +43 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/LICENSE.DeepSeek-V4-Pro.txt +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/manifest.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/tokenizer.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/tokenizer_config.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/LICENSE.DeepSeek-V4-Flash-Vision-Exp.txt +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/manifest.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/tokenizer.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/tokenizer_config.json +0 -0
- /package/{assets → packages/selector/assets}/screenshots/context-compression-selector-profiles.jpg +0 -0
- /package/{assets → packages/selector/assets}/screenshots/context-compression-selector-settings.png +0 -0
- /package/{cordis.patch.yml → packages/selector/cordis.patch.yml} +0 -0
- /package/{dsh.plugin.json → packages/selector/dsh.plugin.json} +0 -0
- /package/{lib → packages/selector/lib}/client.d.ts +0 -0
- /package/{lib → packages/selector/lib}/client.js +0 -0
- /package/{lib → packages/selector/lib}/config.js +0 -0
- /package/{lib → packages/selector/lib}/index.d.ts +0 -0
- /package/{lib → packages/selector/lib}/index.js +0 -0
- /package/{lib → packages/selector/lib}/invariant.d.ts +0 -0
- /package/{lib → packages/selector/lib}/invariant.js +0 -0
- /package/{lib → packages/selector/lib}/pruner.d.ts +0 -0
- /package/{lib → packages/selector/lib}/pruner.js +0 -0
- /package/{lib → packages/selector/lib}/tail-trim.js +0 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { readdir, readFile, stat } from 'node:fs/promises'
|
|
4
|
+
import { join, relative } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const root = fileURLToPath(new URL('..', import.meta.url))
|
|
8
|
+
|
|
9
|
+
const fail = (message: string): never => {
|
|
10
|
+
throw new Error(`release verification: ${message}`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const json = async (path: string) => JSON.parse(await readFile(path, 'utf8')) as Record<string, unknown>
|
|
14
|
+
const rootPackage = await json(join(root, 'package.json'))
|
|
15
|
+
const selectorPackage = await json(join(root, 'packages/selector/package.json')) as Record<string, Record<string, unknown>>
|
|
16
|
+
|
|
17
|
+
const rootScripts = rootPackage.scripts as Record<string, string> | undefined
|
|
18
|
+
const selectorScripts = selectorPackage.scripts as Record<string, string> | undefined
|
|
19
|
+
|
|
20
|
+
if (!(rootScripts?.typecheck as string)?.includes('pnpm run typecheck:tests')
|
|
21
|
+
|| rootScripts?.['typecheck:tests'] !== 'tsc --noEmit -p tsconfig.tests.json') {
|
|
22
|
+
fail('root typecheck must include the strict active-test TypeScript gate')
|
|
23
|
+
}
|
|
24
|
+
if (selectorScripts?.test
|
|
25
|
+
!== 'vitest run --root ../.. --config vitest.config.ts --project runtime --project selector-host --project selector-client') {
|
|
26
|
+
fail('Package-local test script is not the verified root project command')
|
|
27
|
+
}
|
|
28
|
+
const ci = await readFile(join(root, '.github/workflows/ci.yml'), 'utf8')
|
|
29
|
+
if (!ci.includes('pnpm --filter dsh-context-compression-improved test')) {
|
|
30
|
+
fail('CI lacks the package-local gate: pnpm --filter dsh-context-compression-improved test')
|
|
31
|
+
}
|
|
32
|
+
// The packed release E2E is the release gate that actually installs the
|
|
33
|
+
// tarballs: verify it cannot be silently dropped or defanged. It must exist
|
|
34
|
+
// as a root script, CI must run exactly that script, the script must default
|
|
35
|
+
// to fail-closed release mode, and that mode must refuse every skip/null
|
|
36
|
+
// lifecycle outcome.
|
|
37
|
+
if (rootScripts?.['test:e2e:packed'] !== 'node scripts-dist/packed-install-e2e.js') {
|
|
38
|
+
fail('root test:e2e:packed script is missing or does not run the packed E2E directly')
|
|
39
|
+
}
|
|
40
|
+
if (!ci.includes('pnpm run test:e2e:packed') && !ci.includes('pnpm test:e2e:packed')) {
|
|
41
|
+
fail('CI does not run the packed release E2E gate')
|
|
42
|
+
}
|
|
43
|
+
const packedE2e = await readFile(join(root, 'scripts/packed-install-e2e.ts'), 'utf8')
|
|
44
|
+
if (!/const e2eMode\s*(:\s*'dev'\s*\|\s*'release')?\s*=\s*process\.env\.DSH_E2E_MODE === 'dev' \? 'dev' : 'release'/u.test(packedE2e)) {
|
|
45
|
+
fail('packed E2E must default to release mode (dev only via an explicit DSH_E2E_MODE)')
|
|
46
|
+
}
|
|
47
|
+
for (const failClosed of [
|
|
48
|
+
'release gate requires the upgrade leg to run',
|
|
49
|
+
'release gate requires the official clean-harness lifecycle to run',
|
|
50
|
+
]) {
|
|
51
|
+
if (!packedE2e.includes(failClosed)) {
|
|
52
|
+
fail(`packed E2E release mode lost its fail-closed guard: ${failClosed}`)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const packedComponents = await readFile(join(root, 'scripts/packed-components-smoke.ts'), 'utf8')
|
|
56
|
+
for (const required of [
|
|
57
|
+
'Runtime.measureForCompaction(visionCtx, visionImage)',
|
|
58
|
+
"estimatedImageCount?.kind === 'tokenizer-estimate'",
|
|
59
|
+
'estimatedImageCount.tokens === 340',
|
|
60
|
+
'estimatedImageCount.upperBoundTokens === 384',
|
|
61
|
+
'imageMeasurement.currentSurface.kind',
|
|
62
|
+
]) {
|
|
63
|
+
if (!packedComponents.includes(required)) {
|
|
64
|
+
fail(`packed component smoke lost its installed vision estimate guard: ${required}`)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const required of [
|
|
68
|
+
"packedVisionSmoke.imageSession?.measurement?.kind === 'tokenizer-estimate'",
|
|
69
|
+
'packedVisionSmoke.imageSession.measurement.tokens === 340',
|
|
70
|
+
'packedVisionSmoke.imageSession.measurement.upperBoundTokens === 384',
|
|
71
|
+
'packedVisionSmoke.imageSession.measurement.estimatorId',
|
|
72
|
+
'packedVisionSmoke.imageSession.measurement.estimatorRevision',
|
|
73
|
+
]) {
|
|
74
|
+
if (!packedE2e.includes(required)) {
|
|
75
|
+
fail(`packed E2E lost its parsed vision estimate guard: ${required}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
await stat(join(root, 'tsconfig.tests.json'))
|
|
79
|
+
|
|
80
|
+
if ((selectorPackage.dsh as Record<string, Record<string, string>>)?.bundle?.patch !== './cordis.patch.yml') {
|
|
81
|
+
fail('selector must declare the DSH Bundle patch')
|
|
82
|
+
}
|
|
83
|
+
if ((selectorPackage.files as string[] | undefined)?.some((entry: string) => entry.endsWith('.css'))) {
|
|
84
|
+
fail('selector must not rely on separately served CSS assets')
|
|
85
|
+
}
|
|
86
|
+
if ((selectorPackage.name as unknown as string) !== 'dsh-context-compression-improved') fail('unexpected package name')
|
|
87
|
+
if ((selectorPackage.publishConfig as Record<string, string>)?.access !== 'public') fail('publish access is not public')
|
|
88
|
+
if ((selectorPackage.publishConfig as Record<string, string>)?.tag !== 'latest') fail('publish tag is not latest')
|
|
89
|
+
for (const peer of [
|
|
90
|
+
'@deepseek-ai/dsh-command-compact',
|
|
91
|
+
'@deepseek-ai/dsh-compaction-basic',
|
|
92
|
+
]) {
|
|
93
|
+
if (((selectorPackage.peerDependencies as Record<string, string>) ?? {})[peer] !== '>=0.1.1-rc.2 <0.2.0') {
|
|
94
|
+
fail(`selector peer ${peer} is missing or outside the verified range`)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const notice = await readFile(join(root, 'packages/selector/THIRD_PARTY_NOTICES.md'), 'utf8')
|
|
98
|
+
if (!notice.includes('Copyright (c) 2026 DeepSeek')) {
|
|
99
|
+
fail('packages/selector does not carry the full DeepSeek Harness MIT notice')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface AssetManifest {
|
|
103
|
+
directory: string
|
|
104
|
+
repository: string
|
|
105
|
+
modelIds: string
|
|
106
|
+
revision?: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const assetManifests: AssetManifest[] = [
|
|
110
|
+
{
|
|
111
|
+
directory: 'deepseek-v4',
|
|
112
|
+
repository: 'deepseek-ai/DeepSeek-V4-Pro',
|
|
113
|
+
modelIds: 'deepseek-v4-flash","deepseek-v4-pro',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
directory: 'deepseek-v4-vision-exp',
|
|
117
|
+
repository: 'deepseek-ai/DeepSeek-V4-Flash-Vision-Exp',
|
|
118
|
+
revision: '6821d6ad3681a4b137b066b76094fa82ebd0a380',
|
|
119
|
+
modelIds: 'deepseek-v4-flash-vision-exp',
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
for (const expected of assetManifests) {
|
|
123
|
+
const assetRoot = join(root, 'packages/selector/assets', expected.directory)
|
|
124
|
+
const manifest = await json(join(assetRoot, 'manifest.json')) as {
|
|
125
|
+
repository: string
|
|
126
|
+
modelIds: string[]
|
|
127
|
+
revision?: string
|
|
128
|
+
files: Record<string, { bytes: number; sha256: string }>
|
|
129
|
+
}
|
|
130
|
+
if (manifest.repository !== expected.repository) fail(`${expected.directory} manifest repository differs`)
|
|
131
|
+
if (!JSON.stringify(manifest.modelIds).includes(expected.modelIds)) {
|
|
132
|
+
fail(`${expected.directory} manifest model ids differ`)
|
|
133
|
+
}
|
|
134
|
+
if (expected.revision !== undefined && manifest.revision !== expected.revision) {
|
|
135
|
+
fail(`${expected.directory} manifest revision is not the pinned vision revision`)
|
|
136
|
+
}
|
|
137
|
+
for (const [name, descriptor] of Object.entries(manifest.files)) {
|
|
138
|
+
const bytes = await readFile(join(assetRoot, name))
|
|
139
|
+
const hash = createHash('sha256').update(bytes).digest('hex')
|
|
140
|
+
if (bytes.byteLength !== descriptor.bytes) fail(`${expected.directory}/${name} byte length differs from manifest`)
|
|
141
|
+
if (hash !== descriptor.sha256) fail(`${expected.directory}/${name} SHA-256 differs from manifest`)
|
|
142
|
+
}
|
|
143
|
+
if (!(selectorPackage.files as string[] | undefined)?.some(
|
|
144
|
+
entry => entry === 'assets' || entry === `assets/${expected.directory}/*`,
|
|
145
|
+
)) {
|
|
146
|
+
fail(`package files list omits assets for ${expected.directory}`)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const sourceRoots = [join(root, 'packages/selector/src')]
|
|
151
|
+
const forbidden = [
|
|
152
|
+
{ pattern: /compaction\/group-trim/u, label: 'custom compaction/group-trim event' },
|
|
153
|
+
{ pattern: /@deepseek-ai\/[^'"\s]+\/src(?:\/|['"])/u, label: 'Harness source subpath import' },
|
|
154
|
+
{ pattern: /(?:\/home\/|[A-Za-z]:\\Users\\)/u, label: 'developer absolute path' },
|
|
155
|
+
{ pattern: /\.\.\/\.\.\/\.\.\/(?:core|packages)\//u, label: 'monorepo-relative source import' },
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
const walk = async (directory: string): Promise<string[]> => {
|
|
159
|
+
const entries = await readdir(directory, { withFileTypes: true })
|
|
160
|
+
const files: string[] = []
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
const path = join(directory, entry.name)
|
|
163
|
+
if (entry.isDirectory()) files.push(...await walk(path))
|
|
164
|
+
else files.push(path)
|
|
165
|
+
}
|
|
166
|
+
return files
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (const sourceRoot of sourceRoots) {
|
|
170
|
+
for (const path of await walk(sourceRoot)) {
|
|
171
|
+
const text = await readFile(path, 'utf8')
|
|
172
|
+
for (const rule of forbidden) {
|
|
173
|
+
if (rule.pattern.test(text)) fail(`${relative(root, path)} contains ${rule.label}`)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const lib = join(root, 'packages/selector/lib')
|
|
179
|
+
if (!(await stat(lib)).isDirectory()) fail(`${relative(root, lib)} is missing; run build first`)
|
|
180
|
+
for (const path of await walk(lib)) {
|
|
181
|
+
if (path.endsWith('.map')) fail(`${relative(root, path)} is a source map`)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// A git install ships exactly the tracked tree, so the artifact graph the
|
|
185
|
+
// entries import must be complete AND committed. tsdown splits a shared chunk
|
|
186
|
+
// out of every entry that reuses a module, and an untracked chunk makes the
|
|
187
|
+
// installed entry crash at module-load time with ERR_MODULE_NOT_FOUND — the
|
|
188
|
+
// same failure class as a cross-package import, so it gets the same gate.
|
|
189
|
+
// Only the import graph is gated: build by-products nothing imports (for
|
|
190
|
+
// example lib/style.css, whose rules the client artifact already carries
|
|
191
|
+
// inline) are neither shipped nor required.
|
|
192
|
+
const libArtifacts = await walk(lib)
|
|
193
|
+
const libNames = new Set(libArtifacts.map(path => relative(lib, path).replaceAll('\\', '/')))
|
|
194
|
+
const libScripts = [...libNames].filter(name => name.endsWith('.js')).sort()
|
|
195
|
+
const libAllowedSuffixes = ((selectorPackage.files as unknown as string[]) ?? [])
|
|
196
|
+
.filter(pattern => pattern.startsWith('lib/'))
|
|
197
|
+
.map(pattern => pattern.slice(pattern.lastIndexOf('*') + 1))
|
|
198
|
+
if (libAllowedSuffixes.length === 0) fail('selector package files allowlist covers no lib artifact')
|
|
199
|
+
const isAllowed = (name: string) => libAllowedSuffixes.some(suffix => name.endsWith(suffix))
|
|
200
|
+
const importedArtifacts = new Set<string>()
|
|
201
|
+
for (const name of libScripts) {
|
|
202
|
+
const text = await readFile(join(lib, name), 'utf8')
|
|
203
|
+
for (const match of text.matchAll(/from\s*["'](\.\/[^"']+)["']/gu)) {
|
|
204
|
+
const target = match[1]!.slice(2)
|
|
205
|
+
if (!libNames.has(target)) fail(`lib/${name} imports missing artifact ${match[1]}`)
|
|
206
|
+
importedArtifacts.add(target)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const name of [...libScripts, ...importedArtifacts].sort()) {
|
|
210
|
+
if (!isAllowed(name)) fail(`lib/${name} is part of the load graph but the package files allowlist would drop it`)
|
|
211
|
+
}
|
|
212
|
+
let trackedLib: Set<string> | undefined
|
|
213
|
+
try {
|
|
214
|
+
trackedLib = new Set(execFileSync('git', ['ls-files', '--', 'packages/selector/lib'], { cwd: root, encoding: 'utf8' })
|
|
215
|
+
.split('\n').map(line => line.trim()).filter(Boolean))
|
|
216
|
+
} catch (cause: unknown) {
|
|
217
|
+
fail(`cannot read the tracked artifact list with git: ${(cause as Error).message}`)
|
|
218
|
+
}
|
|
219
|
+
if (trackedLib === undefined || trackedLib.size === 0) fail('git tracks no packages/selector/lib artifact; a git install would ship an unbuilt package')
|
|
220
|
+
for (const name of [...libScripts, ...importedArtifacts].sort()) {
|
|
221
|
+
const tracked = `packages/selector/lib/${name}`
|
|
222
|
+
if (!trackedLib!.has(tracked)) {
|
|
223
|
+
fail(`${tracked} is not committed; a git install would fetch an incomplete artifact graph`)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const clientArtifact = await readFile(join(root, 'packages/selector/lib/client.js'), 'utf8')
|
|
228
|
+
if (!clientArtifact.startsWith('window.__ModuleLoader__.load({')) {
|
|
229
|
+
fail('client.js is not a Harness lazy-CJS artifact')
|
|
230
|
+
}
|
|
231
|
+
if (!clientArtifact.includes('data-plugin-css') || !clientArtifact.includes('document.head.appendChild(tag)')) {
|
|
232
|
+
fail('client.js does not contain its tagged CSS injection')
|
|
233
|
+
}
|
|
234
|
+
if (/^\s*(?:import|export)\s/mu.test(clientArtifact)) fail('client.js contains ESM syntax')
|
|
235
|
+
if (/(?:\/home\/|[A-Za-z]:\\Users\\)/u.test(clientArtifact)) fail('client.js contains a developer absolute path')
|
|
236
|
+
if (clientArtifact.includes('sourceMappingURL')) fail('client.js contains a source map reference')
|
|
237
|
+
const clientRequires = [...clientArtifact.matchAll(/require\("([^"]+)"\)/gu)].map(match => match[1]!).filter(Boolean)
|
|
238
|
+
const allowedClientRequires = new Set([
|
|
239
|
+
'react',
|
|
240
|
+
'react/jsx-runtime',
|
|
241
|
+
'@deepseek-ai/dsh-client-ui-primitives',
|
|
242
|
+
])
|
|
243
|
+
for (const dependency of clientRequires) {
|
|
244
|
+
if (!allowedClientRequires.has(dependency)) fail(`client.js has unexpected external dependency ${dependency}`)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const collectSpecs = async (directory: string) => (await walk(directory))
|
|
248
|
+
.filter(path => /\.spec\.tsx?$/u.test(path))
|
|
249
|
+
.map(path => relative(root, path).replaceAll('\\', '/'))
|
|
250
|
+
.sort()
|
|
251
|
+
|
|
252
|
+
const runtimeSpecs = await collectSpecs(join(root, 'packages/selector/tests/runtime'))
|
|
253
|
+
const selectorSpecs = (await collectSpecs(join(root, 'packages/selector/tests')))
|
|
254
|
+
.filter(path => !path.startsWith('packages/selector/tests/runtime/'))
|
|
255
|
+
if (runtimeSpecs.some(path => !path.endsWith('.spec.ts'))) {
|
|
256
|
+
fail('Runtime test inventory contains a spec outside the active **/*.spec.ts project')
|
|
257
|
+
}
|
|
258
|
+
const selectorUnclassified = selectorSpecs.filter(path => path !== 'packages/selector/tests/cache-prefix-audit.spec.ts'
|
|
259
|
+
&& path !== 'packages/selector/tests/estimator-catalog.spec.ts'
|
|
260
|
+
&& path !== 'packages/selector/tests/built/client-artifact.spec.ts'
|
|
261
|
+
&& !path.endsWith('.host.spec.ts')
|
|
262
|
+
&& !path.endsWith('.client.spec.ts')
|
|
263
|
+
&& !path.endsWith('.client.spec.tsx'))
|
|
264
|
+
if (selectorUnclassified.length > 0) {
|
|
265
|
+
fail(`Selector test inventory contains unclassified specs: ${selectorUnclassified.join(', ')}`)
|
|
266
|
+
}
|
|
267
|
+
const rootTestConfig = await readFile(join(root, 'vitest.config.ts'), 'utf8')
|
|
268
|
+
for (const required of [
|
|
269
|
+
'packages/selector/tests/runtime/**/*.spec.ts',
|
|
270
|
+
'packages/selector/tests/**/*.host.spec.ts',
|
|
271
|
+
'packages/selector/tests/**/*.client.spec.{ts,tsx}',
|
|
272
|
+
'packages/selector/tests/cache-prefix-audit.spec.ts',
|
|
273
|
+
]) {
|
|
274
|
+
if (!rootTestConfig.includes(required)) fail(`vitest.config.ts lacks active inventory rule ${required}`)
|
|
275
|
+
}
|
|
276
|
+
const builtTestConfig = await readFile(join(root, 'vitest.built.config.ts'), 'utf8')
|
|
277
|
+
if (!builtTestConfig.includes('packages/selector/tests/built/**/*.spec.ts')) {
|
|
278
|
+
fail('vitest.built.config.ts lacks the built client artifact inventory rule')
|
|
279
|
+
}
|
|
280
|
+
const forbiddenTestDependencies = [
|
|
281
|
+
/(?:from\s+|import\s*\(|require\s*\()\s*['"]@deepseek-ai\/dsh-compaction-tool-result-pruner/u,
|
|
282
|
+
/(?:from\s+|import\s*\(|require\s*\()\s*['"]@deepseek-ai\/dsh-tool-context-retrieve/u,
|
|
283
|
+
/\.\.\/\.\.\/\.\.\/(?:core|client)\//u,
|
|
284
|
+
]
|
|
285
|
+
for (const path of [...runtimeSpecs, ...selectorSpecs]) {
|
|
286
|
+
const text = await readFile(join(root, path), 'utf8')
|
|
287
|
+
if (forbiddenTestDependencies.some(pattern => pattern.test(text))) {
|
|
288
|
+
fail(`${path} depends on a removed core-extension or monorepo test contract`)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const runtime = await import(new URL('../packages/selector/lib/pruner.js', import.meta.url).href)
|
|
293
|
+
const defaults = runtime.DEFAULT_CUSTOM_COMPRESSION_POLICY as Record<string, Record<string, unknown>> | undefined
|
|
294
|
+
if ((defaults?.history as Record<string, number>)?.trigger !== 500_000) fail('Custom History default is not 500000')
|
|
295
|
+
if ((defaults?.tailTrim as Record<string, unknown>)?.trigger !== 700_000
|
|
296
|
+
|| (defaults?.tailTrim as Record<string, unknown>)?.enabled !== false) {
|
|
297
|
+
fail('Custom TailTrim default is not disabled at 700000')
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.info('release verification: OK')
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Test inventory
|
|
2
|
+
|
|
3
|
+
Every checked-in spec belongs to an active test project. There is no silent
|
|
4
|
+
legacy-test allowlist.
|
|
5
|
+
|
|
6
|
+
## Root suite
|
|
7
|
+
|
|
8
|
+
- Runtime: `packages/selector/tests/runtime/**/*.spec.ts` under the `runtime`
|
|
9
|
+
project.
|
|
10
|
+
- Selector Host: `cache-prefix-audit.spec.ts`, `estimator-catalog.spec.ts` plus
|
|
11
|
+
every `*.host.spec.ts` under the `selector-host` project.
|
|
12
|
+
- Selector client: every `*.client.spec.ts` or `*.client.spec.tsx` under the
|
|
13
|
+
`selector-client` project.
|
|
14
|
+
- Built browser artifact: `packages/selector/tests/built/client-artifact.spec.ts`
|
|
15
|
+
under the separate `vitest.built.config.ts` gate, after `pnpm build`.
|
|
16
|
+
|
|
17
|
+
The root `pnpm test`, the package-local `pnpm test` command, and
|
|
18
|
+
`pnpm test:built` are release gates. `scripts-dist/verify-release.js` (compiled from `scripts/verify-release.ts`) compares the
|
|
19
|
+
checked-in spec inventory with these project rules so a new test cannot be
|
|
20
|
+
silently excluded.
|
|
21
|
+
|
|
22
|
+
## Removed transition tests
|
|
23
|
+
|
|
24
|
+
The standalone extraction initially carried twelve specs copied from the
|
|
25
|
+
integrated Harness worktree. They depended on removed private packages,
|
|
26
|
+
monorepo-relative fixtures, or the retired `compaction/group-trim` event and
|
|
27
|
+
were not executable in a public checkout. They were removed instead of
|
|
28
|
+
reintroducing Harness core dependencies.
|
|
29
|
+
|
|
30
|
+
Their supported behavior is covered by active public tests:
|
|
31
|
+
|
|
32
|
+
- policy resolution, reducers, exact measurement, Fresh, Aggregate, routine
|
|
33
|
+
and capacity-pressure History, Native pruning, TailTrim publication,
|
|
34
|
+
recovery, replay and orphan fail-open behavior:
|
|
35
|
+
`packages/selector/tests/runtime/public/public-runtime.spec.ts`;
|
|
36
|
+
- Loader composition, preset overlay, Minimal pause/restore and parent/child
|
|
37
|
+
service identity: `packages/selector/tests/preset-overlay-loader.e2e.host.spec.ts`;
|
|
38
|
+
- package/export/tarball contract:
|
|
39
|
+
`packages/selector/tests/public/package-contract.client.spec.ts` and
|
|
40
|
+
`scripts-dist/packed-install-e2e.js` (compiled from `scripts/packed-install-e2e.ts`);
|
|
41
|
+
- profile and Custom editor behavior:
|
|
42
|
+
`packages/selector/tests/profiles.client.spec.tsx`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2023",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noUncheckedIndexedAccess": true,
|
|
9
|
+
"exactOptionalPropertyTypes": true,
|
|
10
|
+
"verbatimModuleSyntax": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
"types": ["node", "vitest/globals"]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"allowImportingTsExtensions": false,
|
|
7
|
+
"rootDir": "scripts",
|
|
8
|
+
"outDir": "scripts-dist",
|
|
9
|
+
"noEmit": false,
|
|
10
|
+
"types": ["node"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["scripts/**/*.ts"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"composite": false
|
|
6
|
+
},
|
|
7
|
+
"include": [
|
|
8
|
+
"packages/selector/src/**/*.ts",
|
|
9
|
+
"packages/selector/src/**/*.tsx",
|
|
10
|
+
"packages/selector/tests/**/*.ts",
|
|
11
|
+
"packages/selector/tests/**/*.tsx",
|
|
12
|
+
"vitest.config.ts",
|
|
13
|
+
"vitest.built.config.ts"
|
|
14
|
+
]
|
|
15
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
test: {
|
|
6
|
+
passWithNoTests: false,
|
|
7
|
+
projects: [
|
|
8
|
+
{
|
|
9
|
+
test: {
|
|
10
|
+
name: 'runtime',
|
|
11
|
+
environment: 'node',
|
|
12
|
+
include: ['packages/selector/tests/runtime/**/*.spec.ts'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
test: {
|
|
17
|
+
name: 'selector-host',
|
|
18
|
+
environment: 'node',
|
|
19
|
+
include: [
|
|
20
|
+
'packages/selector/tests/cache-prefix-audit.spec.ts',
|
|
21
|
+
'packages/selector/tests/estimator-catalog.spec.ts',
|
|
22
|
+
'packages/selector/tests/**/*.host.spec.ts',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
resolve: {
|
|
28
|
+
alias: {
|
|
29
|
+
'@deepseek-ai/dsh-client-ui-primitives': fileURLToPath(new URL(
|
|
30
|
+
'./packages/selector/tests/support/ui-primitives.tsx',
|
|
31
|
+
import.meta.url,
|
|
32
|
+
)),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
test: {
|
|
36
|
+
name: 'selector-client',
|
|
37
|
+
environment: 'jsdom',
|
|
38
|
+
include: ['packages/selector/tests/**/*.client.spec.{ts,tsx}'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{assets → packages/selector/assets}/screenshots/context-compression-selector-profiles.jpg
RENAMED
|
File without changes
|
/package/{assets → packages/selector/assets}/screenshots/context-compression-selector-settings.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|