dsh-context-compression-improved 0.2.1 → 0.3.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 -1
- package/.github/workflows/ci.yml +1 -7
- package/CHANGELOG.ja.md +18 -21
- package/CHANGELOG.ko.md +17 -21
- package/CHANGELOG.md +16 -17
- package/CHANGELOG.zh.md +15 -18
- package/CONTRIBUTING.md +1 -1
- package/README.ja.md +1 -20
- package/README.ko.md +12 -30
- package/README.md +1 -19
- package/README.zh.md +5 -21
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/docs/compat-0.1.5-report.md +77 -0
- package/docs/installation.ja.md +6 -4
- package/docs/installation.ko.md +9 -7
- package/docs/installation.md +3 -1
- package/docs/installation.zh.md +3 -2
- package/docs/repair-log.md +54 -33
- package/eslint.config.js +2 -2
- package/package.json +56 -36
- package/packages/selector/README.md +7 -12
- package/packages/selector/README.zh.md +6 -13
- package/packages/selector/THIRD_PARTY_NOTICES.md +2 -8
- package/packages/selector/cordis.patch.yml +7 -5
- package/packages/selector/dsh.plugin.json +2 -2
- package/packages/selector/lib/client.d.ts +31 -5
- package/packages/selector/lib/client.js +100 -82
- package/packages/selector/lib/index.d.ts +8 -20
- package/packages/selector/lib/index.js +9 -14
- package/packages/selector/lib/invariant.js +4 -4
- package/packages/selector/lib/pruner.d.ts +13 -4
- package/packages/selector/lib/pruner.js +91 -46
- package/packages/selector/lib/tail-trim.js +13 -4
- package/packages/selector/package.json +23 -23
- package/packages/selector/screenshots.json +1 -4
- package/packages/selector/src/client/CompressionProfileControls.tsx +5 -8
- package/packages/selector/src/client/CompressionProfileSelector.tsx +9 -5
- package/packages/selector/src/client/EstimatorControls.tsx +28 -32
- package/packages/selector/src/client/index.ts +101 -63
- package/packages/selector/src/client/locales.ts +0 -2
- package/packages/selector/src/client/preset-options.ts +49 -35
- package/packages/selector/src/client/settings-section.tsx +4 -6
- package/packages/selector/src/deepseek-v4-tokenizer.ts +0 -5
- package/packages/selector/src/index.ts +41 -46
- package/packages/selector/src/invariant.ts +5 -5
- package/packages/selector/src/pruner.ts +67 -36
- package/packages/selector/src/runtime/measurement.ts +6 -2
- package/packages/selector/src/runtime/session-events.ts +14 -1
- package/packages/selector/src/runtime/tail-trim.ts +4 -4
- package/packages/selector/src/runtime/tokenpilot/estimator.ts +34 -3
- package/packages/selector/src/runtime/types.ts +1 -1
- package/packages/selector/tests/auto-compact.client.spec.tsx +0 -13
- package/packages/selector/tests/built/client-artifact.spec.ts +5 -1
- package/packages/selector/tests/code-skeleton.client.spec.ts +2 -1
- package/packages/selector/tests/custom-contract.client.spec.ts +2 -1
- package/packages/selector/tests/estimator-channel.client.spec.tsx +16 -11
- package/packages/selector/tests/estimator-route-registration.host.spec.ts +12 -0
- package/packages/selector/tests/host-preset-overlay.host.spec.ts +6 -4
- package/packages/selector/tests/preset-options-write.client.spec.ts +87 -79
- package/packages/selector/tests/preset-overlay-loader.e2e.host.spec.ts +17 -12
- package/packages/selector/tests/preset-overlay.host.spec.ts +5 -2
- package/packages/selector/tests/profiles.client.spec.tsx +3 -3
- package/packages/selector/tests/public/package-contract.client.spec.ts +27 -3
- package/packages/selector/tests/runtime/public/public-runtime.spec.ts +103 -81
- package/packages/selector/tests/runtime/session-events.spec.ts +1 -1
- package/packages/selector/tests/settings-seat.client.spec.ts +86 -0
- package/packages/selector/tests/standing-generation.host.spec.ts +88 -71
- package/packages/selector/tests/subagent-cache-reuse.host.spec.ts +24 -21
- package/pnpm-workspace.yaml +52 -5
- package/scripts/capture-profile-baseline.mjs +42 -0
- package/scripts/generate-tokenizer-fixtures.py +5 -5
- package/scripts/generate-vision-fixtures.py +2 -2
- package/scripts/{packed-components-smoke.ts → packed-components-smoke.mjs} +116 -119
- package/scripts/{packed-install-e2e.ts → packed-install-e2e.mjs} +188 -160
- package/scripts/{verify-release.ts → verify-release.mjs} +70 -94
- package/tests/TEST_INVENTORY.md +6 -7
- package/scripts/capture-profile-baseline.ts +0 -80
- package/tsconfig.scripts.json +0 -13
- /package/{packages/selector/assets/screenshots → docs/assets}/context-compression-selector-profiles.jpg +0 -0
- /package/{packages/selector/assets/screenshots → docs/assets}/context-compression-selector-settings.png +0 -0
|
@@ -6,69 +6,82 @@ import type { CompressionSelectorInjected } from '../src/client/CompressionProfi
|
|
|
6
6
|
import type { ContextCompressionSettings } from '../src/profiles.ts'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The settings transport applies a `set` op AT its path, so the write path is
|
|
10
|
+
* asserted through the ops it submits: a whole-section write is what silently
|
|
11
|
+
* deleted `estimatorMode` (and every sibling override) whenever the user
|
|
12
|
+
* touched a second field of the TokenPilot-inspired card.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
type PathOp = { op: 'set', path: string[], value: unknown } | { op: 'unset', path: string[] }
|
|
16
|
+
|
|
17
|
+
interface ScopeStub {
|
|
18
|
+
readonly writes: PathOp[][]
|
|
19
|
+
readonly snapshot: () => Record<string, unknown>
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
writes: { field: string, value: unknown }[]
|
|
25
|
-
section: () => Record<string, unknown>
|
|
26
|
-
sectionOf: (key: string) => unknown
|
|
27
|
-
} {
|
|
22
|
+
/** Emulate the Host settings service: apply ops at their path, then bump the revision. */
|
|
23
|
+
function createScopeStub(initial: ContextCompressionSettings, commit = true): ScopeStub {
|
|
28
24
|
let revision = 1
|
|
29
25
|
let value: Record<string, unknown> = structuredClone(initial) as unknown as Record<string, unknown>
|
|
30
|
-
const writes:
|
|
26
|
+
const writes: PathOp[][] = []
|
|
31
27
|
const listeners = new Set<() => void>()
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
mode: 'host',
|
|
41
|
-
}),
|
|
42
|
-
subscribe: (listener: () => void) => {
|
|
43
|
-
listeners.add(listener)
|
|
44
|
-
return () => { listeners.delete(listener) }
|
|
45
|
-
},
|
|
46
|
-
set: (field: string, fieldValue: unknown) => {
|
|
47
|
-
writes.push({ field, value: fieldValue })
|
|
48
|
-
// The Host applies `set` AT the path: the section value is replaced.
|
|
49
|
-
if (commit) {
|
|
50
|
-
value = { ...value, [field]: structuredClone(fieldValue) }
|
|
51
|
-
revision += 1
|
|
52
|
-
for (const listener of listeners) listener()
|
|
53
|
-
}
|
|
54
|
-
return Promise.resolve()
|
|
55
|
-
},
|
|
56
|
-
unset: (field: string) => {
|
|
57
|
-
writes.push({ field, value: undefined })
|
|
58
|
-
if (commit) {
|
|
59
|
-
delete value[field]
|
|
60
|
-
revision += 1
|
|
61
|
-
for (const listener of listeners) listener()
|
|
28
|
+
const applyOps = (ops: readonly PathOp[]): void => {
|
|
29
|
+
for (const op of ops) {
|
|
30
|
+
const [head, ...rest] = op.path
|
|
31
|
+
if (head === undefined) continue
|
|
32
|
+
if (rest.length === 0) {
|
|
33
|
+
if (op.op === 'set') value = { ...value, [head]: op.value }
|
|
34
|
+
else delete value[head]
|
|
35
|
+
continue
|
|
62
36
|
}
|
|
63
|
-
|
|
64
|
-
|
|
37
|
+
const child = { ...(value[head] as Record<string, unknown> | undefined) }
|
|
38
|
+
const leaf = rest[rest.length - 1] as string
|
|
39
|
+
if (op.op === 'set') child[leaf] = op.value
|
|
40
|
+
else delete child[leaf]
|
|
41
|
+
value = { ...value, [head]: child }
|
|
42
|
+
}
|
|
43
|
+
revision += 1
|
|
44
|
+
for (const listener of listeners) listener()
|
|
65
45
|
}
|
|
66
46
|
return {
|
|
67
|
-
scope,
|
|
68
47
|
writes,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
snapshot: () => value,
|
|
49
|
+
// The scope face the injection factory binds.
|
|
50
|
+
scope: {
|
|
51
|
+
getSnapshot: () => ({
|
|
52
|
+
status: 'ready',
|
|
53
|
+
value: value as unknown as ContextCompressionSettings,
|
|
54
|
+
base: undefined,
|
|
55
|
+
user: undefined,
|
|
56
|
+
revision,
|
|
57
|
+
writable: true,
|
|
58
|
+
mode: 'host',
|
|
59
|
+
}),
|
|
60
|
+
subscribe: (listener: () => void) => {
|
|
61
|
+
listeners.add(listener)
|
|
62
|
+
return () => { listeners.delete(listener) }
|
|
63
|
+
},
|
|
64
|
+
set: (field: string, fieldValue: unknown) => {
|
|
65
|
+
const ops: PathOp[] = [{ op: 'set', path: [field], value: fieldValue }]
|
|
66
|
+
writes.push(ops)
|
|
67
|
+
applyOps(ops)
|
|
68
|
+
return Promise.resolve()
|
|
69
|
+
},
|
|
70
|
+
unset: (field: string) => {
|
|
71
|
+
const ops: PathOp[] = [{ op: 'unset', path: [field] }]
|
|
72
|
+
writes.push(ops)
|
|
73
|
+
applyOps(ops)
|
|
74
|
+
return Promise.resolve()
|
|
75
|
+
},
|
|
76
|
+
mutate: (ops: readonly PathOp[]) => {
|
|
77
|
+
writes.push([...ops])
|
|
78
|
+
// A refused or lost write changes no document, so the revision the
|
|
79
|
+
// confirmation read fences on never moves.
|
|
80
|
+
if (commit) applyOps(ops)
|
|
81
|
+
return Promise.resolve()
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
} as ScopeStub & { scope: unknown }
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
const DEFAULT_CUSTOM = {
|
|
@@ -91,8 +104,8 @@ function bindInjected(
|
|
|
91
104
|
commit = true,
|
|
92
105
|
): {
|
|
93
106
|
injected: CompressionSelectorInjected
|
|
94
|
-
writes:
|
|
95
|
-
|
|
107
|
+
writes: PathOp[][]
|
|
108
|
+
snapshot: () => Record<string, unknown>
|
|
96
109
|
} {
|
|
97
110
|
const settings: ContextCompressionSettings = {
|
|
98
111
|
profile: 'tokenpilot-inspired',
|
|
@@ -104,9 +117,6 @@ function bindInjected(
|
|
|
104
117
|
const stub = createScopeStub(settings, commit)
|
|
105
118
|
let options: Record<string, unknown> | undefined
|
|
106
119
|
const ctx = {
|
|
107
|
-
effect: (factory: () => unknown) => { factory(); return () => {} },
|
|
108
|
-
locale: { register: () => {}, bind: () => (key: string) => key },
|
|
109
|
-
settingsScope: { bind: () => stub.scope },
|
|
110
120
|
slots: {
|
|
111
121
|
inject: (_slot: string, factory: () => (() => void) | Generator<() => void>) => {
|
|
112
122
|
const result = factory()
|
|
@@ -117,54 +127,52 @@ function bindInjected(
|
|
|
117
127
|
return () => {}
|
|
118
128
|
},
|
|
119
129
|
},
|
|
130
|
+
// apply consumes the services through their declarative-inject faces
|
|
131
|
+
// (ctx.locale / ctx.settingsScope), the same shape cordis binds on the host.
|
|
132
|
+
locale: { bind: () => (key: string) => key, register: () => {} },
|
|
133
|
+
settingsScope: { bind: () => (stub as unknown as { scope: unknown }).scope },
|
|
120
134
|
}
|
|
121
135
|
apply(ctx as never)
|
|
122
136
|
if (options === undefined) throw new Error('the settings card never registered')
|
|
123
137
|
const factory = options['inject'] as () => CompressionSelectorInjected
|
|
124
|
-
return { injected: factory(), writes: stub.writes,
|
|
138
|
+
return { injected: factory(), writes: stub.writes, snapshot: stub.snapshot }
|
|
125
139
|
}
|
|
126
140
|
|
|
127
|
-
describe('presetOptions writes
|
|
141
|
+
describe('presetOptions writes are path-addressed', () => {
|
|
128
142
|
it('keeps the estimator channel when a provider is chosen afterwards', async () => {
|
|
129
|
-
const { injected,
|
|
143
|
+
const { injected, writes, snapshot } = bindInjected({ estimatorMode: 'host' })
|
|
130
144
|
await injected.savePresetOptions({ estimatorProvider: 'local-35b' })
|
|
131
|
-
expect(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
it('writes the merged document rather than the bare patch', async () => {
|
|
135
|
-
const { injected, writes } = bindInjected({ estimatorMode: 'direct', estimatorBaseUrl: 'http://192.168.100.242:8200' })
|
|
136
|
-
await injected.savePresetOptions({ estimatorModel: 'Qwen3.6-35B-A3B' })
|
|
137
|
-
expect(writes).toEqual([{
|
|
138
|
-
field: 'presetOptions',
|
|
139
|
-
value: {
|
|
140
|
-
estimatorMode: 'direct',
|
|
141
|
-
estimatorBaseUrl: 'http://192.168.100.242:8200',
|
|
142
|
-
estimatorModel: 'Qwen3.6-35B-A3B',
|
|
143
|
-
},
|
|
144
|
-
}])
|
|
145
|
+
expect(snapshot()['presetOptions']).toEqual({ estimatorMode: 'host', estimatorProvider: 'local-35b' })
|
|
146
|
+
expect(writes).toEqual([[{ op: 'set', path: ['presetOptions', 'estimatorProvider'], value: 'local-35b' }]])
|
|
145
147
|
})
|
|
146
148
|
|
|
147
|
-
it('
|
|
148
|
-
const { injected,
|
|
149
|
+
it('never replaces the whole section, so sibling overrides survive', async () => {
|
|
150
|
+
const { injected, writes, snapshot } = bindInjected({
|
|
149
151
|
estimatorMode: 'direct',
|
|
152
|
+
estimatorBaseUrl: 'http://192.168.100.242:8200',
|
|
150
153
|
dedupeToolResults: false,
|
|
151
154
|
readState: false,
|
|
152
155
|
})
|
|
153
156
|
await injected.savePresetOptions({ estimatorModel: 'Qwen3.6-35B-A3B' })
|
|
154
157
|
await injected.savePresetOptions({ estimatorProvider: 'local-35b' })
|
|
155
|
-
expect(
|
|
158
|
+
expect(snapshot()['presetOptions']).toEqual({
|
|
156
159
|
estimatorMode: 'direct',
|
|
160
|
+
estimatorBaseUrl: 'http://192.168.100.242:8200',
|
|
157
161
|
dedupeToolResults: false,
|
|
158
162
|
readState: false,
|
|
159
163
|
estimatorModel: 'Qwen3.6-35B-A3B',
|
|
160
164
|
estimatorProvider: 'local-35b',
|
|
161
165
|
})
|
|
166
|
+
for (const batch of writes) {
|
|
167
|
+
for (const op of batch) expect(op.path.length).toBe(2)
|
|
168
|
+
}
|
|
162
169
|
})
|
|
163
170
|
|
|
164
171
|
it('clears exactly the field a patch names with undefined', async () => {
|
|
165
|
-
const { injected,
|
|
172
|
+
const { injected, writes, snapshot } = bindInjected({ estimatorMode: 'direct', estimatorApiKey: 'sk-stored' })
|
|
166
173
|
await injected.savePresetOptions({ estimatorApiKey: undefined })
|
|
167
|
-
expect(
|
|
174
|
+
expect(snapshot()['presetOptions']).toEqual({ estimatorMode: 'direct' })
|
|
175
|
+
expect(writes).toEqual([[{ op: 'unset', path: ['presetOptions', 'estimatorApiKey'] }]])
|
|
168
176
|
})
|
|
169
177
|
|
|
170
178
|
it('writes nothing when the patch already matches the stored section', async () => {
|
|
@@ -15,15 +15,18 @@ import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
|
|
15
15
|
import { scopeOf } from '@deepseek-ai/dsh-scope'
|
|
16
16
|
import {
|
|
17
17
|
SettingsProvider,
|
|
18
|
-
settingsNamespace,
|
|
19
18
|
type SettingsNamespace,
|
|
20
19
|
} from '@deepseek-ai/dsh-settings'
|
|
21
20
|
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
|
21
|
+
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
|
22
22
|
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
|
23
23
|
import ToolRuntime from '@deepseek-ai/dsh-tools'
|
|
24
24
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
25
25
|
import { apply } from '../src/index.ts'
|
|
26
26
|
|
|
27
|
+
// 0.1.5 removed the settingsNamespace() wrapper; namespaces are validated at runtime.
|
|
28
|
+
const nsBrand = (value: string): SettingsNamespace => value as unknown as SettingsNamespace
|
|
29
|
+
|
|
27
30
|
const CORDIS_ORIGINAL = Symbol.for('cordis.original')
|
|
28
31
|
type Traceable = { [CORDIS_ORIGINAL]?: unknown }
|
|
29
32
|
|
|
@@ -74,21 +77,23 @@ async function harness(): Promise<Context> {
|
|
|
74
77
|
const presets = await createPresetRoot()
|
|
75
78
|
const runtime = new Context()
|
|
76
79
|
runtime.baseUrl = `${pathToFileURL(presets).href}/`
|
|
77
|
-
await runtime.plugin(Loader)
|
|
80
|
+
await runtime.plugin(Loader).await()
|
|
78
81
|
runtime.loader.builtins.include = Include
|
|
79
82
|
runtime.loader.builtins.group = Group
|
|
80
|
-
await runtime.plugin(LlmRuntime)
|
|
81
|
-
await runtime.plugin(SessionStore)
|
|
82
|
-
await runtime.plugin(SystemPrompt
|
|
83
|
-
await runtime.plugin(ToolRuntime)
|
|
84
|
-
await runtime.plugin(AgentRegistry)
|
|
85
|
-
await runtime.plugin(AgentLoop
|
|
86
|
-
await runtime.plugin(CommandRuntime)
|
|
87
|
-
await runtime.plugin(
|
|
88
|
-
await runtime.plugin(
|
|
83
|
+
await runtime.plugin(LlmRuntime).await()
|
|
84
|
+
await runtime.plugin(SessionStore).await()
|
|
85
|
+
await runtime.plugin(SystemPrompt).await()
|
|
86
|
+
await runtime.plugin(ToolRuntime).await()
|
|
87
|
+
await runtime.plugin(AgentRegistry).await()
|
|
88
|
+
await runtime.plugin(AgentLoop).await()
|
|
89
|
+
await runtime.plugin(CommandRuntime).await()
|
|
90
|
+
await runtime.plugin(SessionProjectionRegistry).await()
|
|
91
|
+
await runtime.plugin(TokenMeter).await()
|
|
92
|
+
await runtime.plugin(MemorySettings).await()
|
|
89
93
|
await runtime.plugin(AgentPresets, {
|
|
90
94
|
default: 'standard',
|
|
91
95
|
roots: [{ path: presets, trust: 'system' }],
|
|
96
|
+
includeShippedRoot: false,
|
|
92
97
|
includeUserRoot: false,
|
|
93
98
|
})
|
|
94
99
|
await runtime.plugin({
|
|
@@ -187,7 +192,7 @@ describe('standalone selector Bundle through the real preset Loader', () => {
|
|
|
187
192
|
|
|
188
193
|
it('publishes the daily Custom defaults from the plugin-owned settings row', async () => {
|
|
189
194
|
const runtime = await harness()
|
|
190
|
-
const namespace =
|
|
195
|
+
const namespace = nsBrand('context-compression')
|
|
191
196
|
const settings = runtime.settings.get(namespace) as CustomSettings
|
|
192
197
|
|
|
193
198
|
expect(settings.custom.history.trigger).toBe(500_000)
|
|
@@ -164,8 +164,11 @@ describe('plugin-owned preset overlay decorator', () => {
|
|
|
164
164
|
const first = await presets.mount({}, 'standard')
|
|
165
165
|
const same = await presets.mount({}, 'standard')
|
|
166
166
|
expect(same.path).toBe(first.path)
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
// Windows (NTFS) ignores POSIX permission bits: mode stays 0o666.
|
|
168
|
+
if (process.platform !== 'win32') {
|
|
169
|
+
expect((await stat(dirname(first.path))).mode & 0o777).toBe(0o700)
|
|
170
|
+
expect((await stat(first.path)).mode & 0o777).toBe(0o600)
|
|
171
|
+
}
|
|
169
172
|
|
|
170
173
|
await writeFile(standard.path, `${await readFile(standard.path, 'utf8')}\n- id: later\n name: '/opt/preset/later.js'\n`)
|
|
171
174
|
const changed = await presets.mount({}, 'standard')
|
|
@@ -244,14 +244,14 @@ describe('context compression profiles', () => {
|
|
|
244
244
|
},
|
|
245
245
|
)
|
|
246
246
|
|
|
247
|
-
it('
|
|
247
|
+
it('keeps the selector selectable under a Minimal preset: 0.1.5 no longer exposes agentPreset client-side, so availability cannot gate the UI', () => {
|
|
248
248
|
const { state } = renderReady(
|
|
249
249
|
vi.fn(() => Promise.resolve()), false, key => COPY[key] ?? key,
|
|
250
250
|
{ agentPreset: 'minimal' },
|
|
251
251
|
)
|
|
252
252
|
|
|
253
|
-
expect(screen.
|
|
254
|
-
expect(screen.getByRole<HTMLButtonElement>('button', { name: /Context compression/ }).disabled).toBe(
|
|
253
|
+
expect(screen.queryByText(/Minimal mode does not load context compression.*effectively off.*native behavior remains/)).toBeNull()
|
|
254
|
+
expect(screen.getByRole<HTMLButtonElement>('button', { name: /Context compression/ }).disabled).toBe(false)
|
|
255
255
|
expect(state.getSnapshot().value?.profile).toBe('native')
|
|
256
256
|
})
|
|
257
257
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
import { describe, expect, it } from 'vitest'
|
|
4
4
|
|
|
@@ -19,10 +19,34 @@ describe('standalone package contract', () => {
|
|
|
19
19
|
expect(Object.keys(selector.peerDependencies ?? {})).not.toContain('dsh-context-compression-improved-runtime')
|
|
20
20
|
expect(Object.keys(selector.peerDependencies ?? {})).not.toContain('@deepseek-ai/dsh-compaction-tool-result-pruner')
|
|
21
21
|
expect(selector.peerDependencies?.['@deepseek-ai/dsh-compaction-basic'])
|
|
22
|
-
.toBe('>=0.1.
|
|
22
|
+
.toBe('>=0.1.5-rc.2 <0.2.0-0')
|
|
23
23
|
expect(selector.peerDependencies?.['@deepseek-ai/dsh-command-compact'])
|
|
24
|
-
.toBe('>=0.1.
|
|
24
|
+
.toBe('>=0.1.5-rc.2 <0.2.0-0')
|
|
25
25
|
expect(selector.publishConfig?.tag).toBe('latest')
|
|
26
|
+
expect(existsSync(resolve(root, '../runtime/package.json'))).toBe(false)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('makes the repository root the one git install surface', () => {
|
|
30
|
+
const rootManifest = JSON.parse(readFileSync(resolve(root, '../package.json'), 'utf8')) as {
|
|
31
|
+
name: string
|
|
32
|
+
exports?: Record<string, unknown>
|
|
33
|
+
dependencies?: Record<string, string>
|
|
34
|
+
engines?: Record<string, string>
|
|
35
|
+
dsh?: { bundle?: { patch?: string }, client?: { inject?: string[] } }
|
|
36
|
+
}
|
|
37
|
+
expect(rootManifest.name).toBe('dsh-context-compression-improved')
|
|
38
|
+
for (const specifier of ['.', './invariant', './pruner', './client']) {
|
|
39
|
+
expect(rootManifest.exports?.[specifier]).toBeDefined()
|
|
40
|
+
}
|
|
41
|
+
expect(rootManifest.dependencies?.['@huggingface/tokenizers']).toBe('0.1.3')
|
|
42
|
+
expect(rootManifest.dependencies?.['js-yaml']).toBe('^4.3.2')
|
|
43
|
+
expect(rootManifest.dsh?.bundle?.patch).toBe('./packages/selector/cordis.patch.yml')
|
|
44
|
+
expect(rootManifest.dsh?.client?.inject).toEqual([
|
|
45
|
+
'@deepseek-ai/dsh-client-locale',
|
|
46
|
+
'@deepseek-ai/dsh-client-ui-slots',
|
|
47
|
+
'@deepseek-ai/dsh-client-ui-settings',
|
|
48
|
+
])
|
|
49
|
+
expect(rootManifest.engines?.dsh).toBe('>=0.1.5-alpha.1 <0.2.0-0')
|
|
26
50
|
})
|
|
27
51
|
|
|
28
52
|
it('uses the community package in the one Bundle patch', () => {
|