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,250 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
2
|
+
import { Context } from '@deepseek-ai/cordis'
|
|
3
|
+
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
|
4
|
+
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
|
5
|
+
import { createUserMessage, type GenerateOptions } from '@deepseek-ai/dsh-llm'
|
|
6
|
+
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
|
|
7
|
+
import { SessionId } from '@deepseek-ai/dsh-session'
|
|
8
|
+
import SubagentRuntime from '@deepseek-ai/dsh-subagent'
|
|
9
|
+
import * as Fork from '@deepseek-ai/dsh-subagent-fork-in-process'
|
|
10
|
+
import * as Spawn from '@deepseek-ai/dsh-subagent-spawn-in-process'
|
|
11
|
+
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
|
12
|
+
import { PublicMockAdapter, publicTextResponse } from './support/mock-adapter.ts'
|
|
13
|
+
import {
|
|
14
|
+
assessReusablePrefix,
|
|
15
|
+
buildSafeCacheAuditRecord,
|
|
16
|
+
fingerprintStablePrefix,
|
|
17
|
+
type JsonValue,
|
|
18
|
+
type StablePrefixEnvelope,
|
|
19
|
+
} from './support/cache-prefix-audit.js'
|
|
20
|
+
|
|
21
|
+
const SYSTEM = 'You are a terse assistant in an isolated parent-child cache-prefix test. '
|
|
22
|
+
+ 'Follow the latest user instruction literally. Answer in one short sentence. '
|
|
23
|
+
+ 'Do not use markdown, tools, timestamps, random identifiers, environment values, '
|
|
24
|
+
+ 'or unstated facts. This deliberately long stable persona supplies enough repeated '
|
|
25
|
+
+ 'context for the provider cache while remaining identical for parent and child requests.'
|
|
26
|
+
|
|
27
|
+
const PARENT_TEXT = 'Remember that the stable test phrase is cobalt-heron-42 and repeat it once.'
|
|
28
|
+
const CHILD_TEXT = 'Repeat the stable test phrase once more.'
|
|
29
|
+
const SPAWN_TEXT = 'Answer only with the word fresh.'
|
|
30
|
+
|
|
31
|
+
let ctx: Context | undefined
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await ctx?.fiber.dispose()
|
|
35
|
+
ctx = undefined
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const jsonValue = (value: unknown): JsonValue => JSON.parse(JSON.stringify(value)) as JsonValue
|
|
39
|
+
|
|
40
|
+
const envelope = (options: GenerateOptions, messageCount = options.messages.length): StablePrefixEnvelope => ({
|
|
41
|
+
provider: options.provider,
|
|
42
|
+
model: options.model,
|
|
43
|
+
system: options.system ?? null,
|
|
44
|
+
tools: jsonValue(options.tools ?? []) as JsonValue[],
|
|
45
|
+
messages: jsonValue(options.messages.slice(0, messageCount)) as JsonValue[],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
async function liveHarness(requests: Map<string, GenerateOptions[]>): Promise<Context> {
|
|
49
|
+
const created = new Context()
|
|
50
|
+
await mountAgentLoopTestDependencies(created, { systemPrompt: { persona: SYSTEM } })
|
|
51
|
+
await created.plugin(TokenMeter)
|
|
52
|
+
await created.plugin(AgentLoop, { agents: [] })
|
|
53
|
+
await created.plugin(SubagentRuntime)
|
|
54
|
+
await created.plugin(Fork, { providerName: 'fork' })
|
|
55
|
+
await created.plugin(Spawn, { providerName: 'spawn' })
|
|
56
|
+
await created.plugin(LlmDeepSeek)
|
|
57
|
+
created.on('llm/stream', (options, next) => {
|
|
58
|
+
const sessionId = String(options.sessionId)
|
|
59
|
+
requests.set(sessionId, [...(requests.get(sessionId) ?? []), options])
|
|
60
|
+
return next()
|
|
61
|
+
})
|
|
62
|
+
return created
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function keylessHarness(requests: Map<string, GenerateOptions[]>): Promise<Context> {
|
|
66
|
+
const created = new Context()
|
|
67
|
+
await mountAgentLoopTestDependencies(created, { systemPrompt: { persona: SYSTEM } })
|
|
68
|
+
await created.plugin(AgentLoop, { agents: [] })
|
|
69
|
+
await created.plugin(SubagentRuntime)
|
|
70
|
+
await created.plugin(Fork, { providerName: 'fork' })
|
|
71
|
+
await created.plugin(Spawn, { providerName: 'spawn' })
|
|
72
|
+
created.llm.registerAdapter(['mock'], new PublicMockAdapter([
|
|
73
|
+
publicTextResponse('cobalt-heron-42'),
|
|
74
|
+
publicTextResponse('cobalt-heron-42'),
|
|
75
|
+
publicTextResponse('fresh'),
|
|
76
|
+
]))
|
|
77
|
+
created.on('llm/stream', (options, next) => {
|
|
78
|
+
const sessionId = String(options.sessionId)
|
|
79
|
+
requests.set(sessionId, [...(requests.get(sessionId) ?? []), options])
|
|
80
|
+
return next()
|
|
81
|
+
})
|
|
82
|
+
return created
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe('parent/child prefix behavior (keyless full-loop E2E)', () => {
|
|
86
|
+
it('runs parent, fork, and spawn through the real loop and session providers', async () => {
|
|
87
|
+
const requests = new Map<string, GenerateOptions[]>()
|
|
88
|
+
ctx = await keylessHarness(requests)
|
|
89
|
+
const parent = ctx.agentLoop.create(SessionId('cache-parent-keyless-e2e'), {
|
|
90
|
+
provider: 'mock',
|
|
91
|
+
model: 'mock',
|
|
92
|
+
})
|
|
93
|
+
parent.followup(createUserMessage({
|
|
94
|
+
content: [{ type: 'text', text: PARENT_TEXT }],
|
|
95
|
+
source: { kind: 'user' },
|
|
96
|
+
}))
|
|
97
|
+
await parent.whenIdle()
|
|
98
|
+
const parentEvents = parent.session.events.slice()
|
|
99
|
+
const parentRequest = requests.get(String(parent.id))?.at(-1)
|
|
100
|
+
expect(parentRequest).toBeDefined()
|
|
101
|
+
|
|
102
|
+
const forkRun = await ctx.subagents.start('fork', {
|
|
103
|
+
prompt: [{ type: 'text', text: CHILD_TEXT }],
|
|
104
|
+
parent,
|
|
105
|
+
signal: new AbortController().signal,
|
|
106
|
+
})
|
|
107
|
+
await forkRun.result
|
|
108
|
+
const forkChild = forkRun.localAgent
|
|
109
|
+
const forkRequest = requests.get(String(forkRun.id))?.at(-1)
|
|
110
|
+
expect(forkChild).toBeDefined()
|
|
111
|
+
expect(forkRequest).toBeDefined()
|
|
112
|
+
if (forkChild === undefined || forkRequest === undefined || parentRequest === undefined) {
|
|
113
|
+
throw new Error('keyless fork did not publish its complete evidence')
|
|
114
|
+
}
|
|
115
|
+
expect(forkChild.session.header.seedLength).toBe(parentEvents.length)
|
|
116
|
+
expect(forkChild.session.events.slice(0, parentEvents.length)).toEqual(parentEvents)
|
|
117
|
+
const parentStable = envelope(parentRequest)
|
|
118
|
+
const forkStablePrefix = envelope(forkRequest, parentRequest.messages.length)
|
|
119
|
+
expect(fingerprintStablePrefix(forkStablePrefix)).toBe(fingerprintStablePrefix(parentStable))
|
|
120
|
+
expect(Object.isFrozen(forkRequest)).toBe(true)
|
|
121
|
+
|
|
122
|
+
const spawnRun = await ctx.subagents.start('spawn', {
|
|
123
|
+
prompt: [{ type: 'text', text: SPAWN_TEXT }],
|
|
124
|
+
parent,
|
|
125
|
+
signal: new AbortController().signal,
|
|
126
|
+
})
|
|
127
|
+
await spawnRun.result
|
|
128
|
+
const spawnChild = spawnRun.localAgent
|
|
129
|
+
const spawnRequest = requests.get(String(spawnRun.id))?.at(-1)
|
|
130
|
+
expect(spawnChild?.session.header.seedLength).toBeUndefined()
|
|
131
|
+
expect(spawnRequest).toBeDefined()
|
|
132
|
+
expect(JSON.stringify(spawnRequest?.messages)).not.toContain(PARENT_TEXT)
|
|
133
|
+
expect(assessReusablePrefix({
|
|
134
|
+
mode: 'spawn',
|
|
135
|
+
inheritsParentContext: false,
|
|
136
|
+
parent: parentStable,
|
|
137
|
+
child: envelope(spawnRequest!),
|
|
138
|
+
})).toEqual({ eligible: false, reason: 'spawn-does-not-inherit' })
|
|
139
|
+
|
|
140
|
+
const safeRecord = buildSafeCacheAuditRecord({
|
|
141
|
+
fingerprint: fingerprintStablePrefix(parentStable),
|
|
142
|
+
estimatedSharedPrefixTokens: Math.ceil(JSON.stringify(parentStable).length / 4),
|
|
143
|
+
parentSessionId: String(parent.session.header.id),
|
|
144
|
+
childSessionId: String(forkChild.session.header.id),
|
|
145
|
+
mode: 'fork',
|
|
146
|
+
eligible: true,
|
|
147
|
+
reason: 'identical-fork-prefix',
|
|
148
|
+
})
|
|
149
|
+
expect(safeRecord.confirmationStatus).toBe('unconfirmed')
|
|
150
|
+
expect(JSON.stringify(safeRecord)).not.toContain(PARENT_TEXT)
|
|
151
|
+
|
|
152
|
+
await spawnRun.dispose()
|
|
153
|
+
await forkRun.dispose()
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe.skipIf(!process.env.DEEPSEEK_API_KEY)('parent/child natural cache-prefix reuse (real DeepSeek API)', () => {
|
|
158
|
+
it('preserves the fork prefix, separates spawn, and records only official usage evidence', async () => {
|
|
159
|
+
const requests = new Map<string, GenerateOptions[]>()
|
|
160
|
+
ctx = await liveHarness(requests)
|
|
161
|
+
const parent = ctx.agentLoop.create(SessionId('cache-parent-e2e'), {
|
|
162
|
+
provider: 'deepseek-official',
|
|
163
|
+
model: 'deepseek-v4-flash',
|
|
164
|
+
})
|
|
165
|
+
parent.followup(createUserMessage({
|
|
166
|
+
content: [{ type: 'text', text: PARENT_TEXT }],
|
|
167
|
+
source: { kind: 'user' },
|
|
168
|
+
}))
|
|
169
|
+
await parent.whenIdle()
|
|
170
|
+
const parentEvents = parent.session.events.slice()
|
|
171
|
+
const parentRequest = requests.get(String(parent.id))?.at(-1)
|
|
172
|
+
expect(parentRequest).toBeDefined()
|
|
173
|
+
|
|
174
|
+
const forkRun = await ctx.subagents.start('fork', {
|
|
175
|
+
prompt: [{ type: 'text', text: CHILD_TEXT }],
|
|
176
|
+
parent,
|
|
177
|
+
signal: new AbortController().signal,
|
|
178
|
+
})
|
|
179
|
+
await forkRun.result
|
|
180
|
+
const forkChild = forkRun.localAgent
|
|
181
|
+
expect(forkChild).toBeDefined()
|
|
182
|
+
if (forkChild === undefined) throw new Error('fork did not publish a local agent')
|
|
183
|
+
const forkRequest = requests.get(String(forkRun.id))?.at(-1)
|
|
184
|
+
expect(forkRequest).toBeDefined()
|
|
185
|
+
expect(forkChild.session.header).toMatchObject({
|
|
186
|
+
parentSession: parent.session.header.id,
|
|
187
|
+
seedLength: parentEvents.length,
|
|
188
|
+
})
|
|
189
|
+
expect(forkChild.session.events.slice(0, parentEvents.length)).toEqual(parentEvents)
|
|
190
|
+
|
|
191
|
+
const parentStable = envelope(parentRequest!)
|
|
192
|
+
const forkStablePrefix = envelope(forkRequest!, parentRequest!.messages.length)
|
|
193
|
+
expect(assessReusablePrefix({
|
|
194
|
+
mode: 'fork',
|
|
195
|
+
inheritsParentContext: true,
|
|
196
|
+
parent: parentStable,
|
|
197
|
+
child: forkStablePrefix,
|
|
198
|
+
})).toEqual({ eligible: true, reason: 'identical-fork-prefix' })
|
|
199
|
+
expect(fingerprintStablePrefix(parentStable)).toBe(fingerprintStablePrefix(forkStablePrefix))
|
|
200
|
+
expect(Object.isFrozen(forkRequest)).toBe(true)
|
|
201
|
+
|
|
202
|
+
const measurement = ctx.tokenMeter.measure(forkChild.session)
|
|
203
|
+
const usage = measurement.baseline.kind === 'usage' ? measurement.baseline.usage : undefined
|
|
204
|
+
const cacheMissTokens = usage === undefined
|
|
205
|
+
? undefined
|
|
206
|
+
: usage.inputTokens + (usage.cacheWriteTokens ?? 0)
|
|
207
|
+
const observedPromptTokens = usage === undefined
|
|
208
|
+
? undefined
|
|
209
|
+
: cacheMissTokens! + (usage.cacheReadTokens ?? 0)
|
|
210
|
+
const record = buildSafeCacheAuditRecord({
|
|
211
|
+
fingerprint: fingerprintStablePrefix(parentStable),
|
|
212
|
+
estimatedSharedPrefixTokens: Math.ceil(JSON.stringify(parentStable).length / 4),
|
|
213
|
+
parentSessionId: String(parent.session.header.id),
|
|
214
|
+
childSessionId: String(forkChild.session.header.id),
|
|
215
|
+
mode: 'fork',
|
|
216
|
+
eligible: true,
|
|
217
|
+
reason: 'identical-fork-prefix',
|
|
218
|
+
...(usage?.cacheReadTokens === undefined ? {} : { cacheReadTokens: usage.cacheReadTokens }),
|
|
219
|
+
...(cacheMissTokens === undefined ? {} : { cacheMissTokens }),
|
|
220
|
+
...(observedPromptTokens === undefined ? {} : { observedPromptTokens }),
|
|
221
|
+
})
|
|
222
|
+
expect(JSON.stringify(record)).not.toContain(PARENT_TEXT)
|
|
223
|
+
expect(JSON.stringify(record)).not.toContain(CHILD_TEXT)
|
|
224
|
+
console.info(`SUBAGENT_CACHE_E2E ${JSON.stringify(record)}`)
|
|
225
|
+
|
|
226
|
+
const spawnRun = await ctx.subagents.start('spawn', {
|
|
227
|
+
prompt: [{ type: 'text', text: SPAWN_TEXT }],
|
|
228
|
+
parent,
|
|
229
|
+
signal: new AbortController().signal,
|
|
230
|
+
})
|
|
231
|
+
await spawnRun.result
|
|
232
|
+
const spawnChild = spawnRun.localAgent
|
|
233
|
+
expect(spawnChild).toBeDefined()
|
|
234
|
+
if (spawnChild === undefined) throw new Error('spawn did not publish a local agent')
|
|
235
|
+
const spawnRequest = requests.get(String(spawnRun.id))?.at(-1)
|
|
236
|
+
expect(spawnChild.session.header.parentSession).toBe(parent.session.header.id)
|
|
237
|
+
expect(spawnChild.session.header.seedLength).toBeUndefined()
|
|
238
|
+
expect(spawnRequest).toBeDefined()
|
|
239
|
+
expect(JSON.stringify(spawnRequest!.messages)).not.toContain(PARENT_TEXT)
|
|
240
|
+
expect(assessReusablePrefix({
|
|
241
|
+
mode: 'spawn',
|
|
242
|
+
inheritsParentContext: false,
|
|
243
|
+
parent: parentStable,
|
|
244
|
+
child: envelope(spawnRequest!),
|
|
245
|
+
})).toEqual({ eligible: false, reason: 'spawn-does-not-inherit' })
|
|
246
|
+
|
|
247
|
+
await spawnRun.dispose()
|
|
248
|
+
await forkRun.dispose()
|
|
249
|
+
}, 180_000)
|
|
250
|
+
})
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
const CACHE_PREFIX_AUDIT_VERSION = 'dsh-prefix-audit-v1' as const
|
|
4
|
+
|
|
5
|
+
type JsonPrimitive = boolean | number | string | null
|
|
6
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }
|
|
7
|
+
|
|
8
|
+
export interface StablePrefixEnvelope {
|
|
9
|
+
provider: string
|
|
10
|
+
model: string
|
|
11
|
+
system: JsonValue
|
|
12
|
+
tools: JsonValue[]
|
|
13
|
+
messages: JsonValue[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PrefixAssessmentInput {
|
|
17
|
+
mode: 'fork' | 'spawn'
|
|
18
|
+
inheritsParentContext: boolean
|
|
19
|
+
parent: StablePrefixEnvelope
|
|
20
|
+
child: StablePrefixEnvelope
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CacheUsage {
|
|
24
|
+
cacheReadTokens: number
|
|
25
|
+
cacheMissTokens: number
|
|
26
|
+
observedPromptTokens: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SafeCacheAuditInput {
|
|
30
|
+
fingerprint: string
|
|
31
|
+
estimatedSharedPrefixTokens: number
|
|
32
|
+
parentSessionId: string
|
|
33
|
+
childSessionId: string
|
|
34
|
+
mode: 'fork' | 'spawn'
|
|
35
|
+
eligible: boolean
|
|
36
|
+
reason: string
|
|
37
|
+
cacheReadTokens?: number
|
|
38
|
+
cacheMissTokens?: number
|
|
39
|
+
observedPromptTokens?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const canonicalize = (value: unknown): unknown => {
|
|
43
|
+
if (Array.isArray(value)) return value.map(canonicalize)
|
|
44
|
+
if (value === null || typeof value !== 'object') return value
|
|
45
|
+
|
|
46
|
+
return Object.fromEntries(
|
|
47
|
+
Object.entries(value as Record<string, unknown>)
|
|
48
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
49
|
+
.map(([key, item]) => [key, canonicalize(item)]),
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const canonicalJson = (value: unknown): string => JSON.stringify(canonicalize(value))
|
|
54
|
+
|
|
55
|
+
export const fingerprintStablePrefix = (envelope: StablePrefixEnvelope): string => {
|
|
56
|
+
const payload = canonicalJson({ auditVersion: CACHE_PREFIX_AUDIT_VERSION, envelope })
|
|
57
|
+
return createHash('sha256').update(payload, 'utf8').digest('hex')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const assessReusablePrefix = (
|
|
61
|
+
input: PrefixAssessmentInput,
|
|
62
|
+
): { eligible: boolean; reason: 'identical-fork-prefix' | 'spawn-does-not-inherit' | 'stable-prefix-mismatch' } => {
|
|
63
|
+
if (input.mode === 'spawn' || !input.inheritsParentContext) {
|
|
64
|
+
return { eligible: false, reason: 'spawn-does-not-inherit' }
|
|
65
|
+
}
|
|
66
|
+
if (canonicalJson(input.parent) !== canonicalJson(input.child)) {
|
|
67
|
+
return { eligible: false, reason: 'stable-prefix-mismatch' }
|
|
68
|
+
}
|
|
69
|
+
return { eligible: true, reason: 'identical-fork-prefix' }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const validateCacheUsage = (usage: CacheUsage): boolean => (
|
|
73
|
+
Number.isSafeInteger(usage.cacheReadTokens)
|
|
74
|
+
&& Number.isSafeInteger(usage.cacheMissTokens)
|
|
75
|
+
&& Number.isSafeInteger(usage.observedPromptTokens)
|
|
76
|
+
&& usage.cacheReadTokens >= 0
|
|
77
|
+
&& usage.cacheMissTokens >= 0
|
|
78
|
+
&& usage.observedPromptTokens >= 0
|
|
79
|
+
&& usage.cacheReadTokens + usage.cacheMissTokens === usage.observedPromptTokens
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
export const buildSafeCacheAuditRecord = (input: SafeCacheAuditInput) => ({
|
|
83
|
+
auditVersion: CACHE_PREFIX_AUDIT_VERSION,
|
|
84
|
+
fingerprint: input.fingerprint,
|
|
85
|
+
estimatedSharedPrefixTokens: input.estimatedSharedPrefixTokens,
|
|
86
|
+
parentSessionId: input.parentSessionId,
|
|
87
|
+
childSessionId: input.childSessionId,
|
|
88
|
+
mode: input.mode,
|
|
89
|
+
eligible: input.eligible,
|
|
90
|
+
reason: input.reason,
|
|
91
|
+
cacheReadTokens: input.cacheReadTokens ?? null,
|
|
92
|
+
cacheMissTokens: input.cacheMissTokens ?? null,
|
|
93
|
+
observedPromptTokens: input.observedPromptTokens ?? null,
|
|
94
|
+
confirmationStatus: input.cacheReadTokens !== undefined
|
|
95
|
+
&& input.cacheMissTokens !== undefined
|
|
96
|
+
&& input.observedPromptTokens !== undefined
|
|
97
|
+
&& input.cacheReadTokens > 0
|
|
98
|
+
&& validateCacheUsage({
|
|
99
|
+
cacheReadTokens: input.cacheReadTokens,
|
|
100
|
+
cacheMissTokens: input.cacheMissTokens,
|
|
101
|
+
observedPromptTokens: input.observedPromptTokens,
|
|
102
|
+
})
|
|
103
|
+
? 'confirmed'
|
|
104
|
+
: 'unconfirmed',
|
|
105
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GenerateOptions,
|
|
3
|
+
LlmResolvedModelInfo,
|
|
4
|
+
StreamChunk,
|
|
5
|
+
} from '@deepseek-ai/dsh-llm'
|
|
6
|
+
import { LlmAdapter } from '@deepseek-ai/dsh-llm'
|
|
7
|
+
|
|
8
|
+
/** Minimal scripted adapter using only the published LLM test surface. */
|
|
9
|
+
export class PublicMockAdapter extends LlmAdapter {
|
|
10
|
+
constructor(private readonly script: StreamChunk[][]) {
|
|
11
|
+
super()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
override resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo> {
|
|
15
|
+
return Promise.resolve({ provider, id: model, name: model })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
|
19
|
+
const chunks = this.script.shift()
|
|
20
|
+
if (chunks === undefined) throw new Error('PublicMockAdapter: script exhausted')
|
|
21
|
+
for (const chunk of chunks) {
|
|
22
|
+
if (options.signal?.aborted === true) throw new Error('aborted')
|
|
23
|
+
yield chunk
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One deterministic text response for the concrete AgentLoop. */
|
|
29
|
+
export function publicTextResponse(text: string): StreamChunk[] {
|
|
30
|
+
return [
|
|
31
|
+
{ type: 'block-start', index: 0, blockType: 'text' },
|
|
32
|
+
{ type: 'text-delta', index: 0, text },
|
|
33
|
+
{ type: 'block-end', index: 0, block: { type: 'text', text } },
|
|
34
|
+
{ type: 'usage', usage: { inputTokens: 10, outputTokens: text.length } },
|
|
35
|
+
{ type: 'finish', reason: { kind: 'stop' } },
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
interface MenuItem {
|
|
4
|
+
id: string
|
|
5
|
+
label: ReactNode
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface MenuProps {
|
|
9
|
+
anchor: ReactNode
|
|
10
|
+
items: readonly MenuItem[]
|
|
11
|
+
onSelect: (id: string) => void
|
|
12
|
+
open: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function IconChevronDownOutline14({ className }: { className?: string }) {
|
|
16
|
+
return <span aria-hidden="true" className={className}>⌄</span>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Menu({ anchor, items, onSelect, open }: MenuProps) {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
{anchor}
|
|
23
|
+
{open ? (
|
|
24
|
+
<div role="menu">
|
|
25
|
+
{items.map(item => (
|
|
26
|
+
<button key={item.id} role="menuitem" type="button" onClick={() => { onSelect(item.id) }}>
|
|
27
|
+
{item.label}
|
|
28
|
+
</button>
|
|
29
|
+
))}
|
|
30
|
+
</div>
|
|
31
|
+
) : null}
|
|
32
|
+
</>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { basename, dirname, relative, resolve } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { defineConfig } from 'tsdown'
|
|
6
|
+
import { transform } from 'lightningcss'
|
|
7
|
+
|
|
8
|
+
const PLUGIN_ID = 'dsh-context-compression-improved'
|
|
9
|
+
const SELECTOR_ROOT = dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const CSS_PREFIX = '\0dsh-context-compression-css:'
|
|
11
|
+
const CSS_SUFFIX = '.mjs'
|
|
12
|
+
const CLIENT_EXTERNALS = new Set([
|
|
13
|
+
'react',
|
|
14
|
+
'react/jsx-runtime',
|
|
15
|
+
'@deepseek-ai/dsh-client-ui-primitives',
|
|
16
|
+
])
|
|
17
|
+
const cssFiles = new Map<string, string>()
|
|
18
|
+
|
|
19
|
+
function stableCssFileId(file: string): string {
|
|
20
|
+
const id = relative(SELECTOR_ROOT, file).replaceAll('\\', '/')
|
|
21
|
+
if (id === '..' || id.startsWith('../')) {
|
|
22
|
+
throw new Error(`CSS module is outside the selector package: ${id}`)
|
|
23
|
+
}
|
|
24
|
+
return id
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function styleModule(file: string, css: string, classMap: Readonly<Record<string, string>>): string {
|
|
28
|
+
const tagId = `${PLUGIN_ID}/${basename(file)}`
|
|
29
|
+
return [
|
|
30
|
+
`const css = ${JSON.stringify(css)};`,
|
|
31
|
+
`const tagId = ${JSON.stringify(tagId)};`,
|
|
32
|
+
'if (typeof document !== "undefined" && document.querySelector(`style[data-plugin-css="${tagId}"]`) === null) {',
|
|
33
|
+
' const tag = document.createElement("style");',
|
|
34
|
+
` tag.dataset.plugin = ${JSON.stringify(PLUGIN_ID)};`,
|
|
35
|
+
' tag.dataset.pluginCss = tagId;',
|
|
36
|
+
' tag.textContent = css;',
|
|
37
|
+
' document.head.appendChild(tag);',
|
|
38
|
+
'}',
|
|
39
|
+
`export default ${JSON.stringify(classMap)};`,
|
|
40
|
+
].join('\n')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
name: `${PLUGIN_ID}/client`,
|
|
45
|
+
entry: { client: 'src/client/index.ts' },
|
|
46
|
+
outDir: 'lib',
|
|
47
|
+
format: 'cjs',
|
|
48
|
+
platform: 'browser',
|
|
49
|
+
target: 'es2023',
|
|
50
|
+
dts: false,
|
|
51
|
+
clean: false,
|
|
52
|
+
fixedExtension: false,
|
|
53
|
+
hash: false,
|
|
54
|
+
sourcemap: false,
|
|
55
|
+
deps: {
|
|
56
|
+
neverBundle: specifier => CLIENT_EXTERNALS.has(specifier),
|
|
57
|
+
alwaysBundle: specifier => !CLIENT_EXTERNALS.has(specifier),
|
|
58
|
+
},
|
|
59
|
+
define: {
|
|
60
|
+
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV ?? 'production'),
|
|
61
|
+
'import.meta.env.MODE': JSON.stringify(process.env.NODE_ENV ?? 'production'),
|
|
62
|
+
'import.meta.env': JSON.stringify({ MODE: process.env.NODE_ENV ?? 'production' }),
|
|
63
|
+
},
|
|
64
|
+
plugins: [{
|
|
65
|
+
name: 'dsh-context-compression-css-inline',
|
|
66
|
+
resolveId(source: string, importer: string | undefined) {
|
|
67
|
+
if (!source.endsWith('.module.css')) return null
|
|
68
|
+
const file = importer === undefined ? source : resolve(dirname(importer), source)
|
|
69
|
+
const digest = createHash('sha256').update(stableCssFileId(file)).digest('hex').slice(0, 12)
|
|
70
|
+
const id = `${CSS_PREFIX}${digest}-${basename(file)}${CSS_SUFFIX}`
|
|
71
|
+
cssFiles.set(id, file)
|
|
72
|
+
return id
|
|
73
|
+
},
|
|
74
|
+
async load(id: string) {
|
|
75
|
+
const file = cssFiles.get(id)
|
|
76
|
+
if (file === undefined) return null
|
|
77
|
+
this.addWatchFile(file)
|
|
78
|
+
const source = await readFile(file)
|
|
79
|
+
const stableFile = stableCssFileId(file)
|
|
80
|
+
const result = transform({
|
|
81
|
+
// lightningcss includes filename in CSS Modules hashes. A package-
|
|
82
|
+
// relative identity keeps reviewed tarballs byte-for-byte stable when
|
|
83
|
+
// the repository is cloned or moved to a different absolute path.
|
|
84
|
+
filename: stableFile,
|
|
85
|
+
code: source,
|
|
86
|
+
cssModules: { pattern: '[hash]_[local]' },
|
|
87
|
+
minify: true,
|
|
88
|
+
})
|
|
89
|
+
const classMap: Record<string, string> = {}
|
|
90
|
+
for (const [local, value] of Object.entries(result.exports ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
|
|
91
|
+
classMap[local] = value.name
|
|
92
|
+
}
|
|
93
|
+
return styleModule(file, result.code.toString(), classMap)
|
|
94
|
+
},
|
|
95
|
+
}],
|
|
96
|
+
outputOptions: {
|
|
97
|
+
entryFileNames: 'client.js',
|
|
98
|
+
banner: `window.__ModuleLoader__.load({ id: ${JSON.stringify(PLUGIN_ID)}, factory: (require) => {`,
|
|
99
|
+
intro: 'var module = { exports: {} }; var exports = module.exports;',
|
|
100
|
+
footer: 'return module.exports; } });',
|
|
101
|
+
},
|
|
102
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineConfig } from 'tsdown'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: {
|
|
5
|
+
index: 'src/index.ts',
|
|
6
|
+
pruner: 'src/pruner.ts',
|
|
7
|
+
invariant: 'src/invariant.ts',
|
|
8
|
+
// This ESM face supplies client.d.ts. The sequential client build then
|
|
9
|
+
// overwrites only client.js with the Harness lazy-CJS artifact.
|
|
10
|
+
client: 'src/client/index.ts',
|
|
11
|
+
},
|
|
12
|
+
format: 'esm',
|
|
13
|
+
dts: true,
|
|
14
|
+
clean: true,
|
|
15
|
+
outDir: 'lib',
|
|
16
|
+
fixedExtension: false,
|
|
17
|
+
hash: false,
|
|
18
|
+
sourcemap: false,
|
|
19
|
+
deps: { neverBundle: true },
|
|
20
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
packages:
|
|
2
|
+
- packages/*
|
|
3
|
+
|
|
4
|
+
linkWorkspacePackages: true
|
|
5
|
+
|
|
6
|
+
peerDependencyRules:
|
|
7
|
+
allowedVersions:
|
|
8
|
+
typescript: ">=5 <7"
|
|
9
|
+
|
|
10
|
+
allowBuilds:
|
|
11
|
+
esbuild: true
|
|
12
|
+
"@huggingface/tokenizers": true
|
|
13
|
+
|
|
14
|
+
# GHSA-2883-xcg3-v3hh - js-yaml < 4.3.2 does not bound CPU use for empty merge
|
|
15
|
+
# sources. @deepseek-ai/cordis-plugin-include and @deepseek-ai/dsh-agent-presets
|
|
16
|
+
# both declare ranges that already allow the patch, yet keep resolving 4.3.1, so
|
|
17
|
+
# the floor is pinned here instead of waiting on their next release.
|
|
18
|
+
overrides:
|
|
19
|
+
js-yaml@<4.3.2: ^4.3.2
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-off baseline capture: freeze the resolvePolicy output for the pre-existing
|
|
3
|
+
* profiles (before 'tokenpilot-inspired' is added) into a JSON fixture used by
|
|
4
|
+
* tests/runtime/tokenpilot/profile-baseline.spec.ts as the backward-compat golden.
|
|
5
|
+
*
|
|
6
|
+
* Usage: node scripts-dist/capture-profile-baseline.js [baseline-file]
|
|
7
|
+
*/
|
|
8
|
+
import { writeFileSync } from 'node:fs'
|
|
9
|
+
import { createRequire } from 'node:module'
|
|
10
|
+
|
|
11
|
+
/** The slice of the built pruner entry this capture consumes. */
|
|
12
|
+
interface PrunerRuntime {
|
|
13
|
+
resolveConfig: (input: Record<string, unknown>) => Record<string, unknown>
|
|
14
|
+
resolvePolicy: (
|
|
15
|
+
config: Record<string, unknown>,
|
|
16
|
+
profile: string,
|
|
17
|
+
custom: unknown,
|
|
18
|
+
options: Record<string, unknown>,
|
|
19
|
+
) => Record<string, unknown>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** One option matrix row. */
|
|
23
|
+
interface OptionCase {
|
|
24
|
+
label: string
|
|
25
|
+
options: Record<string, unknown>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One custom-document matrix row. */
|
|
29
|
+
interface CustomDocCase {
|
|
30
|
+
label: string
|
|
31
|
+
custom: unknown
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The fixture shape `profile-baseline.spec.ts` reads back. */
|
|
35
|
+
interface ProfileBaseline {
|
|
36
|
+
capturedAt: string
|
|
37
|
+
profiles: Record<string, Record<string, Record<string, unknown>>>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const require = createRequire(import.meta.url)
|
|
41
|
+
const runtime = require('../packages/selector/lib/pruner.js') as PrunerRuntime
|
|
42
|
+
|
|
43
|
+
const OLD_PROFILES: readonly string[] = [
|
|
44
|
+
'off',
|
|
45
|
+
'native',
|
|
46
|
+
'balanced',
|
|
47
|
+
'cache-strict',
|
|
48
|
+
'savings',
|
|
49
|
+
'adaptive',
|
|
50
|
+
'custom',
|
|
51
|
+
]
|
|
52
|
+
const baseConfig = runtime.resolveConfig({})
|
|
53
|
+
const OPTION_CASES: readonly OptionCase[] = [
|
|
54
|
+
{ label: 'defaults', options: {} },
|
|
55
|
+
{ label: 'linkage', options: { contextWindowTokens: 128_000, autoCompactThresholdPercent: 80 } },
|
|
56
|
+
]
|
|
57
|
+
const CUSTOM_DOCS: readonly CustomDocCase[] = [
|
|
58
|
+
{ label: 'default-custom', custom: undefined },
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
const snapshot: ProfileBaseline = { capturedAt: 'pre-tokenpilot-inspired', profiles: {} }
|
|
62
|
+
for (const profile of OLD_PROFILES) {
|
|
63
|
+
const byCase: Record<string, Record<string, unknown>> = {}
|
|
64
|
+
snapshot.profiles[profile] = byCase
|
|
65
|
+
for (const optionCase of OPTION_CASES) {
|
|
66
|
+
for (const customCase of CUSTOM_DOCS) {
|
|
67
|
+
const key = `${optionCase.label}${customCase.label === 'default-custom' ? '' : `/${customCase.label}`}`
|
|
68
|
+
byCase[key] = runtime.resolvePolicy(
|
|
69
|
+
baseConfig,
|
|
70
|
+
profile,
|
|
71
|
+
customCase.custom,
|
|
72
|
+
optionCase.options,
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const out = process.argv[2] ?? 'packages/selector/tests/runtime/fixtures/profile-baseline.json'
|
|
79
|
+
writeFileSync(out, `${JSON.stringify(snapshot, null, 2)}\n`)
|
|
80
|
+
console.log(`baseline written: ${out}`)
|