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,1072 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-ts-comment -- standalone E2E test script; DSH internal types unavailable */
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
// internals and cordis augmented services outside the scripts tsconfig scope.
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import { spawn } from 'node:child_process'
|
|
6
|
+
import { createReadStream } from 'node:fs'
|
|
7
|
+
import { copyFile, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, writeFile } from 'node:fs/promises'
|
|
8
|
+
import { readFileSync } from 'node:fs'
|
|
9
|
+
import { createServer, IncomingMessage, ServerResponse, Server } from 'node:http'
|
|
10
|
+
import { createRequire } from 'node:module'
|
|
11
|
+
import { tmpdir } from 'node:os'
|
|
12
|
+
import { basename, dirname, join } from 'node:path'
|
|
13
|
+
import { Readable } from 'node:stream'
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
15
|
+
|
|
16
|
+
const root = fileURLToPath(new URL('..', import.meta.url))
|
|
17
|
+
// Release mode (default) is fail-closed: the upgrade leg and the official
|
|
18
|
+
// clean-harness lifecycle must both run to green or the gate exits non-zero.
|
|
19
|
+
// Set DSH_E2E_MODE=dev for an offline smoke with explicit skip markers.
|
|
20
|
+
const e2eMode: 'dev' | 'release' = process.env.DSH_E2E_MODE === 'dev' ? 'dev' : 'release'
|
|
21
|
+
const packages = [
|
|
22
|
+
{ directory: join(root, 'packages/selector') },
|
|
23
|
+
]
|
|
24
|
+
const officialHostPackages = [
|
|
25
|
+
'@deepseek-ai/cordis@4.0.1',
|
|
26
|
+
'@deepseek-ai/cordis-plugin-group@1.0.1',
|
|
27
|
+
'@deepseek-ai/cordis-plugin-include@1.0.6',
|
|
28
|
+
'@deepseek-ai/cordis-plugin-loader@1.0.2',
|
|
29
|
+
'@deepseek-ai/dsh-agent@0.1.1-rc.2',
|
|
30
|
+
'@deepseek-ai/dsh-agent-loop@0.1.1-rc.2',
|
|
31
|
+
'@deepseek-ai/dsh-agent-loop-testkit@0.1.1-rc.2',
|
|
32
|
+
'@deepseek-ai/dsh-agent-presets@0.1.1-rc.2',
|
|
33
|
+
'@deepseek-ai/dsh-commands@0.1.1-rc.2',
|
|
34
|
+
'@deepseek-ai/dsh-command-compact@0.1.1-rc.2',
|
|
35
|
+
'@deepseek-ai/dsh-compaction-basic@0.1.1-rc.2',
|
|
36
|
+
'@deepseek-ai/dsh-llm@0.1.1-rc.2',
|
|
37
|
+
'@deepseek-ai/dsh-session@0.1.1-rc.2',
|
|
38
|
+
'@deepseek-ai/dsh-scope@0.1.1-rc.2',
|
|
39
|
+
'@deepseek-ai/dsh-settings@0.1.1-rc.2',
|
|
40
|
+
'@deepseek-ai/dsh-system-prompt@0.1.1-rc.2',
|
|
41
|
+
'@deepseek-ai/dsh-token-meter@0.1.1-rc.2',
|
|
42
|
+
'@deepseek-ai/dsh-tools@0.1.1-rc.2',
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
// On Windows npm and pnpm only exist as .cmd shims, which Node refuses to
|
|
46
|
+
// spawn directly, so those two are routed through the shell.
|
|
47
|
+
const spawnTool = (command: string, args: string[], options?: Record<string, unknown>) => spawn(
|
|
48
|
+
command,
|
|
49
|
+
args,
|
|
50
|
+
process.platform === 'win32' && (command === 'npm' || command === 'pnpm')
|
|
51
|
+
? { ...options, shell: true }
|
|
52
|
+
: options,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const run = (command: string, args: string[], options: Record<string, unknown> = {}): Promise<void> => new Promise((resolve, reject) => {
|
|
56
|
+
const child = spawnTool(command, args, { stdio: 'inherit', ...options })
|
|
57
|
+
child.once('error', reject)
|
|
58
|
+
child.once('exit', (code, signal) => {
|
|
59
|
+
if (code === 0) resolve()
|
|
60
|
+
else reject(new Error(`${command} exited ${code ?? `from signal ${signal}`}`))
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const capture = (command: string, args: string[], options: Record<string, unknown> = {}): Promise<{ stdout: string; stderr: string }> => new Promise((resolve, reject) => {
|
|
65
|
+
const child = spawnTool(command, args, { ...options, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
66
|
+
let stdout = ''
|
|
67
|
+
let stderr = ''
|
|
68
|
+
child.stdout!.setEncoding('utf8')
|
|
69
|
+
child.stderr!.setEncoding('utf8')
|
|
70
|
+
child.stdout!.on('data', (chunk: string) => { stdout += chunk })
|
|
71
|
+
child.stderr!.on('data', (chunk: string) => { stderr += chunk })
|
|
72
|
+
child.once('error', reject)
|
|
73
|
+
child.once('exit', (code, signal) => {
|
|
74
|
+
if (code === 0) resolve({ stdout, stderr })
|
|
75
|
+
else reject(new Error([
|
|
76
|
+
`${command} exited ${code ?? `from signal ${signal}`}`,
|
|
77
|
+
stdout.trim(),
|
|
78
|
+
stderr.trim(),
|
|
79
|
+
].filter(Boolean).join('\n')))
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
interface CaptureOutcome {
|
|
84
|
+
code: number | null
|
|
85
|
+
signal: NodeJS.Signals | null
|
|
86
|
+
stdout: string
|
|
87
|
+
stderr: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const captureOutcome = (command: string, args: string[], options: Record<string, unknown> = {}): Promise<CaptureOutcome> => new Promise((resolve, reject) => {
|
|
91
|
+
const child = spawnTool(command, args, { ...options, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
92
|
+
let stdout = ''
|
|
93
|
+
let stderr = ''
|
|
94
|
+
child.stdout!.setEncoding('utf8')
|
|
95
|
+
child.stderr!.setEncoding('utf8')
|
|
96
|
+
child.stdout!.on('data', (chunk: string) => { stdout += chunk })
|
|
97
|
+
child.stderr!.on('data', (chunk: string) => { stderr += chunk })
|
|
98
|
+
child.once('error', reject)
|
|
99
|
+
child.once('exit', (code, signal) => resolve({ code, signal, stdout, stderr }))
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const allocateLoopbackPort = (): Promise<number> => new Promise((resolve, reject) => {
|
|
103
|
+
const server = createServer()
|
|
104
|
+
server.once('error', reject)
|
|
105
|
+
server.listen(0, '127.0.0.1', () => {
|
|
106
|
+
const address = server.address()
|
|
107
|
+
if (address === null || typeof address === 'string') {
|
|
108
|
+
server.close(() => reject(new Error('could not allocate a loopback port for the official profile probe')))
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
server.close(error => error === undefined ? resolve(address.port) : reject(error))
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const assert = (condition: unknown, message: string): asserts condition => {
|
|
116
|
+
if (!condition) throw new Error(`packed Host smoke: ${message}`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface Violation {
|
|
120
|
+
label: string
|
|
121
|
+
pattern: RegExp
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function scanPublishedTree(packageRoot: string) {
|
|
125
|
+
const pending = [packageRoot]
|
|
126
|
+
const violations: string[] = []
|
|
127
|
+
const forbidden: Violation[] = [
|
|
128
|
+
{ label: 'developer absolute path', pattern: /(?:\/home\/|[A-Za-z]:\\Users\\)/u },
|
|
129
|
+
{ label: 'OpenAI-style secret', pattern: /\bsk-[A-Za-z0-9_-]{20,}\b/u },
|
|
130
|
+
{ label: 'NPM token', pattern: /\bnpm_[A-Za-z0-9]{20,}\b/u },
|
|
131
|
+
{ label: 'GitHub token', pattern: /\b(?:ghp_|github_pat_)[A-Za-z0-9_]{20,}\b/u },
|
|
132
|
+
{ label: 'assigned DeepSeek API key', pattern: /DEEPSEEK_API_KEY\s*=\s*[^\s"']+/u },
|
|
133
|
+
]
|
|
134
|
+
while (pending.length > 0) {
|
|
135
|
+
const directory = pending.pop()!
|
|
136
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
137
|
+
const path = join(directory, entry.name)
|
|
138
|
+
if (entry.isDirectory()) {
|
|
139
|
+
pending.push(path)
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
if (!/\.(?:js|d\.ts|json|md|ya?ml|txt)$/u.test(entry.name)
|
|
143
|
+
|| entry.name === 'tokenizer.json') continue
|
|
144
|
+
const text = await readFile(path, 'utf8')
|
|
145
|
+
for (const rule of forbidden) {
|
|
146
|
+
if (rule.pattern.test(text)) violations.push(`${path}: ${rule.label}`)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (violations.length > 0) throw new Error(`packed content scan failed:\n${violations.join('\n')}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function runPackedHostSmoke(consumerRoot: string) {
|
|
154
|
+
const consumerRequire = createRequire(join(consumerRoot, 'package.json'))
|
|
155
|
+
const load = async (specifier: string) => import(pathToFileURL(consumerRequire.resolve(specifier)).href)
|
|
156
|
+
const [
|
|
157
|
+
cordis,
|
|
158
|
+
groupModule,
|
|
159
|
+
includeModule,
|
|
160
|
+
loaderModule,
|
|
161
|
+
agentModule,
|
|
162
|
+
agentLoopModule,
|
|
163
|
+
presetsModule,
|
|
164
|
+
commandsModule,
|
|
165
|
+
llmModule,
|
|
166
|
+
sessionModule,
|
|
167
|
+
scopeModule,
|
|
168
|
+
settingsModule,
|
|
169
|
+
systemPromptModule,
|
|
170
|
+
tokenMeterModule,
|
|
171
|
+
toolsModule,
|
|
172
|
+
selectorModule,
|
|
173
|
+
] = await Promise.all([
|
|
174
|
+
load('@deepseek-ai/cordis'),
|
|
175
|
+
load('@deepseek-ai/cordis-plugin-group'),
|
|
176
|
+
load('@deepseek-ai/cordis-plugin-include'),
|
|
177
|
+
load('@deepseek-ai/cordis-plugin-loader'),
|
|
178
|
+
load('@deepseek-ai/dsh-agent'),
|
|
179
|
+
load('@deepseek-ai/dsh-agent-loop'),
|
|
180
|
+
load('@deepseek-ai/dsh-agent-presets'),
|
|
181
|
+
load('@deepseek-ai/dsh-commands'),
|
|
182
|
+
load('@deepseek-ai/dsh-llm'),
|
|
183
|
+
load('@deepseek-ai/dsh-session'),
|
|
184
|
+
load('@deepseek-ai/dsh-scope'),
|
|
185
|
+
load('@deepseek-ai/dsh-settings'),
|
|
186
|
+
load('@deepseek-ai/dsh-system-prompt'),
|
|
187
|
+
load('@deepseek-ai/dsh-token-meter'),
|
|
188
|
+
load('@deepseek-ai/dsh-tools'),
|
|
189
|
+
load('dsh-context-compression-improved'),
|
|
190
|
+
]) as any[]
|
|
191
|
+
|
|
192
|
+
class MemorySettings extends settingsModule.SettingsProvider {
|
|
193
|
+
writable = true
|
|
194
|
+
|
|
195
|
+
load() {
|
|
196
|
+
return Promise.resolve({})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
persist() {
|
|
200
|
+
return Promise.resolve()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const presetRoot = join(consumerRoot, 'preset-fixtures')
|
|
205
|
+
await mkdir(presetRoot)
|
|
206
|
+
const marker = join(presetRoot, 'marker.mjs')
|
|
207
|
+
await writeFile(marker, 'export function apply() {}\n')
|
|
208
|
+
const source = `- id: source-marker\n name: ${JSON.stringify(marker)}\n`
|
|
209
|
+
for (const id of ['standard', 'custom', 'minimal']) {
|
|
210
|
+
const directory = join(presetRoot, id)
|
|
211
|
+
await mkdir(directory)
|
|
212
|
+
await writeFile(join(directory, presetsModule.COMPOSITION_FILE), source)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const runtime = new cordis.Context()
|
|
216
|
+
try {
|
|
217
|
+
runtime.baseUrl = `${pathToFileURL(presetRoot).href}/`
|
|
218
|
+
await runtime.plugin(loaderModule.default)
|
|
219
|
+
runtime.loader.builtins.include = includeModule.default
|
|
220
|
+
runtime.loader.builtins.group = groupModule.default
|
|
221
|
+
await runtime.plugin(llmModule.default)
|
|
222
|
+
await runtime.plugin(sessionModule.default)
|
|
223
|
+
await runtime.plugin(systemPromptModule.default, { persona: '' })
|
|
224
|
+
await runtime.plugin(toolsModule.default)
|
|
225
|
+
await runtime.plugin(agentModule.default)
|
|
226
|
+
await runtime.plugin(agentLoopModule.default, { agents: [] })
|
|
227
|
+
await runtime.plugin(commandsModule.default)
|
|
228
|
+
await runtime.plugin(tokenMeterModule.default)
|
|
229
|
+
await runtime.plugin(MemorySettings)
|
|
230
|
+
await runtime.plugin(presetsModule.default, {
|
|
231
|
+
default: 'standard',
|
|
232
|
+
roots: [{ path: presetRoot, trust: 'system' }],
|
|
233
|
+
includeUserRoot: false,
|
|
234
|
+
})
|
|
235
|
+
await runtime.plugin({
|
|
236
|
+
apply: (selectorCtx: any) => selectorModule.apply(selectorCtx, { presetOverlay: true }),
|
|
237
|
+
}).await()
|
|
238
|
+
|
|
239
|
+
const createAgent = async (sessionId: string, preset: string) => {
|
|
240
|
+
const handle = await runtime.agents.create({
|
|
241
|
+
sessionId: sessionModule.SessionId(sessionId),
|
|
242
|
+
setup: async (agentCtx: any) => void await runtime.agentPresets.mount(agentCtx, preset),
|
|
243
|
+
})
|
|
244
|
+
return handle.agent
|
|
245
|
+
}
|
|
246
|
+
const hasRetrieve = (agent: any) => runtime.tools.get(
|
|
247
|
+
'context_compression_retrieve',
|
|
248
|
+
scopeModule.scopeOf(agent.ctx),
|
|
249
|
+
) !== undefined
|
|
250
|
+
const hasCompact = (agent: any) => runtime.commands.list(agent)
|
|
251
|
+
.some((command: any) => command.name === 'compact')
|
|
252
|
+
const hasCompleteStack = (agent: any) =>
|
|
253
|
+
runtime.agentPresets.serviceFor(agent, 'toolResultPruner') !== undefined
|
|
254
|
+
&& runtime.agentPresets.serviceFor(agent, 'compaction') !== undefined
|
|
255
|
+
&& hasRetrieve(agent)
|
|
256
|
+
&& hasCompact(agent)
|
|
257
|
+
const hasNoStack = (agent: any) =>
|
|
258
|
+
runtime.agentPresets.serviceFor(agent, 'toolResultPruner') === undefined
|
|
259
|
+
&& runtime.agentPresets.serviceFor(agent, 'compaction') === undefined
|
|
260
|
+
&& !hasRetrieve(agent)
|
|
261
|
+
&& !hasCompact(agent)
|
|
262
|
+
|
|
263
|
+
const sourcePreset = await runtime.agentPresets.resolve('standard')
|
|
264
|
+
const sourceText = await runtime.agentPresets.read('standard')
|
|
265
|
+
const agent = await createAgent('packed-host-standard', 'standard')
|
|
266
|
+
assert(hasCompleteStack(agent), 'standard preset did not mount the complete compression stack')
|
|
267
|
+
assert((await runtime.agentPresets.resolve('standard')).path === sourcePreset.path,
|
|
268
|
+
'overlay changed the source preset path')
|
|
269
|
+
assert(await runtime.agentPresets.read('standard') === sourceText,
|
|
270
|
+
'overlay changed the source preset contents')
|
|
271
|
+
assert(!sourceText.includes('tool-result-pruner'), 'source preset was mutated')
|
|
272
|
+
|
|
273
|
+
await runtime.agentPresets.recompose(agent.ctx, 'minimal')
|
|
274
|
+
assert(hasNoStack(agent), 'exact Minimal preset retained compression')
|
|
275
|
+
await runtime.agentPresets.recompose(agent.ctx, 'custom')
|
|
276
|
+
assert(hasCompleteStack(agent), 'compression did not return after leaving Minimal')
|
|
277
|
+
|
|
278
|
+
const parent = await createAgent('packed-host-parent', 'standard')
|
|
279
|
+
const childHandle = await runtime.agents.create({
|
|
280
|
+
sessionId: sessionModule.SessionId('packed-host-child'),
|
|
281
|
+
setup: (childCtx: any) => void runtime.agentPresets.composeFrom(childCtx, parent.ctx),
|
|
282
|
+
})
|
|
283
|
+
const child = childHandle.agent
|
|
284
|
+
const original = Symbol.for('cordis.original')
|
|
285
|
+
const parentPruner = runtime.agentPresets.serviceFor(parent, 'toolResultPruner')
|
|
286
|
+
const childPruner = runtime.agentPresets.serviceFor(child, 'toolResultPruner')
|
|
287
|
+
const parentCompaction = runtime.agentPresets.serviceFor(parent, 'compaction')
|
|
288
|
+
const childCompaction = runtime.agentPresets.serviceFor(child, 'compaction')
|
|
289
|
+
assert(parentPruner !== undefined && childPruner !== undefined,
|
|
290
|
+
'parent or child pruner is missing')
|
|
291
|
+
assert(parentCompaction !== undefined && childCompaction !== undefined,
|
|
292
|
+
'parent or child compaction engine is missing')
|
|
293
|
+
assert(Object.is((childPruner as any)[original], (parentPruner as any)[original]),
|
|
294
|
+
'child did not inherit the parent pruner instance')
|
|
295
|
+
assert(Object.is((childCompaction as any)[original], (parentCompaction as any)[original]),
|
|
296
|
+
'child did not inherit the parent compaction instance')
|
|
297
|
+
assert(hasRetrieve(child) && hasCompact(child),
|
|
298
|
+
'child lacks the inherited retrieve tool or compact command')
|
|
299
|
+
|
|
300
|
+
const namespace = settingsModule.settingsNamespace('context-compression')
|
|
301
|
+
const settings = runtime.settings.get(namespace)
|
|
302
|
+
assert(settings.custom.history.trigger === 500_000,
|
|
303
|
+
'packed Host did not expose the 500000 History default')
|
|
304
|
+
assert(settings.custom.tailTrim.enabled === false && settings.custom.tailTrim.trigger === 700_000,
|
|
305
|
+
'packed Host did not expose disabled TailTrim at 700000')
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
officialHostPackages: officialHostPackages.length,
|
|
309
|
+
standardPreset: 'complete-stack',
|
|
310
|
+
minimalPreset: 'no-stack',
|
|
311
|
+
postMinimalPreset: 'complete-stack',
|
|
312
|
+
childServices: 'same-parent-instances',
|
|
313
|
+
sourcePreset: 'unchanged',
|
|
314
|
+
defaults: { history: 500_000, tailTrim: { enabled: false, trigger: 700_000 } },
|
|
315
|
+
}
|
|
316
|
+
} finally {
|
|
317
|
+
await runtime.fiber.dispose()
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function runOfficialCloneCliSmoke(
|
|
322
|
+
referenceRoot: string,
|
|
323
|
+
registry: string,
|
|
324
|
+
upgradeFrom: string | undefined,
|
|
325
|
+
candidateVersion: string,
|
|
326
|
+
) {
|
|
327
|
+
const clone = await realpath(referenceRoot)
|
|
328
|
+
const git = async (...args: string[]) => (await capture('git', args, { cwd: clone })).stdout.trim()
|
|
329
|
+
const before = {
|
|
330
|
+
tag: await git('describe', '--tags', '--exact-match'),
|
|
331
|
+
commit: await git('rev-parse', 'HEAD'),
|
|
332
|
+
tree: await git('rev-parse', 'HEAD^{tree}'),
|
|
333
|
+
status: await git('status', '--porcelain'),
|
|
334
|
+
}
|
|
335
|
+
assert(before.tag === 'dsh-v0.1.1-rc.2', `official clone tag is ${before.tag}`)
|
|
336
|
+
assert(before.commit === 'b150a551b8d465e31e418e1b2eaf5e79bbb7d28e',
|
|
337
|
+
`official clone commit is ${before.commit}`)
|
|
338
|
+
assert(before.tree === '53915efe4e2126cc7779b73dfc8a3bcec5318c44',
|
|
339
|
+
`official clone tree is ${before.tree}`)
|
|
340
|
+
assert(before.status === '', 'official clone is dirty before CLI smoke')
|
|
341
|
+
|
|
342
|
+
const temporaryRoot = await mkdtemp(join(tmpdir(), 'dsh-selector-official-clone-'))
|
|
343
|
+
const worktree = join(temporaryRoot, 'official')
|
|
344
|
+
const dshHome = join(temporaryRoot, 'dsh-home')
|
|
345
|
+
let added = false
|
|
346
|
+
const environment = { ...process.env, DSH_HOME: dshHome }
|
|
347
|
+
const dsh = (...args: string[]) => run('pnpm', ['dsh', ...args], { cwd: worktree, env: environment })
|
|
348
|
+
const dumpConfig = () => capture('pnpm', ['dsh', '--profile', 'web', '--dump-config'], {
|
|
349
|
+
cwd: worktree,
|
|
350
|
+
env: environment,
|
|
351
|
+
}).then(captured => captured.stdout)
|
|
352
|
+
|
|
353
|
+
const profilePackages = () => {
|
|
354
|
+
const profileRoot = join(dshHome, 'profiles/web')
|
|
355
|
+
const profileRequire = createRequire(join(profileRoot, 'package.json'))
|
|
356
|
+
const selectorPath = profileRequire.resolve('dsh-context-compression-improved/package.json')
|
|
357
|
+
return {
|
|
358
|
+
profileRoot,
|
|
359
|
+
selectorPath,
|
|
360
|
+
selector: JSON.parse(readFileSync(selectorPath, 'utf8')) as Record<string, any>,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const WEB_ONLY_HEADLESS_UNRESOLVED_PEERS = [
|
|
365
|
+
'@deepseek-ai/dsh-client-ui-primitives',
|
|
366
|
+
'@deepseek-ai/dsh-client-ui-slots',
|
|
367
|
+
]
|
|
368
|
+
|
|
369
|
+
const proveBuiltClientPeersLoad = async () => {
|
|
370
|
+
const clientPeers = [
|
|
371
|
+
'@deepseek-ai/dsh-client-locale',
|
|
372
|
+
'@deepseek-ai/dsh-client-runtime',
|
|
373
|
+
'@deepseek-ai/dsh-client-ui-primitives',
|
|
374
|
+
'@deepseek-ai/dsh-client-ui-settings',
|
|
375
|
+
'@deepseek-ai/dsh-client-ui-slots',
|
|
376
|
+
'@deepseek-ai/dsh-client-ui-workspace',
|
|
377
|
+
'react',
|
|
378
|
+
]
|
|
379
|
+
const anchors = JSON.stringify([
|
|
380
|
+
join(worktree, 'packages/bundle/web-app/package.json'),
|
|
381
|
+
join(worktree, 'packages/client/locale/package.json'),
|
|
382
|
+
join(worktree, 'apps/web/package.json'),
|
|
383
|
+
])
|
|
384
|
+
const outcome = await captureOutcome(process.execPath, ['--input-type=commonjs', '-e', [
|
|
385
|
+
"const { createRequire } = require('node:module')",
|
|
386
|
+
`const anchors = ${anchors}.map(path => createRequire(path))`,
|
|
387
|
+
`for (const peer of ${JSON.stringify(clientPeers)}) {`,
|
|
388
|
+
' const resolved = anchors.some(requireFrom => {',
|
|
389
|
+
' try { requireFrom.resolve(peer); return true } catch { return false }',
|
|
390
|
+
' })',
|
|
391
|
+
" if (!resolved) throw new Error('built client peer resolved from no dependent package: ' + peer)",
|
|
392
|
+
'}',
|
|
393
|
+
"console.log('CLIENT_PEERS_OK')",
|
|
394
|
+
].join('\n')], { cwd: worktree, env: environment })
|
|
395
|
+
if (outcome.code !== 0 || !outcome.stdout.includes('CLIENT_PEERS_OK')) {
|
|
396
|
+
throw new Error(`built client peers failed to resolve in the official worktree:\n${outcome.stdout}\n${outcome.stderr}`)
|
|
397
|
+
}
|
|
398
|
+
return clientPeers.length
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const provePluginLoads = async () => {
|
|
402
|
+
const { profileRoot, selector } = profilePackages()
|
|
403
|
+
const selectorPeers = Object.keys(selector.peerDependencies ?? {})
|
|
404
|
+
const enginePeers = selectorPeers.filter(peer => !WEB_ONLY_HEADLESS_UNRESOLVED_PEERS.includes(peer))
|
|
405
|
+
for (const allowed of WEB_ONLY_HEADLESS_UNRESOLVED_PEERS) {
|
|
406
|
+
if (!selectorPeers.includes(allowed)) {
|
|
407
|
+
throw new Error(`web-only whitelist names a peer the selector no longer declares: ${allowed}`)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const script = [
|
|
411
|
+
"const { createRequire } = require('node:module')",
|
|
412
|
+
`const requireFromProfile = createRequire(String.raw\`${join(profileRoot, 'package.json')}\`)`,
|
|
413
|
+
`for (const peer of ${JSON.stringify(enginePeers)}) {`,
|
|
414
|
+
' requireFromProfile.resolve(peer)',
|
|
415
|
+
'}',
|
|
416
|
+
"const selectorEntry = requireFromProfile.resolve('dsh-context-compression-improved')",
|
|
417
|
+
'const plugin = require(selectorEntry)',
|
|
418
|
+
"if (typeof plugin.apply !== 'function') throw new Error('selector entry exports no apply()')",
|
|
419
|
+
`const unresolved = []`,
|
|
420
|
+
`for (const peer of ${JSON.stringify(selectorPeers)}) {`,
|
|
421
|
+
' try { requireFromProfile.resolve(peer) } catch { unresolved.push(peer) }',
|
|
422
|
+
'}',
|
|
423
|
+
"console.log('LOAD_OK ' + JSON.stringify(unresolved))",
|
|
424
|
+
].join('\n')
|
|
425
|
+
const outcome = await captureOutcome(process.execPath, ['--input-type=commonjs', '-e', script], {
|
|
426
|
+
cwd: worktree,
|
|
427
|
+
env: environment,
|
|
428
|
+
})
|
|
429
|
+
const marker = outcome.stdout.split('\n').find(line => line.startsWith('LOAD_OK'))
|
|
430
|
+
if (outcome.code !== 0 || marker === undefined) {
|
|
431
|
+
throw new Error(`official profile failed to load the plugin and its engine peers:\n${outcome.stdout}\n${outcome.stderr}`)
|
|
432
|
+
}
|
|
433
|
+
const unresolved = JSON.parse(marker.slice('LOAD_OK '.length))
|
|
434
|
+
const unexpected = unresolved.filter((peer: string) => !WEB_ONLY_HEADLESS_UNRESOLVED_PEERS.includes(peer))
|
|
435
|
+
if (unexpected.length > 0) {
|
|
436
|
+
throw new Error(`selector peers failed to resolve headless outside the reviewed web-only whitelist: ${unexpected.join(', ')}`)
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
enginePeers: enginePeers.length,
|
|
440
|
+
headlessUnresolvedSelectorPeers: unresolved,
|
|
441
|
+
whitelist: WEB_ONLY_HEADLESS_UNRESOLVED_PEERS,
|
|
442
|
+
builtClientPeersVerified: await proveBuiltClientPeersLoad(),
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const runProfileBootProbe = async (phase: string, expectSeeded: boolean) => {
|
|
447
|
+
const profilePort = await allocateLoopbackPort()
|
|
448
|
+
const { profileRoot } = profilePackages()
|
|
449
|
+
const settingsProof = expectSeeded
|
|
450
|
+
? [
|
|
451
|
+
' const requireFromProfile = createRequire(profileRoot)',
|
|
452
|
+
" const pluginEntry = requireFromProfile.resolve('dsh-context-compression-improved/pruner')",
|
|
453
|
+
' const plugin = requireFromProfile(pluginEntry)',
|
|
454
|
+
' const parsed = plugin.parseContextCompressionSettings(structuredClone(raw))',
|
|
455
|
+
" if (parsed.profile !== 'savings' || parsed.autoCompact.thresholdPercent !== 73) {",
|
|
456
|
+
" throw new Error('boot probe: booted settings did not expose the seeded document: ' + JSON.stringify(parsed))",
|
|
457
|
+
' }',
|
|
458
|
+
]
|
|
459
|
+
: [
|
|
460
|
+
" if (raw?.profile !== 'balanced' || raw?.custom?.version !== 3) {",
|
|
461
|
+
" throw new Error('boot probe: previous-release settings defaults did not resolve: ' + JSON.stringify(raw))",
|
|
462
|
+
' }',
|
|
463
|
+
]
|
|
464
|
+
const probe = [
|
|
465
|
+
"import { readdir } from 'node:fs/promises'",
|
|
466
|
+
"import { tmpdir } from 'node:os'",
|
|
467
|
+
"import { join } from 'node:path'",
|
|
468
|
+
"import { createRequire } from 'node:module'",
|
|
469
|
+
"import { pathToFileURL } from 'node:url'",
|
|
470
|
+
`const worktree = String.raw\`${worktree}\``,
|
|
471
|
+
`const profileRoot = String.raw\`${join(profileRoot, 'package.json')}\``,
|
|
472
|
+
'const bootModule = await import(pathToFileURL(join(worktree, \'apps/cli/src/profile-boot.ts\')).href)',
|
|
473
|
+
"const appBoot = await import(pathToFileURL(join(worktree, 'packages/boot/app-boot/src/index.ts')).href)",
|
|
474
|
+
"const settingsModule = await import(pathToFileURL(join(worktree, 'packages/settings/settings/src/index.ts')).href)",
|
|
475
|
+
'const preBootStores = new Set(await readdir(tmpdir()))',
|
|
476
|
+
'const { ctx } = await bootModule.runProfile({',
|
|
477
|
+
" environment: appBoot.loadLayeredEnv('dsh'),",
|
|
478
|
+
" profile: 'web',",
|
|
479
|
+
' patchFiles: [],',
|
|
480
|
+
` args: ['--host', '127.0.0.1', '--port', ${JSON.stringify(String(profilePort))}, '--no-open'],`,
|
|
481
|
+
'})',
|
|
482
|
+
'try {',
|
|
483
|
+
" const settings = ctx.get('settings')",
|
|
484
|
+
" if (settings === undefined) throw new Error('boot probe: settings service missing')",
|
|
485
|
+
" const raw = settings.get(settingsModule.settingsNamespace('context-compression'))",
|
|
486
|
+
...settingsProof,
|
|
487
|
+
" const presets = ctx.get('agentPresets')",
|
|
488
|
+
" if (presets === undefined) throw new Error('boot probe: agentPresets service missing')",
|
|
489
|
+
' const key = await presets.standingKeyFor()',
|
|
490
|
+
' if (key?.agentPreset !== \'standard\') {',
|
|
491
|
+
" throw new Error('boot probe: standing key did not compose the default preset: ' + JSON.stringify(key))",
|
|
492
|
+
' }',
|
|
493
|
+
' const generated = []',
|
|
494
|
+
" for (const entry of await readdir(tmpdir(), { withFileTypes: true })) {",
|
|
495
|
+
' if (!entry.isDirectory() || preBootStores.has(entry.name)) continue',
|
|
496
|
+
" if (!entry.name.startsWith('dsh-context-compression-presets-')) continue",
|
|
497
|
+
' let children',
|
|
498
|
+
' try {',
|
|
499
|
+
' children = await readdir(join(tmpdir(), entry.name))',
|
|
500
|
+
' } catch {',
|
|
501
|
+
' continue',
|
|
502
|
+
' }',
|
|
503
|
+
" for (const child of children) {",
|
|
504
|
+
" if (/^standard-[0-9a-f]{24}\\.agent\\.cordis\\.yml$/u.test(child)) generated.push(child)",
|
|
505
|
+
' }',
|
|
506
|
+
' }',
|
|
507
|
+
' if (generated.length === 0) {',
|
|
508
|
+
" throw new Error('boot probe: the selector preset overlay generated no standing composition in this boot')",
|
|
509
|
+
' }',
|
|
510
|
+
" console.log('BOOT_PROBE_OK ' + JSON.stringify({ phase: " + JSON.stringify(phase) + ", settings: " + JSON.stringify(expectSeeded ? 'seeded-savings-73' : 'previous-release-defaults') + ", generated: generated.length }))",
|
|
511
|
+
'} finally {',
|
|
512
|
+
' await ctx.fiber.dispose()',
|
|
513
|
+
'}',
|
|
514
|
+
'process.exit(0)',
|
|
515
|
+
].join('\n')
|
|
516
|
+
const probePath = join(temporaryRoot, `boot-probe-${phase}.mjs`)
|
|
517
|
+
await writeFile(probePath, probe, 'utf8')
|
|
518
|
+
const outcome = await captureOutcome(process.execPath, ['--import', 'tsx/esm', probePath], {
|
|
519
|
+
cwd: worktree,
|
|
520
|
+
env: environment,
|
|
521
|
+
})
|
|
522
|
+
const marker = outcome.stdout.split('\n').find(line => line.startsWith('BOOT_PROBE_OK'))
|
|
523
|
+
if (outcome.code !== 0 || marker === undefined) {
|
|
524
|
+
throw new Error(`official profile boot probe (${phase}) failed:\n${outcome.stdout}\n${outcome.stderr}`)
|
|
525
|
+
}
|
|
526
|
+
return JSON.parse(marker.slice('BOOT_PROBE_OK '.length))
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const seedSettings = async () => {
|
|
530
|
+
const { profileRoot } = profilePackages()
|
|
531
|
+
const settingsPath = join(dshHome, 'settings.yaml')
|
|
532
|
+
const script = [
|
|
533
|
+
"const { writeFileSync, readFileSync, existsSync } = require('node:fs')",
|
|
534
|
+
"const { createRequire } = require('node:module')",
|
|
535
|
+
"const yaml = require('js-yaml')",
|
|
536
|
+
`const requireFromProfile = createRequire(String.raw\`${join(profileRoot, 'package.json')}\`)`,
|
|
537
|
+
"const pluginEntry = requireFromProfile.resolve('dsh-context-compression-improved/pruner')",
|
|
538
|
+
'const plugin = require(pluginEntry)',
|
|
539
|
+
"const document = {",
|
|
540
|
+
" 'context-compression': {",
|
|
541
|
+
" profile: 'savings',",
|
|
542
|
+
' custom: plugin.DEFAULT_CUSTOM_COMPRESSION_POLICY,',
|
|
543
|
+
' autoCompact: { thresholdPercent: 73 },',
|
|
544
|
+
' },',
|
|
545
|
+
'}',
|
|
546
|
+
`const path = String.raw\`${settingsPath}\``,
|
|
547
|
+
'const merged = existsSync(path) ? { ...yaml.load(readFileSync(path, \'utf8\')), ...document } : document',
|
|
548
|
+
"writeFileSync(path, yaml.dump(merged), 'utf8')",
|
|
549
|
+
"console.log('SEED_OK')",
|
|
550
|
+
].join('\n')
|
|
551
|
+
const outcome = await captureOutcome(process.execPath, ['--input-type=commonjs', '-e', script], {
|
|
552
|
+
cwd: worktree,
|
|
553
|
+
env: environment,
|
|
554
|
+
})
|
|
555
|
+
if (outcome.code !== 0 || !outcome.stdout.includes('SEED_OK')) {
|
|
556
|
+
throw new Error(`seeding official-profile settings failed:\n${outcome.stdout}\n${outcome.stderr}`)
|
|
557
|
+
}
|
|
558
|
+
return settingsPath
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const proveSettingsPreserved = async (settingsPath: string) => {
|
|
562
|
+
const { profileRoot } = profilePackages()
|
|
563
|
+
const script = [
|
|
564
|
+
"const { readFileSync } = require('node:fs')",
|
|
565
|
+
"const { createRequire } = require('node:module')",
|
|
566
|
+
'const yaml = require(\'js-yaml\')',
|
|
567
|
+
`const requireFromProfile = createRequire(String.raw\`${join(profileRoot, 'package.json')}\`)`,
|
|
568
|
+
"const pluginEntry = requireFromProfile.resolve('dsh-context-compression-improved/pruner')",
|
|
569
|
+
'const plugin = require(pluginEntry)',
|
|
570
|
+
`const document = yaml.load(readFileSync(String.raw\`${settingsPath}\`, 'utf8'))`,
|
|
571
|
+
"const parsed = plugin.parseContextCompressionSettings(document['context-compression'])",
|
|
572
|
+
"if (parsed.profile !== 'savings' || parsed.autoCompact.thresholdPercent !== 73) {",
|
|
573
|
+
" throw new Error('upgraded profile lost the saved user settings: ' + JSON.stringify(parsed))",
|
|
574
|
+
'}',
|
|
575
|
+
"console.log('SETTINGS_OK')",
|
|
576
|
+
].join('\n')
|
|
577
|
+
const outcome = await captureOutcome(process.execPath, ['--input-type=commonjs', '-e', script], {
|
|
578
|
+
cwd: worktree,
|
|
579
|
+
env: environment,
|
|
580
|
+
})
|
|
581
|
+
if (outcome.code !== 0 || !outcome.stdout.includes('SETTINGS_OK')) {
|
|
582
|
+
throw new Error(`settings preservation proof failed:\n${outcome.stdout}\n${outcome.stderr}`)
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
try {
|
|
587
|
+
await run('git', ['worktree', 'add', '--detach', worktree, before.commit], { cwd: clone })
|
|
588
|
+
added = true
|
|
589
|
+
await run('pnpm', ['install', '--frozen-lockfile', '--ignore-scripts'], { cwd: worktree })
|
|
590
|
+
await run('pnpm', ['run', 'build'], { cwd: worktree })
|
|
591
|
+
|
|
592
|
+
if (upgradeFrom === undefined) throw new Error('release gate requires the previous published release for the official lifecycle')
|
|
593
|
+
await dsh('plugin', '--profile', 'web', 'add',
|
|
594
|
+
`dsh-context-compression-improved@${upgradeFrom}`, '--registry', registry)
|
|
595
|
+
const beforeUp = profilePackages()
|
|
596
|
+
if (beforeUp.selector.version !== upgradeFrom) {
|
|
597
|
+
throw new Error(`official lifecycle started on ${String(beforeUp.selector.version)}, expected ${upgradeFrom}`)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const addedDump = await dumpConfig()
|
|
601
|
+
assert(addedDump.includes('context-compression-selector-bundle'),
|
|
602
|
+
'official post-add dump lacks the selector Bundle layer')
|
|
603
|
+
|
|
604
|
+
const postAddBoot = await runProfileBootProbe('post-add', false)
|
|
605
|
+
|
|
606
|
+
const settingsPath = await seedSettings()
|
|
607
|
+
|
|
608
|
+
await dsh('plugin', '--profile', 'web', 'up',
|
|
609
|
+
'dsh-context-compression-improved@latest', '--registry', registry)
|
|
610
|
+
const afterUp = profilePackages()
|
|
611
|
+
if (afterUp.selector.version !== candidateVersion) {
|
|
612
|
+
throw new Error(`official up landed ${String(afterUp.selector.version)}, expected ${String(candidateVersion)}`)
|
|
613
|
+
}
|
|
614
|
+
await proveSettingsPreserved(settingsPath)
|
|
615
|
+
const loadProof = await provePluginLoads()
|
|
616
|
+
const postUpBoot = await runProfileBootProbe('post-up', true)
|
|
617
|
+
const upDump = await dumpConfig()
|
|
618
|
+
for (const marker of ['context-compression-selector-bundle', 'presetOverlay: true']) {
|
|
619
|
+
assert(upDump.includes(marker), `official post-up dump lacks ${marker}`)
|
|
620
|
+
}
|
|
621
|
+
assert(upDump.match(/context-compression-selector-bundle/gu)?.length === 1,
|
|
622
|
+
'official post-up dump contains more than one selector Bundle layer')
|
|
623
|
+
|
|
624
|
+
const peers = await captureOutcome('pnpm', ['peers', 'check'], {
|
|
625
|
+
cwd: join(dshHome, 'profiles/web'),
|
|
626
|
+
env: environment,
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
await dsh('plugin', '--profile', 'web', 'remove', 'dsh-context-compression-improved')
|
|
630
|
+
const removedDump = await dumpConfig()
|
|
631
|
+
assert(!removedDump.includes('context-compression-selector-bundle'),
|
|
632
|
+
'official CLI remove left the selector Bundle layer active')
|
|
633
|
+
|
|
634
|
+
await dsh('plugin', '--profile', 'web', 'add',
|
|
635
|
+
'dsh-context-compression-improved@latest', '--registry', registry)
|
|
636
|
+
const secondDump = await dumpConfig()
|
|
637
|
+
assert(secondDump.includes('context-compression-selector-bundle'),
|
|
638
|
+
'official CLI reinstall did not restore the selector Bundle layer')
|
|
639
|
+
|
|
640
|
+
return {
|
|
641
|
+
tag: before.tag,
|
|
642
|
+
commit: before.commit,
|
|
643
|
+
tree: before.tree,
|
|
644
|
+
status: 'clean',
|
|
645
|
+
install: 'single-package',
|
|
646
|
+
upgrade: {
|
|
647
|
+
from: upgradeFrom,
|
|
648
|
+
to: candidateVersion,
|
|
649
|
+
selector: afterUp.selector.version,
|
|
650
|
+
settingsPreserved: true,
|
|
651
|
+
pluginAndEnginePeersLoad: loadProof,
|
|
652
|
+
realProfileBoot: { postAdd: postAddBoot, postUp: postUpBoot },
|
|
653
|
+
dump: 'single-bundle-layer',
|
|
654
|
+
},
|
|
655
|
+
remove: 'bundle-removed',
|
|
656
|
+
reinstall: 'bundle-restored',
|
|
657
|
+
peersCheck: {
|
|
658
|
+
exitCode: peers.code,
|
|
659
|
+
note: 'informational: plugin peers resolve via the healed profiles/node_modules fallback, proven by load',
|
|
660
|
+
},
|
|
661
|
+
}
|
|
662
|
+
} finally {
|
|
663
|
+
if (added) await run('git', ['worktree', 'remove', '--force', worktree], { cwd: clone })
|
|
664
|
+
await rm(temporaryRoot, { recursive: true, force: true })
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const fixedArtifactRoot = process.env.DSH_SELECTOR_ARTIFACT_DIR
|
|
669
|
+
const artifactRoot = fixedArtifactRoot === undefined
|
|
670
|
+
? await mkdtemp(join(tmpdir(), 'dsh-selector-pack-'))
|
|
671
|
+
: await realpath(fixedArtifactRoot)
|
|
672
|
+
const ownsArtifactRoot = fixedArtifactRoot === undefined
|
|
673
|
+
const comparisonRoot = fixedArtifactRoot === undefined
|
|
674
|
+
? undefined
|
|
675
|
+
: await mkdtemp(join(tmpdir(), 'dsh-selector-rebuild-'))
|
|
676
|
+
const consumerRoot = await mkdtemp(join(tmpdir(), 'dsh-selector-consumer-'))
|
|
677
|
+
let server: Server | undefined
|
|
678
|
+
|
|
679
|
+
try {
|
|
680
|
+
const registryPackages = new Map<string, {
|
|
681
|
+
filename: string
|
|
682
|
+
manifest: Record<string, any>
|
|
683
|
+
tarball: string
|
|
684
|
+
sha1: string
|
|
685
|
+
sha256?: string
|
|
686
|
+
integrity: string
|
|
687
|
+
}>()
|
|
688
|
+
const previousVersions = new Map<string, {
|
|
689
|
+
filename: string
|
|
690
|
+
manifest: Record<string, any>
|
|
691
|
+
tarball: string
|
|
692
|
+
sha1: string
|
|
693
|
+
integrity: string
|
|
694
|
+
}>()
|
|
695
|
+
// The predecessor is whatever the registry actually offers. This used to be
|
|
696
|
+
// the pinned literal '0.1.0-beta.2', which only ever existed under this
|
|
697
|
+
// package's pre-rename name, so it could never resolve under the current one.
|
|
698
|
+
let previousRelease = ''
|
|
699
|
+
// True only once a real predecessor was found. Both release-mode gates below
|
|
700
|
+
// key off it: the upgrade leg must run whenever users have something to
|
|
701
|
+
// upgrade FROM, and must not block a package that has nothing to upgrade from.
|
|
702
|
+
let predecessorFound = false
|
|
703
|
+
let upgradeLeg = 'skipped-no-network'
|
|
704
|
+
for (const name of ['dsh-context-compression-improved']) {
|
|
705
|
+
try {
|
|
706
|
+
const packumentResponse = await fetch(`https://registry.npmjs.org/${name}`)
|
|
707
|
+
if (packumentResponse.status === 404) {
|
|
708
|
+
upgradeLeg = 'skipped-package-not-published'
|
|
709
|
+
previousVersions.clear()
|
|
710
|
+
break
|
|
711
|
+
}
|
|
712
|
+
if (!packumentResponse.ok) throw new Error(`packument responded ${packumentResponse.status}`)
|
|
713
|
+
const packument = await packumentResponse.json() as {
|
|
714
|
+
versions?: Record<string, unknown>
|
|
715
|
+
time?: Record<string, string>
|
|
716
|
+
}
|
|
717
|
+
const localVersion = JSON.parse(
|
|
718
|
+
await readFile(join(packages[0].directory, 'package.json'), 'utf8'),
|
|
719
|
+
).version as string
|
|
720
|
+
const times = packument.time ?? {}
|
|
721
|
+
// Publication order, not semver order: the version a user would be
|
|
722
|
+
// upgrading from is the most recently published one that is not the
|
|
723
|
+
// candidate. Avoids a semver dependency and handles prereleases.
|
|
724
|
+
const earlier = Object.keys(packument.versions ?? {})
|
|
725
|
+
.filter((version) => version !== localVersion)
|
|
726
|
+
.sort((a, b) => Date.parse(times[b] ?? '') - Date.parse(times[a] ?? ''))
|
|
727
|
+
if (earlier.length === 0) {
|
|
728
|
+
upgradeLeg = 'skipped-no-earlier-release'
|
|
729
|
+
previousVersions.clear()
|
|
730
|
+
break
|
|
731
|
+
}
|
|
732
|
+
previousRelease = earlier[0]
|
|
733
|
+
const response = await fetch(`https://registry.npmjs.org/${name}/${previousRelease}`)
|
|
734
|
+
if (!response.ok) throw new Error(`packument responded ${response.status}`)
|
|
735
|
+
const manifest = await response.json() as Record<string, any>
|
|
736
|
+
const tarballResponse = await fetch(manifest.dist.tarball)
|
|
737
|
+
if (!tarballResponse.ok) throw new Error(`tarball responded ${tarballResponse.status}`)
|
|
738
|
+
const bytes = Buffer.from(await tarballResponse.arrayBuffer())
|
|
739
|
+
const filename = `${name}-${previousRelease}.tgz`
|
|
740
|
+
const tarball = join(artifactRoot, `previous-${filename}`)
|
|
741
|
+
await writeFile(tarball, bytes)
|
|
742
|
+
previousVersions.set(name, {
|
|
743
|
+
filename,
|
|
744
|
+
manifest,
|
|
745
|
+
tarball,
|
|
746
|
+
sha1: createHash('sha1').update(bytes).digest('hex'),
|
|
747
|
+
integrity: `sha512-${createHash('sha512').update(bytes).digest('base64')}`,
|
|
748
|
+
})
|
|
749
|
+
} catch (error) {
|
|
750
|
+
if (e2eMode === 'release') {
|
|
751
|
+
throw new Error(`release gate requires the published previous release for the upgrade leg: ${error instanceof Error ? error.message : String(error)}`, { cause: error })
|
|
752
|
+
}
|
|
753
|
+
upgradeLeg = `skipped-previous-release-unavailable:${error instanceof Error ? error.message : String(error)}`
|
|
754
|
+
previousVersions.clear()
|
|
755
|
+
break
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (previousVersions.size === 1) {
|
|
759
|
+
upgradeLeg = 'installed'
|
|
760
|
+
predecessorFound = true
|
|
761
|
+
}
|
|
762
|
+
for (const descriptor of packages) {
|
|
763
|
+
const manifest = JSON.parse(await readFile(join(descriptor.directory, 'package.json'), 'utf8')) as Record<string, any>
|
|
764
|
+
if (fixedArtifactRoot === undefined) {
|
|
765
|
+
await run('npm', ['pack', '--pack-destination', artifactRoot, descriptor.directory], { cwd: root })
|
|
766
|
+
}
|
|
767
|
+
const filename = `${manifest.name}-${manifest.version}.tgz`
|
|
768
|
+
const tarball = join(artifactRoot, filename)
|
|
769
|
+
await stat(tarball)
|
|
770
|
+
const bytes = await readFile(tarball)
|
|
771
|
+
if (comparisonRoot !== undefined) {
|
|
772
|
+
await run('npm', ['pack', '--pack-destination', comparisonRoot, descriptor.directory], { cwd: root })
|
|
773
|
+
const rebuilt = await readFile(join(comparisonRoot, filename))
|
|
774
|
+
const reviewedHash = createHash('sha256').update(bytes).digest('hex')
|
|
775
|
+
const rebuiltHash = createHash('sha256').update(rebuilt).digest('hex')
|
|
776
|
+
if (reviewedHash !== rebuiltHash) {
|
|
777
|
+
throw new Error(`${manifest.name} fixed tarball differs from the current workspace rebuild`)
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
registryPackages.set(manifest.name, {
|
|
781
|
+
filename,
|
|
782
|
+
manifest,
|
|
783
|
+
tarball,
|
|
784
|
+
sha1: createHash('sha1').update(bytes).digest('hex'),
|
|
785
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
786
|
+
integrity: `sha512-${createHash('sha512').update(bytes).digest('base64')}`,
|
|
787
|
+
})
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
server = createServer(async (request: IncomingMessage, response: ServerResponse) => {
|
|
791
|
+
try {
|
|
792
|
+
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1')
|
|
793
|
+
const decodedPath = decodeURIComponent(requestUrl.pathname)
|
|
794
|
+
for (const [name, descriptor] of registryPackages) {
|
|
795
|
+
if (decodedPath === `/${name}`) {
|
|
796
|
+
const address = server!.address()
|
|
797
|
+
if (address === null || typeof address === 'string') throw new Error('registry has no TCP address')
|
|
798
|
+
const tarballUrl = `http://127.0.0.1:${address.port}/${name}/-/${descriptor.filename}`
|
|
799
|
+
const version = {
|
|
800
|
+
...descriptor.manifest,
|
|
801
|
+
dist: {
|
|
802
|
+
tarball: tarballUrl,
|
|
803
|
+
shasum: descriptor.sha1,
|
|
804
|
+
integrity: descriptor.integrity,
|
|
805
|
+
},
|
|
806
|
+
}
|
|
807
|
+
const previous = previousVersions.get(name)
|
|
808
|
+
const previousVersion = previous === undefined ? {} : {
|
|
809
|
+
[previous.manifest.version]: {
|
|
810
|
+
...previous.manifest,
|
|
811
|
+
dist: {
|
|
812
|
+
tarball: `http://127.0.0.1:${address.port}/${name}/-/${previous.filename}`,
|
|
813
|
+
shasum: previous.sha1,
|
|
814
|
+
integrity: previous.integrity,
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
}
|
|
818
|
+
const body = JSON.stringify({
|
|
819
|
+
name,
|
|
820
|
+
'dist-tags': { latest: descriptor.manifest.version },
|
|
821
|
+
versions: { ...previousVersion, [descriptor.manifest.version]: version },
|
|
822
|
+
})
|
|
823
|
+
response.writeHead(200, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) })
|
|
824
|
+
response.end(request.method === 'HEAD' ? undefined : body)
|
|
825
|
+
return
|
|
826
|
+
}
|
|
827
|
+
if (decodedPath === `/${name}/-/${descriptor.filename}`) {
|
|
828
|
+
const details = await stat(descriptor.tarball)
|
|
829
|
+
response.writeHead(200, { 'content-type': 'application/octet-stream', 'content-length': details.size })
|
|
830
|
+
if (request.method === 'HEAD') response.end()
|
|
831
|
+
else createReadStream(descriptor.tarball).pipe(response)
|
|
832
|
+
return
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
for (const [name, previous] of previousVersions) {
|
|
836
|
+
if (decodedPath === `/${name}/-/${previous.filename}`) {
|
|
837
|
+
const details = await stat(previous.tarball)
|
|
838
|
+
response.writeHead(200, { 'content-type': 'application/octet-stream', 'content-length': details.size })
|
|
839
|
+
if (request.method === 'HEAD') response.end()
|
|
840
|
+
else createReadStream(previous.tarball).pipe(response)
|
|
841
|
+
return
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
846
|
+
response.writeHead(404).end()
|
|
847
|
+
return
|
|
848
|
+
}
|
|
849
|
+
const upstream = await fetch(`https://registry.npmjs.org${requestUrl.pathname}${requestUrl.search}`, {
|
|
850
|
+
method: request.method,
|
|
851
|
+
headers: { accept: request.headers.accept ?? '*/*' },
|
|
852
|
+
})
|
|
853
|
+
const headers: Record<string, string> = {}
|
|
854
|
+
for (const name of ['content-type', 'content-length', 'cache-control', 'etag', 'last-modified']) {
|
|
855
|
+
const value = upstream.headers.get(name)
|
|
856
|
+
if (value !== null) headers[name] = value
|
|
857
|
+
}
|
|
858
|
+
response.writeHead(upstream.status, headers)
|
|
859
|
+
if (request.method === 'HEAD' || upstream.body === null) response.end()
|
|
860
|
+
else Readable.fromWeb(upstream.body as any).pipe(response)
|
|
861
|
+
} catch (error) {
|
|
862
|
+
response.writeHead(502, { 'content-type': 'text/plain' })
|
|
863
|
+
response.end(error instanceof Error ? error.message : String(error))
|
|
864
|
+
}
|
|
865
|
+
})
|
|
866
|
+
await new Promise<void>((resolve, reject) => {
|
|
867
|
+
server!.once('error', reject)
|
|
868
|
+
server!.listen(0, '127.0.0.1', resolve)
|
|
869
|
+
})
|
|
870
|
+
const address = server.address()
|
|
871
|
+
if (address === null || typeof address === 'string') throw new Error('registry has no TCP address')
|
|
872
|
+
const registry = `http://127.0.0.1:${address.port}`
|
|
873
|
+
|
|
874
|
+
await writeFile(join(consumerRoot, 'package.json'), JSON.stringify({
|
|
875
|
+
name: 'dsh-context-compression-improved-packed-e2e',
|
|
876
|
+
version: '0.0.0',
|
|
877
|
+
private: true,
|
|
878
|
+
}, null, 2))
|
|
879
|
+
await writeFile(join(consumerRoot, 'pnpm-workspace.yaml'), 'packages: []\n')
|
|
880
|
+
if (upgradeLeg === 'installed') {
|
|
881
|
+
await run('pnpm', [
|
|
882
|
+
'add',
|
|
883
|
+
`dsh-context-compression-improved@${previousRelease}`,
|
|
884
|
+
...officialHostPackages,
|
|
885
|
+
'--registry', registry,
|
|
886
|
+
], { cwd: consumerRoot })
|
|
887
|
+
const previousSelectorDir = await realpath(join(consumerRoot, 'node_modules/dsh-context-compression-improved'))
|
|
888
|
+
const previousSelector = JSON.parse(await readFile(join(previousSelectorDir, 'package.json'), 'utf8'))
|
|
889
|
+
if (previousSelector.version !== previousRelease) {
|
|
890
|
+
throw new Error(`upgrade leg installed ${String(previousSelector.version)}, expected ${previousRelease}`)
|
|
891
|
+
}
|
|
892
|
+
await run('pnpm', [
|
|
893
|
+
'up',
|
|
894
|
+
'dsh-context-compression-improved@latest',
|
|
895
|
+
'--registry', registry,
|
|
896
|
+
], { cwd: consumerRoot })
|
|
897
|
+
const upgradedSelectorDir = await realpath(join(consumerRoot, 'node_modules/dsh-context-compression-improved'))
|
|
898
|
+
const upgradedSelector = JSON.parse(await readFile(join(upgradedSelectorDir, 'package.json'), 'utf8'))
|
|
899
|
+
const candidateVersion = registryPackages.get('dsh-context-compression-improved')?.manifest.version
|
|
900
|
+
if (candidateVersion === undefined) throw new Error('packed candidate version is unknown')
|
|
901
|
+
if (upgradedSelector.version !== candidateVersion) {
|
|
902
|
+
throw new Error(`upgrade leg landed ${String(upgradedSelector.version)}, expected ${String(candidateVersion)}`)
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
905
|
+
console.info(`UPGRADE_LEG_SKIPPED ${upgradeLeg}`)
|
|
906
|
+
await run('pnpm', [
|
|
907
|
+
'add',
|
|
908
|
+
'dsh-context-compression-improved@latest',
|
|
909
|
+
...officialHostPackages,
|
|
910
|
+
'--registry', registry,
|
|
911
|
+
], { cwd: consumerRoot })
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const selectorDir = await realpath(join(consumerRoot, 'node_modules/dsh-context-compression-improved'))
|
|
915
|
+
const selector = JSON.parse(await readFile(join(selectorDir, 'package.json'), 'utf8'))
|
|
916
|
+
if (selector.publishConfig?.tag !== 'latest') {
|
|
917
|
+
throw new Error('packed package is not guarded by publishConfig.tag=latest')
|
|
918
|
+
}
|
|
919
|
+
for (const peer of ['@deepseek-ai/dsh-command-compact', '@deepseek-ai/dsh-compaction-basic']) {
|
|
920
|
+
if (selector.peerDependencies?.[peer] !== '>=0.1.1-rc.2 <0.2.0') {
|
|
921
|
+
throw new Error(`packed selector has an invalid ${peer} peer range`)
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
const notice = await readFile(join(selectorDir, 'THIRD_PARTY_NOTICES.md'), 'utf8')
|
|
925
|
+
if (!notice.includes('Copyright (c) 2026 DeepSeek')) {
|
|
926
|
+
throw new Error(`${basename(selectorDir)} lacks the full DeepSeek Harness MIT notice`)
|
|
927
|
+
}
|
|
928
|
+
await scanPublishedTree(selectorDir)
|
|
929
|
+
|
|
930
|
+
const productionLicenses: Record<string, string> = Object.fromEntries(await Promise.all([
|
|
931
|
+
['dsh-context-compression-improved', selectorDir, 'MIT'],
|
|
932
|
+
['@huggingface/tokenizers', join(dirname(selectorDir), '@huggingface/tokenizers'), 'Apache-2.0'],
|
|
933
|
+
['js-yaml', join(dirname(selectorDir), 'js-yaml'), 'MIT'],
|
|
934
|
+
].map(async ([name, directory, expected]) => {
|
|
935
|
+
const resolved = await realpath(directory as string)
|
|
936
|
+
const manifest = JSON.parse(await readFile(join(resolved, 'package.json'), 'utf8'))
|
|
937
|
+
if (manifest.name !== name) throw new Error(`expected ${name}, found ${String(manifest.name)}`)
|
|
938
|
+
if (manifest.license !== expected) {
|
|
939
|
+
throw new Error(`${name} license is ${String(manifest.license)}, expected ${expected}`)
|
|
940
|
+
}
|
|
941
|
+
return [name, manifest.license]
|
|
942
|
+
})))
|
|
943
|
+
const yamlDir = await realpath(join(dirname(selectorDir), 'js-yaml'))
|
|
944
|
+
const argparseDir = await realpath(join(dirname(yamlDir), 'argparse'))
|
|
945
|
+
const argparse = JSON.parse(await readFile(join(argparseDir, 'package.json'), 'utf8'))
|
|
946
|
+
if (argparse.name !== 'argparse' || argparse.license !== 'Python-2.0') {
|
|
947
|
+
throw new Error(`argparse license is ${String(argparse.license)}, expected Python-2.0`)
|
|
948
|
+
}
|
|
949
|
+
productionLicenses.argparse = argparse.license
|
|
950
|
+
|
|
951
|
+
const packedTokenizerArtifacts: Record<string, { bytes: number; sha256: string }> = {}
|
|
952
|
+
for (const artifactDir of ['deepseek-v4', 'deepseek-v4-vision-exp']) {
|
|
953
|
+
const manifest = JSON.parse(await readFile(join(selectorDir, 'assets', artifactDir, 'manifest.json'), 'utf8'))
|
|
954
|
+
const tokenizerBytes = await readFile(join(selectorDir, 'assets', artifactDir, 'tokenizer.json'))
|
|
955
|
+
const tokenizerHash = createHash('sha256').update(tokenizerBytes).digest('hex')
|
|
956
|
+
if (tokenizerBytes.length !== manifest.files['tokenizer.json'].bytes) throw new Error(`packed ${artifactDir} tokenizer size differs`)
|
|
957
|
+
if (tokenizerHash !== manifest.files['tokenizer.json'].sha256) throw new Error(`packed ${artifactDir} tokenizer hash differs`)
|
|
958
|
+
packedTokenizerArtifacts[artifactDir] = { bytes: tokenizerBytes.length, sha256: tokenizerHash }
|
|
959
|
+
}
|
|
960
|
+
const tokenizer = await readFile(join(selectorDir, 'assets/deepseek-v4/tokenizer.json'))
|
|
961
|
+
const tokenizerHash = packedTokenizerArtifacts['deepseek-v4'].sha256
|
|
962
|
+
|
|
963
|
+
const client = await readFile(join(selectorDir, 'lib/client.js'), 'utf8')
|
|
964
|
+
if (!client.startsWith('window.__ModuleLoader__.load({')) throw new Error('packed client is not lazy-CJS')
|
|
965
|
+
if (/(?:\/home\/|[A-Za-z]:\\Users\\)/u.test(client)) throw new Error('packed client leaks a developer path')
|
|
966
|
+
const hostSmoke = await runPackedHostSmoke(consumerRoot)
|
|
967
|
+
// KEY CHANGE: copy compiled .js from scripts-dist/ instead of .mjs from scripts/
|
|
968
|
+
const installedComponentsScript = join(consumerRoot, 'packed-components-smoke.js')
|
|
969
|
+
await copyFile(join(root, 'scripts-dist/packed-components-smoke.js'), installedComponentsScript)
|
|
970
|
+
const installedComponentsOutput = await capture(process.execPath, [installedComponentsScript], {
|
|
971
|
+
cwd: consumerRoot,
|
|
972
|
+
})
|
|
973
|
+
const installedComponentsLine = installedComponentsOutput.stdout
|
|
974
|
+
.split(/\r?\n/u)
|
|
975
|
+
.find(line => line.startsWith('PACKED_COMPONENTS_E2E '))
|
|
976
|
+
if (installedComponentsLine === undefined) {
|
|
977
|
+
throw new Error(`installed component smoke lacks its result marker:\n${installedComponentsOutput.stdout}`)
|
|
978
|
+
}
|
|
979
|
+
const installedComponentsSmoke = JSON.parse(
|
|
980
|
+
installedComponentsLine.slice('PACKED_COMPONENTS_E2E '.length),
|
|
981
|
+
)
|
|
982
|
+
const visionLine = installedComponentsOutput.stdout
|
|
983
|
+
.split(/\r?\n/u)
|
|
984
|
+
.find(line => line.startsWith('PACKED_VISION_E2E '))
|
|
985
|
+
if (visionLine === undefined) {
|
|
986
|
+
throw new Error('installed component smoke lacks its vision-session result marker')
|
|
987
|
+
}
|
|
988
|
+
const packedVisionSmoke = JSON.parse(visionLine.slice('PACKED_VISION_E2E '.length))
|
|
989
|
+
assert(packedVisionSmoke.model === 'deepseek-v4-flash-vision-exp',
|
|
990
|
+
'installed vision smoke reported the wrong model')
|
|
991
|
+
assert(packedVisionSmoke.imageSession?.measurement?.kind === 'tokenizer-estimate',
|
|
992
|
+
'installed vision smoke did not prove a tokenizer-estimate image surface')
|
|
993
|
+
assert(packedVisionSmoke.imageSession.measurement.tokens === 340
|
|
994
|
+
&& packedVisionSmoke.imageSession.measurement.upperBoundTokens === 384,
|
|
995
|
+
'installed vision smoke reported the wrong 800x600 image estimate')
|
|
996
|
+
assert(packedVisionSmoke.imageSession.measurement.estimatorId
|
|
997
|
+
=== 'deepseek-ai/DeepSeek-V4-Flash-Vision-Exp/image-token-estimate'
|
|
998
|
+
&& packedVisionSmoke.imageSession.measurement.estimatorRevision
|
|
999
|
+
=== '6821d6ad3681a4b137b066b76094fa82ebd0a380:v1',
|
|
1000
|
+
'installed vision smoke reported the wrong image estimator identity')
|
|
1001
|
+
assert(packedVisionSmoke.imageSession.currentSurfaceKind === 'tokenizer-estimate'
|
|
1002
|
+
&& packedVisionSmoke.imageSession.exactRewriteIneligible === true
|
|
1003
|
+
&& packedVisionSmoke.imageSession.originalIntact === true,
|
|
1004
|
+
'installed vision smoke did not prove estimate propagation and exact-only safety')
|
|
1005
|
+
let officialCloneSmoke: any = null
|
|
1006
|
+
const candidateVersion = registryPackages.get('dsh-context-compression-improved')?.manifest.version
|
|
1007
|
+
if (candidateVersion === undefined) throw new Error('packed candidate version is unknown')
|
|
1008
|
+
const previousForLifecycle = predecessorFound ? previousRelease : undefined
|
|
1009
|
+
if (!predecessorFound) {
|
|
1010
|
+
// The official lifecycle installs the previous release and then upgrades to
|
|
1011
|
+
// the candidate, so it has nothing to do without a predecessor. Record the
|
|
1012
|
+
// same explicit marker the upgrade leg produced instead of failing the run.
|
|
1013
|
+
officialCloneSmoke = upgradeLeg
|
|
1014
|
+
} else if (process.env.DSH_OFFICIAL_CLONE !== undefined) {
|
|
1015
|
+
officialCloneSmoke = await runOfficialCloneCliSmoke(
|
|
1016
|
+
process.env.DSH_OFFICIAL_CLONE,
|
|
1017
|
+
registry,
|
|
1018
|
+
previousForLifecycle,
|
|
1019
|
+
candidateVersion,
|
|
1020
|
+
)
|
|
1021
|
+
} else {
|
|
1022
|
+
const cloneRoot = join(artifactRoot, 'official-clone')
|
|
1023
|
+
try {
|
|
1024
|
+
await run('git', [
|
|
1025
|
+
'clone', '--depth', '1', '--branch', 'dsh-v0.1.1-rc.2',
|
|
1026
|
+
'https://github.com/deepseek-ai/deepseek-harness.git', cloneRoot,
|
|
1027
|
+
])
|
|
1028
|
+
officialCloneSmoke = await runOfficialCloneCliSmoke(
|
|
1029
|
+
cloneRoot,
|
|
1030
|
+
registry,
|
|
1031
|
+
previousForLifecycle,
|
|
1032
|
+
candidateVersion,
|
|
1033
|
+
)
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
if (e2eMode === 'release') throw error
|
|
1036
|
+
console.info(`OFFICIAL_CLONE_SMOKE_SKIPPED ${error instanceof Error ? error.message : String(error)}`)
|
|
1037
|
+
await rm(cloneRoot, { recursive: true, force: true })
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (e2eMode === 'release' && predecessorFound && upgradeLeg !== 'installed') {
|
|
1042
|
+
throw new Error(`release gate requires the upgrade leg to run; it reported: ${upgradeLeg}`)
|
|
1043
|
+
}
|
|
1044
|
+
if (e2eMode === 'release' && officialCloneSmoke === null) {
|
|
1045
|
+
throw new Error('release gate requires the official clean-harness lifecycle to run')
|
|
1046
|
+
}
|
|
1047
|
+
console.info(JSON.stringify({
|
|
1048
|
+
installCommand: 'pnpm add dsh-context-compression-improved@latest',
|
|
1049
|
+
e2eMode,
|
|
1050
|
+
upgradeLeg,
|
|
1051
|
+
artifactSource: fixedArtifactRoot === undefined ? 'fresh-pack' : artifactRoot,
|
|
1052
|
+
workspaceRebuildMatched: fixedArtifactRoot === undefined ? null : true,
|
|
1053
|
+
plugin: selector.version,
|
|
1054
|
+
productionLicenses,
|
|
1055
|
+
tokenizerArtifacts: packedTokenizerArtifacts,
|
|
1056
|
+
tokenizerBytes: tokenizer.length,
|
|
1057
|
+
tokenizerSha256: tokenizerHash,
|
|
1058
|
+
hostSmoke,
|
|
1059
|
+
installedComponentsSmoke,
|
|
1060
|
+
packedVisionSmoke,
|
|
1061
|
+
officialCloneSmoke,
|
|
1062
|
+
tarballs: Object.fromEntries([...registryPackages].map(([name, value]) => [name, {
|
|
1063
|
+
file: basename(value.tarball),
|
|
1064
|
+
sha256: value.sha256,
|
|
1065
|
+
}])),
|
|
1066
|
+
}, null, 2))
|
|
1067
|
+
} finally {
|
|
1068
|
+
if (server !== undefined) await new Promise<void>(resolve => server!.close(() => resolve()))
|
|
1069
|
+
if (comparisonRoot !== undefined) await rm(comparisonRoot, { recursive: true, force: true })
|
|
1070
|
+
if (ownsArtifactRoot) await rm(artifactRoot, { recursive: true, force: true })
|
|
1071
|
+
await rm(consumerRoot, { recursive: true, force: true })
|
|
1072
|
+
}
|