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.
Files changed (148) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/workflows/ci.yml +39 -0
  3. package/CHANGELOG.ja.md +39 -0
  4. package/CHANGELOG.ko.md +39 -0
  5. package/CHANGELOG.md +135 -0
  6. package/CHANGELOG.zh.md +39 -0
  7. package/CONTRIBUTING.md +22 -0
  8. package/README.ja.md +104 -0
  9. package/README.ko.md +103 -0
  10. package/README.md +89 -12
  11. package/README.zh.md +87 -12
  12. package/SECURITY.md +18 -0
  13. package/THIRD_PARTY_NOTICES.md +7 -31
  14. package/docs/installation.ja.md +76 -0
  15. package/docs/installation.ko.md +76 -0
  16. package/docs/installation.md +76 -0
  17. package/docs/installation.zh.md +76 -0
  18. package/docs/repair-log.md +582 -0
  19. package/eslint.config.js +30 -0
  20. package/package.json +84 -81
  21. package/packages/selector/LICENSE +21 -0
  22. package/packages/selector/README.md +26 -0
  23. package/packages/selector/README.zh.md +26 -0
  24. package/packages/selector/THIRD_PARTY_NOTICES.md +38 -0
  25. package/packages/selector/docs/history-tool-call-working-set-spec.md +112 -0
  26. package/packages/selector/docs/native-tool-result-selector-spec.md +34 -0
  27. package/packages/selector/docs/subagent-cache-reuse-spec.md +46 -0
  28. package/packages/selector/lib/style.css +308 -0
  29. package/packages/selector/package.json +115 -0
  30. package/{screenshots.json → packages/selector/screenshots.json} +6 -6
  31. package/packages/selector/src/client/CompressionProfileControls.tsx +229 -0
  32. package/packages/selector/src/client/CompressionProfileSelector.module.css +170 -0
  33. package/packages/selector/src/client/CompressionProfileSelector.tsx +79 -0
  34. package/packages/selector/src/client/CustomPolicyEditor.tsx +216 -0
  35. package/packages/selector/src/client/EstimatorControls.tsx +281 -0
  36. package/packages/selector/src/client/decode.ts +49 -0
  37. package/packages/selector/src/client/index.ts +111 -0
  38. package/packages/selector/src/client/locales.ts +198 -0
  39. package/packages/selector/src/client/preset-options.ts +70 -0
  40. package/packages/selector/src/client/settings-section.tsx +126 -0
  41. package/packages/selector/src/css-modules.d.ts +6 -0
  42. package/packages/selector/src/deepseek-v4-tokenizer.ts +210 -0
  43. package/packages/selector/src/estimator-catalog.ts +104 -0
  44. package/packages/selector/src/index.ts +327 -0
  45. package/packages/selector/src/invariant.ts +113 -0
  46. package/packages/selector/src/preset-overlay.ts +567 -0
  47. package/packages/selector/src/profiles.ts +342 -0
  48. package/packages/selector/src/pruner/content.ts +188 -0
  49. package/packages/selector/src/pruner/session.ts +94 -0
  50. package/packages/selector/src/pruner/state.ts +43 -0
  51. package/packages/selector/src/pruner/tuning.ts +23 -0
  52. package/packages/selector/src/pruner/types.ts +60 -0
  53. package/packages/selector/src/pruner.ts +2144 -0
  54. package/packages/selector/src/runtime/adaptive-cost.ts +194 -0
  55. package/packages/selector/src/runtime/audit.ts +215 -0
  56. package/packages/selector/src/runtime/config.ts +613 -0
  57. package/packages/selector/src/runtime/custom-policy.ts +278 -0
  58. package/packages/selector/src/runtime/deepseek-official-pricing.ts +298 -0
  59. package/packages/selector/src/runtime/deepseek-v4-vision-tokens.ts +254 -0
  60. package/packages/selector/src/runtime/measurement.ts +403 -0
  61. package/packages/selector/src/runtime/reducers.ts +656 -0
  62. package/packages/selector/src/runtime/retrieve.ts +457 -0
  63. package/packages/selector/src/runtime/session-events.ts +17 -0
  64. package/packages/selector/src/runtime/tail-trim.ts +166 -0
  65. package/packages/selector/src/runtime/token-count.ts +72 -0
  66. package/packages/selector/src/runtime/tokenpilot/dedup.ts +81 -0
  67. package/packages/selector/src/runtime/tokenpilot/estimator.ts +183 -0
  68. package/packages/selector/src/runtime/tokenpilot/locator.ts +128 -0
  69. package/packages/selector/src/runtime/tokenpilot/read-state.ts +77 -0
  70. package/packages/selector/src/runtime/types.ts +309 -0
  71. package/packages/selector/src/runtime/value.ts +48 -0
  72. package/packages/selector/tests/auto-compact.client.spec.tsx +226 -0
  73. package/packages/selector/tests/built/client-artifact.spec.ts +51 -0
  74. package/packages/selector/tests/cache-prefix-audit.spec.ts +123 -0
  75. package/packages/selector/tests/code-skeleton.client.spec.ts +88 -0
  76. package/packages/selector/tests/custom-contract.client.spec.ts +202 -0
  77. package/packages/selector/tests/estimator-catalog.spec.ts +70 -0
  78. package/packages/selector/tests/estimator-channel.client.spec.tsx +247 -0
  79. package/packages/selector/tests/estimator-route-registration.host.spec.ts +176 -0
  80. package/packages/selector/tests/host-preset-overlay.host.spec.ts +204 -0
  81. package/packages/selector/tests/preset-options-write.client.spec.ts +181 -0
  82. package/packages/selector/tests/preset-overlay-loader.e2e.host.spec.ts +196 -0
  83. package/packages/selector/tests/preset-overlay.host.spec.ts +243 -0
  84. package/packages/selector/tests/profiles.client.spec.tsx +434 -0
  85. package/packages/selector/tests/public/package-contract.client.spec.ts +33 -0
  86. package/packages/selector/tests/runtime/adaptive-cost.spec.ts +167 -0
  87. package/packages/selector/tests/runtime/audit.spec.ts +129 -0
  88. package/packages/selector/tests/runtime/auto-compact-config.spec.ts +523 -0
  89. package/packages/selector/tests/runtime/code-skeleton.spec.ts +141 -0
  90. package/packages/selector/tests/runtime/deepseek-official-pricing.spec.ts +186 -0
  91. package/packages/selector/tests/runtime/deepseek-v4-tokenizer.spec.ts +122 -0
  92. package/packages/selector/tests/runtime/deepseek-v4-vision-tokens.spec.ts +122 -0
  93. package/packages/selector/tests/runtime/fixtures/profile-baseline.json +273 -0
  94. package/packages/selector/tests/runtime/fixtures/tokenizer-golden.json +106 -0
  95. package/packages/selector/tests/runtime/fixtures/vision-golden.json +459 -0
  96. package/packages/selector/tests/runtime/public/public-runtime.spec.ts +2531 -0
  97. package/packages/selector/tests/runtime/session-events.spec.ts +27 -0
  98. package/packages/selector/tests/runtime/tokenizer-golden.spec.ts +53 -0
  99. package/packages/selector/tests/runtime/tokenpilot/dedup.spec.ts +52 -0
  100. package/packages/selector/tests/runtime/tokenpilot/estimator.spec.ts +56 -0
  101. package/packages/selector/tests/runtime/tokenpilot/locator.spec.ts +76 -0
  102. package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +100 -0
  103. package/packages/selector/tests/runtime/tokenpilot/read-state.spec.ts +58 -0
  104. package/packages/selector/tests/runtime/value.spec.ts +23 -0
  105. package/packages/selector/tests/standing-generation.host.spec.ts +631 -0
  106. package/packages/selector/tests/subagent-cache-reuse.host.spec.ts +250 -0
  107. package/packages/selector/tests/support/cache-prefix-audit.ts +105 -0
  108. package/packages/selector/tests/support/mock-adapter.ts +37 -0
  109. package/packages/selector/tests/support/ui-primitives.tsx +34 -0
  110. package/packages/selector/tsconfig.json +11 -0
  111. package/packages/selector/tsdown.client.config.ts +102 -0
  112. package/packages/selector/tsdown.config.ts +20 -0
  113. package/pnpm-workspace.yaml +19 -0
  114. package/scripts/capture-profile-baseline.ts +80 -0
  115. package/scripts/generate-tokenizer-fixtures.py +81 -0
  116. package/scripts/generate-vision-fixtures.py +208 -0
  117. package/scripts/packed-components-smoke.ts +713 -0
  118. package/scripts/packed-install-e2e.ts +1072 -0
  119. package/scripts/verify-release.ts +300 -0
  120. package/tests/TEST_INVENTORY.md +42 -0
  121. package/tsconfig.base.json +18 -0
  122. package/tsconfig.json +7 -0
  123. package/tsconfig.scripts.json +13 -0
  124. package/tsconfig.tests.json +15 -0
  125. package/vitest.built.config.ts +9 -0
  126. package/vitest.config.ts +43 -0
  127. /package/{assets → packages/selector/assets}/deepseek-v4/LICENSE.DeepSeek-V4-Pro.txt +0 -0
  128. /package/{assets → packages/selector/assets}/deepseek-v4/manifest.json +0 -0
  129. /package/{assets → packages/selector/assets}/deepseek-v4/tokenizer.json +0 -0
  130. /package/{assets → packages/selector/assets}/deepseek-v4/tokenizer_config.json +0 -0
  131. /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/LICENSE.DeepSeek-V4-Flash-Vision-Exp.txt +0 -0
  132. /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/manifest.json +0 -0
  133. /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/tokenizer.json +0 -0
  134. /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/tokenizer_config.json +0 -0
  135. /package/{assets → packages/selector/assets}/screenshots/context-compression-selector-profiles.jpg +0 -0
  136. /package/{assets → packages/selector/assets}/screenshots/context-compression-selector-settings.png +0 -0
  137. /package/{cordis.patch.yml → packages/selector/cordis.patch.yml} +0 -0
  138. /package/{dsh.plugin.json → packages/selector/dsh.plugin.json} +0 -0
  139. /package/{lib → packages/selector/lib}/client.d.ts +0 -0
  140. /package/{lib → packages/selector/lib}/client.js +0 -0
  141. /package/{lib → packages/selector/lib}/config.js +0 -0
  142. /package/{lib → packages/selector/lib}/index.d.ts +0 -0
  143. /package/{lib → packages/selector/lib}/index.js +0 -0
  144. /package/{lib → packages/selector/lib}/invariant.d.ts +0 -0
  145. /package/{lib → packages/selector/lib}/invariant.js +0 -0
  146. /package/{lib → packages/selector/lib}/pruner.d.ts +0 -0
  147. /package/{lib → packages/selector/lib}/pruner.js +0 -0
  148. /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,7 @@
1
+ {
2
+ "extends": "./tsconfig.base.json",
3
+ "files": [],
4
+ "references": [
5
+ { "path": "./packages/selector" }
6
+ ]
7
+ }
@@ -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
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: 'jsdom',
6
+ include: ['packages/selector/tests/built/**/*.spec.ts'],
7
+ passWithNoTests: false,
8
+ },
9
+ })
@@ -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
File without changes
File without changes