dsh-context-compression-improved 0.2.1 → 0.3.1
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
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// and cordis augmented services that are not in the scripts tsconfig scope.
|
|
4
1
|
import { realpath } from 'node:fs/promises'
|
|
5
2
|
import { createRequire } from 'node:module'
|
|
6
3
|
import { dirname, sep } from 'node:path'
|
|
7
4
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
8
5
|
import { Context } from '@deepseek-ai/cordis'
|
|
9
|
-
import { agentEvents
|
|
6
|
+
import { agentEvents } from '@deepseek-ai/dsh-agent'
|
|
10
7
|
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
|
11
8
|
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
|
12
9
|
import BasicCompactionEngine from '@deepseek-ai/dsh-compaction-basic'
|
|
13
10
|
import LlmRuntime, {
|
|
14
|
-
CallId,
|
|
11
|
+
ToolCallId as CallId,
|
|
15
12
|
createMessage,
|
|
16
13
|
createUserMessage,
|
|
17
14
|
createToolResultMessage,
|
|
18
15
|
LlmAdapter,
|
|
19
16
|
} from '@deepseek-ai/dsh-llm'
|
|
20
|
-
import SessionStore, { SessionId, canonicalHeader } from '@deepseek-ai/dsh-session'
|
|
21
|
-
import { SettingsProvider
|
|
17
|
+
import SessionStore, { Session, SessionId, canonicalHeader } from '@deepseek-ai/dsh-session'
|
|
18
|
+
import { SettingsProvider } from '@deepseek-ai/dsh-settings'
|
|
22
19
|
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
|
20
|
+
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
|
23
21
|
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
|
24
22
|
import ToolRuntime from '@deepseek-ai/dsh-tools'
|
|
25
23
|
|
|
@@ -31,9 +29,9 @@ const selectorPackage = consumerRequire.resolve('dsh-context-compression-improve
|
|
|
31
29
|
const selectorRequire = createRequire(selectorPackage)
|
|
32
30
|
const prunerEntry = selectorRequire.resolve('dsh-context-compression-improved/pruner')
|
|
33
31
|
const SelectorHost = await import(pathToFileURL(consumerRequire.resolve('dsh-context-compression-improved')).href)
|
|
34
|
-
const Runtime = await import(pathToFileURL(prunerEntry).href)
|
|
32
|
+
const Runtime = await import(pathToFileURL(prunerEntry).href)
|
|
35
33
|
|
|
36
|
-
const assert = (condition
|
|
34
|
+
const assert = (condition, message) => {
|
|
37
35
|
if (!condition) throw new Error(`packed component smoke: ${message}`)
|
|
38
36
|
}
|
|
39
37
|
|
|
@@ -45,29 +43,26 @@ for (const path of [selectorPackage, prunerEntry]) {
|
|
|
45
43
|
|
|
46
44
|
class MemorySettings extends SettingsProvider {
|
|
47
45
|
writable = true
|
|
48
|
-
stored
|
|
46
|
+
stored = {}
|
|
49
47
|
|
|
50
48
|
load() {
|
|
51
49
|
return Promise.resolve(structuredClone(this.stored))
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
persist(namespace
|
|
52
|
+
persist(namespace, section) {
|
|
55
53
|
this.stored[namespace] = structuredClone(section)
|
|
56
54
|
return Promise.resolve()
|
|
57
55
|
}
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
class NativeSummaryAdapter extends LlmAdapter {
|
|
61
|
-
responses
|
|
62
|
-
contextWindow: number
|
|
63
|
-
|
|
64
|
-
constructor(responses: string[], contextWindow: number) {
|
|
59
|
+
constructor(responses, contextWindow) {
|
|
65
60
|
super()
|
|
66
61
|
this.responses = [...responses]
|
|
67
62
|
this.contextWindow = contextWindow
|
|
68
63
|
}
|
|
69
64
|
|
|
70
|
-
resolveModel(provider
|
|
65
|
+
resolveModel(provider, model) {
|
|
71
66
|
return Promise.resolve({
|
|
72
67
|
provider,
|
|
73
68
|
id: model,
|
|
@@ -76,23 +71,23 @@ class NativeSummaryAdapter extends LlmAdapter {
|
|
|
76
71
|
})
|
|
77
72
|
}
|
|
78
73
|
|
|
79
|
-
async * stream(options
|
|
74
|
+
async * stream(options) {
|
|
80
75
|
options.signal?.throwIfAborted()
|
|
81
76
|
const text = this.responses.shift()
|
|
82
77
|
if (text === undefined) throw new Error('NativeSummaryAdapter response script exhausted')
|
|
83
|
-
yield { type: 'block-start', index: 0, blockType: 'text' }
|
|
84
|
-
yield { type: 'text-delta', index: 0, text }
|
|
85
|
-
yield { type: 'block-end', index: 0, block: { type: 'text', text } }
|
|
86
|
-
yield { type: 'usage', usage: { inputTokens: 10, outputTokens: text.length } }
|
|
87
|
-
yield { type: 'finish', reason: { kind: 'stop' } }
|
|
78
|
+
yield { type: 'block-start', index: 0, blockType: 'text' }
|
|
79
|
+
yield { type: 'text-delta', index: 0, text }
|
|
80
|
+
yield { type: 'block-end', index: 0, block: { type: 'text', text } }
|
|
81
|
+
yield { type: 'usage', usage: { inputTokens: 10, outputTokens: text.length } }
|
|
82
|
+
yield { type: 'finish', reason: { kind: 'stop' } }
|
|
88
83
|
}
|
|
89
84
|
}
|
|
90
85
|
|
|
91
|
-
function captureAudit(ctx
|
|
92
|
-
const records
|
|
86
|
+
function captureAudit(ctx) {
|
|
87
|
+
const records = []
|
|
93
88
|
Object.defineProperty(ctx.logger, 'info', {
|
|
94
89
|
configurable: true,
|
|
95
|
-
value(message
|
|
90
|
+
value(message) {
|
|
96
91
|
const line = String(message)
|
|
97
92
|
if (line.startsWith(AUDIT_PREFIX)) records.push(JSON.parse(line.slice(AUDIT_PREFIX.length)))
|
|
98
93
|
return ctx.logger
|
|
@@ -101,15 +96,7 @@ function captureAudit(ctx: Context) {
|
|
|
101
96
|
return records
|
|
102
97
|
}
|
|
103
98
|
|
|
104
|
-
function appendToolTurn(
|
|
105
|
-
session: Record<string, unknown>,
|
|
106
|
-
turn: number,
|
|
107
|
-
text: string,
|
|
108
|
-
closeTurn: boolean,
|
|
109
|
-
userText?: string,
|
|
110
|
-
provider = 'deepseek',
|
|
111
|
-
model = MODEL,
|
|
112
|
-
) {
|
|
99
|
+
function appendToolTurn(session, turn, text, closeTurn, userText, provider = 'deepseek', model = MODEL) {
|
|
113
100
|
const callId = CallId(`packed-call-${String(turn)}`)
|
|
114
101
|
session.append('turn/start', { turn })
|
|
115
102
|
if (session.requestHeader() === undefined) {
|
|
@@ -126,6 +113,7 @@ function appendToolTurn(
|
|
|
126
113
|
}
|
|
127
114
|
session.append('step/start', { turn, step: 1 })
|
|
128
115
|
const assistant = session.append('assistant/message', {
|
|
116
|
+
stream: [],
|
|
129
117
|
turn,
|
|
130
118
|
step: 1,
|
|
131
119
|
message: createMessage({
|
|
@@ -146,16 +134,10 @@ function appendToolTurn(
|
|
|
146
134
|
}, { surfaceOp: 'append' })
|
|
147
135
|
session.append('step/end', { turn, step: 1 })
|
|
148
136
|
if (closeTurn) session.append('turn/end', { turn, reason: { kind: 'completed' } })
|
|
149
|
-
return { assistantSeq:
|
|
137
|
+
return { assistantSeq: assistant.seq, resultSeq: result.seq }
|
|
150
138
|
}
|
|
151
139
|
|
|
152
|
-
function appendToolBatchTurn(
|
|
153
|
-
session: Record<string, unknown>,
|
|
154
|
-
turn: number,
|
|
155
|
-
texts: string[],
|
|
156
|
-
closeTurn: boolean,
|
|
157
|
-
userText?: string,
|
|
158
|
-
) {
|
|
140
|
+
function appendToolBatchTurn(session, turn, texts, closeTurn, userText) {
|
|
159
141
|
const calls = texts.map((_, index) => ({
|
|
160
142
|
id: CallId(`packed-call-${String(turn)}-${String(index + 1)}`),
|
|
161
143
|
name: 'bash',
|
|
@@ -175,6 +157,7 @@ function appendToolBatchTurn(
|
|
|
175
157
|
}
|
|
176
158
|
session.append('step/start', { turn, step: 1 })
|
|
177
159
|
session.append('assistant/message', {
|
|
160
|
+
stream: [],
|
|
178
161
|
turn,
|
|
179
162
|
step: 1,
|
|
180
163
|
message: createMessage({
|
|
@@ -203,12 +186,14 @@ function appendToolBatchTurn(
|
|
|
203
186
|
if (closeTurn) session.append('turn/end', { turn, reason: { kind: 'completed' } })
|
|
204
187
|
}
|
|
205
188
|
|
|
206
|
-
function stubAgent(ctx
|
|
189
|
+
function stubAgent(ctx, session) {
|
|
207
190
|
return {
|
|
208
|
-
id:
|
|
191
|
+
id: session.id,
|
|
209
192
|
options: {},
|
|
210
193
|
session,
|
|
211
|
-
|
|
194
|
+
// 0.1.5 moved the durable Inbox behind the loop driver; the pruner only
|
|
195
|
+
// reads id/session, so an inert face is enough for the stub.
|
|
196
|
+
inbox: undefined,
|
|
212
197
|
status: 'idle',
|
|
213
198
|
ctx,
|
|
214
199
|
send() {},
|
|
@@ -216,7 +201,7 @@ function stubAgent(ctx: Context, session: Record<string, unknown>) {
|
|
|
216
201
|
steer: () => ({ outcome: Promise.resolve({ status: 'rejected' }) }),
|
|
217
202
|
inject() {},
|
|
218
203
|
cancel() {},
|
|
219
|
-
runMaintenance:
|
|
204
|
+
runMaintenance: task => task(new AbortController().signal),
|
|
220
205
|
whenIdle: () => Promise.resolve(),
|
|
221
206
|
}
|
|
222
207
|
}
|
|
@@ -225,11 +210,12 @@ const ctx = new Context()
|
|
|
225
210
|
try {
|
|
226
211
|
await ctx.plugin(MemorySettings).await()
|
|
227
212
|
await ctx.plugin(SelectorHost).await()
|
|
228
|
-
await ctx.plugin(LlmRuntime)
|
|
229
|
-
await ctx.plugin(SessionStore)
|
|
230
|
-
await ctx.plugin(SystemPrompt)
|
|
231
|
-
await ctx.plugin(ToolRuntime)
|
|
232
|
-
await ctx.plugin(
|
|
213
|
+
await ctx.plugin(LlmRuntime).await()
|
|
214
|
+
await ctx.plugin(SessionStore).await()
|
|
215
|
+
await ctx.plugin(SystemPrompt).await()
|
|
216
|
+
await ctx.plugin(ToolRuntime).await()
|
|
217
|
+
await ctx.plugin(SessionProjectionRegistry).await()
|
|
218
|
+
await ctx.plugin(TokenMeter).await()
|
|
233
219
|
const audit = captureAudit(ctx)
|
|
234
220
|
|
|
235
221
|
const policy = structuredClone(Runtime.DEFAULT_CUSTOM_COMPRESSION_POLICY)
|
|
@@ -238,6 +224,9 @@ try {
|
|
|
238
224
|
policy.aggregate = { enabled: true, trigger: 1_000, target: 400 }
|
|
239
225
|
policy.history = {
|
|
240
226
|
enabled: true,
|
|
227
|
+
// Strict required-reclaim: the batch must pull tool tokens back under the
|
|
228
|
+
// trigger, so it sits above the large protected tail and placeholder
|
|
229
|
+
// residue while remaining below the session's tool-result total.
|
|
241
230
|
trigger: 7_800,
|
|
242
231
|
keepRecentToolCalls: 0,
|
|
243
232
|
keepRecentTokens: 1,
|
|
@@ -245,7 +234,7 @@ try {
|
|
|
245
234
|
}
|
|
246
235
|
policy.prefixPolicy = 'pressure-break'
|
|
247
236
|
policy.tailTrim = { enabled: true, trigger: 8 }
|
|
248
|
-
const namespace =
|
|
237
|
+
const namespace = Runtime.CONTEXT_COMPRESSION_SETTINGS_NAMESPACE
|
|
249
238
|
await ctx.settings.update(namespace, { profile: 'custom', custom: policy })
|
|
250
239
|
await ctx.plugin(Runtime.default, {
|
|
251
240
|
profile: 'native',
|
|
@@ -255,12 +244,12 @@ try {
|
|
|
255
244
|
tailChars: 8,
|
|
256
245
|
}).await()
|
|
257
246
|
|
|
258
|
-
const session =
|
|
259
|
-
appendToolTurn(session
|
|
247
|
+
const session = Session.create(SessionId('packed-components-full-pipeline'))
|
|
248
|
+
appendToolTurn(session, 1, 'packed Fresh '.repeat(600), false, 'run packed Fresh')
|
|
260
249
|
ctx.toolResultPruner.pruneSession(session, { stage: 'fresh', freshTurn: 1, freshStep: 1 })
|
|
261
250
|
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
|
262
251
|
appendToolBatchTurn(
|
|
263
|
-
session
|
|
252
|
+
session,
|
|
264
253
|
2,
|
|
265
254
|
Array.from({ length: 30 }, (_, index) => `packed Aggregate ${String(index)} `.repeat(20)),
|
|
266
255
|
false,
|
|
@@ -268,18 +257,14 @@ try {
|
|
|
268
257
|
)
|
|
269
258
|
ctx.toolResultPruner.pruneSession(session, { stage: 'fresh', freshTurn: 2, freshStep: 1 })
|
|
270
259
|
session.append('turn/end', { turn: 2, reason: { kind: 'completed' } })
|
|
271
|
-
appendToolTurn(session
|
|
272
|
-
appendToolTurn(session
|
|
260
|
+
appendToolTurn(session, 3, 'packed History '.repeat(600), true, 'run packed History')
|
|
261
|
+
appendToolTurn(session, 4, 'packed recent protected working-set context '.repeat(1000), true, 'retain packed recent context')
|
|
273
262
|
session.append('turn/start', { turn: 5 })
|
|
274
263
|
ctx.toolResultPruner.pruneSession(session, { stage: 'pressure' })
|
|
275
264
|
|
|
276
265
|
const tail = audit.find(record => record.kind === 'rewrite' && record.component === 'tail-trim')
|
|
277
|
-
if (tail === undefined) {
|
|
278
|
-
console.error('TAILTRIM_DIAG', JSON.stringify(audit.filter(record =>
|
|
279
|
-
record.component === 'tail-trim' || record.kind === 'rewrite'), null, 2))
|
|
280
|
-
}
|
|
281
266
|
assert(tail !== undefined, 'TailTrim did not commit from installed Runtime')
|
|
282
|
-
const ref = `session://${String(
|
|
267
|
+
const ref = `session://${String(session.id)}/tailtrim/${String(tail.manifestSeq)}`
|
|
283
268
|
const recovered = await ctx.tools.execute({
|
|
284
269
|
name: 'context_compression_retrieve',
|
|
285
270
|
arguments: { ref, max_lines: 20 },
|
|
@@ -289,12 +274,12 @@ try {
|
|
|
289
274
|
})
|
|
290
275
|
assert(recovered.isError === false, 'installed TailTrim recovery returned an error')
|
|
291
276
|
const recoveredText = recovered.content
|
|
292
|
-
.map(
|
|
277
|
+
.map(block => block.type === 'text' ? block.text : '')
|
|
293
278
|
.join('\n')
|
|
294
279
|
assert(recoveredText.includes('kind: tailtrim-group'),
|
|
295
280
|
'installed TailTrim recovery did not return a TailTrim group')
|
|
296
|
-
const tailSourcePayloads =
|
|
297
|
-
.map(seq => (
|
|
281
|
+
const tailSourcePayloads = tail.sourceSeqs
|
|
282
|
+
.map(seq => session.snapshotEvents()[seq])
|
|
298
283
|
.filter(event => event !== undefined)
|
|
299
284
|
.map(event => JSON.stringify(event))
|
|
300
285
|
const recoveredMarker = [
|
|
@@ -322,9 +307,9 @@ try {
|
|
|
322
307
|
capacityPolicy.tailTrim.enabled = false
|
|
323
308
|
await ctx.settings.update(namespace, { profile: 'custom', custom: capacityPolicy })
|
|
324
309
|
|
|
325
|
-
const inactiveCapacity =
|
|
326
|
-
appendToolTurn(inactiveCapacity
|
|
327
|
-
appendToolTurn(inactiveCapacity
|
|
310
|
+
const inactiveCapacity = Session.create(SessionId('packed-history-capacity-inactive'))
|
|
311
|
+
appendToolTurn(inactiveCapacity, 1, 'packed capacity inactive '.repeat(600), true)
|
|
312
|
+
appendToolTurn(inactiveCapacity, 2, 'packed newest protected result', true)
|
|
328
313
|
inactiveCapacity.append('request/context', {
|
|
329
314
|
provider: 'deepseek',
|
|
330
315
|
model: MODEL,
|
|
@@ -333,16 +318,16 @@ try {
|
|
|
333
318
|
inactiveCapacity.append('turn/start', { turn: 3 })
|
|
334
319
|
ctx.toolResultPruner.pruneSession(inactiveCapacity, { stage: 'pressure' })
|
|
335
320
|
const inactiveCapacityAudit = audit.find(record => record.kind === 'component-evaluation'
|
|
336
|
-
&& record.sessionId === String(
|
|
321
|
+
&& record.sessionId === String(inactiveCapacity.id)
|
|
337
322
|
&& record.component === 'history')
|
|
338
323
|
assert(inactiveCapacityAudit?.status === 'skipped'
|
|
339
324
|
&& inactiveCapacityAudit.reason === 'below-micro-deadline'
|
|
340
325
|
&& inactiveCapacityAudit.historyMode === 'capacity-pressure',
|
|
341
326
|
'installed History did not prove the inactive capacity-pressure gate')
|
|
342
327
|
|
|
343
|
-
const activeCapacity =
|
|
344
|
-
appendToolTurn(activeCapacity
|
|
345
|
-
appendToolTurn(activeCapacity
|
|
328
|
+
const activeCapacity = Session.create(SessionId('packed-history-capacity-active'))
|
|
329
|
+
appendToolTurn(activeCapacity, 1, 'packed capacity active '.repeat(600), true)
|
|
330
|
+
appendToolTurn(activeCapacity, 2, 'packed newest protected result', true)
|
|
346
331
|
const activeCapacityWindow = Math.floor(ctx.tokenMeter.measure(activeCapacity).totalTokens / 0.75)
|
|
347
332
|
activeCapacity.append('request/context', {
|
|
348
333
|
provider: 'deepseek',
|
|
@@ -352,21 +337,21 @@ try {
|
|
|
352
337
|
activeCapacity.append('turn/start', { turn: 3 })
|
|
353
338
|
ctx.toolResultPruner.pruneSession(activeCapacity, { stage: 'pressure' })
|
|
354
339
|
const capacityRewrite = audit.find(record => record.kind === 'rewrite'
|
|
355
|
-
&& record.sessionId === String(
|
|
340
|
+
&& record.sessionId === String(activeCapacity.id)
|
|
356
341
|
&& record.component === 'history')
|
|
357
342
|
assert(capacityRewrite?.historyMode === 'capacity-pressure'
|
|
358
343
|
&& capacityRewrite.stage === 'pressure'
|
|
359
344
|
&& typeof capacityRewrite.reducer === 'string'
|
|
360
345
|
&& Number.isSafeInteger(capacityRewrite.tokensBefore)
|
|
361
346
|
&& Number.isSafeInteger(capacityRewrite.tokensAfter)
|
|
362
|
-
&&
|
|
347
|
+
&& capacityRewrite.tokensBefore > capacityRewrite.tokensAfter,
|
|
363
348
|
'installed History did not commit exact capacity-pressure evidence')
|
|
364
349
|
|
|
365
350
|
const boundaryCtx = new Context()
|
|
366
351
|
try {
|
|
367
352
|
await mountAgentLoopTestDependencies(boundaryCtx)
|
|
368
|
-
await boundaryCtx.plugin(AgentLoop, { agents: [] })
|
|
369
|
-
await boundaryCtx.plugin(TokenMeter)
|
|
353
|
+
await boundaryCtx.plugin(AgentLoop, { agents: [] }).await()
|
|
354
|
+
await boundaryCtx.plugin(TokenMeter).await()
|
|
370
355
|
const boundaryAudit = captureAudit(boundaryCtx)
|
|
371
356
|
boundaryCtx.llm.registerAdapter(
|
|
372
357
|
['deepseek'],
|
|
@@ -383,17 +368,19 @@ try {
|
|
|
383
368
|
historyKeepRecentTokens: 1,
|
|
384
369
|
historyMinReclaimTokens: 1,
|
|
385
370
|
}).await()
|
|
386
|
-
const boundaryAgent = boundaryCtx.agentLoop.create(
|
|
371
|
+
const boundaryAgent = await boundaryCtx.agentLoop.create(
|
|
387
372
|
SessionId('packed-history-capacity-request-boundary'),
|
|
388
373
|
{ provider: 'deepseek', model: MODEL },
|
|
389
374
|
)
|
|
390
375
|
const boundarySession = boundaryAgent.session
|
|
391
|
-
appendToolTurn(boundarySession
|
|
392
|
-
appendToolTurn(boundarySession
|
|
376
|
+
appendToolTurn(boundarySession, 1, 'packed old capacity-pressure evidence '.repeat(600), true)
|
|
377
|
+
appendToolTurn(boundarySession, 2, 'packed newest protected result', true)
|
|
393
378
|
const boundaryTotal = boundaryCtx.tokenMeter.measure(boundarySession).totalTokens
|
|
394
379
|
boundarySession.append('request/context', {
|
|
395
380
|
provider: 'deepseek',
|
|
396
381
|
model: MODEL,
|
|
382
|
+
// Just under the frozen 80% deadline D = 0.7 * window so the gate is
|
|
383
|
+
// active while the strict reclaim target stays reachable.
|
|
397
384
|
contextWindow: Math.floor(boundaryTotal / 0.72),
|
|
398
385
|
})
|
|
399
386
|
boundaryAgent.followup(createUserMessage({
|
|
@@ -402,21 +389,24 @@ try {
|
|
|
402
389
|
}))
|
|
403
390
|
await boundaryAgent.whenIdle()
|
|
404
391
|
const boundaryRewrite = boundaryAudit.find(record => record.kind === 'rewrite'
|
|
405
|
-
&& record.sessionId === String(
|
|
392
|
+
&& record.sessionId === String(boundarySession.id)
|
|
406
393
|
&& record.component === 'history')
|
|
407
394
|
assert(boundaryRewrite?.stage === 'pressure'
|
|
408
395
|
&& boundaryRewrite.historyMode === 'capacity-pressure'
|
|
409
|
-
&&
|
|
396
|
+
&& boundaryRewrite.tokensBefore > boundaryRewrite.tokensAfter,
|
|
410
397
|
'installed Cache Strict did not schedule History from the real request boundary')
|
|
411
398
|
} finally {
|
|
412
399
|
await boundaryCtx.fiber.dispose()
|
|
413
400
|
}
|
|
414
401
|
|
|
415
|
-
// Packed vision-session gate
|
|
402
|
+
// Packed vision-session gate: the installed runtime must actually resolve
|
|
403
|
+
// the deepseek-v4-flash-vision-exp route, count text exactly with the
|
|
404
|
+
// separately bundled vision tokenizer, estimate image surfaces, and keep
|
|
405
|
+
// image-bearing results outside exact rewrite proofs.
|
|
416
406
|
const VISION_MODEL = 'deepseek-v4-flash-vision-exp'
|
|
417
407
|
const VISION_TOKENIZER_REPOSITORY = 'deepseek-ai/DeepSeek-V4-Flash-Vision-Exp'
|
|
418
408
|
const VISION_TOKENIZER_REVISION = '6821d6ad3681a4b137b066b76094fa82ebd0a380'
|
|
419
|
-
const imageBlock = (width
|
|
409
|
+
const imageBlock = (width, height) => ({
|
|
420
410
|
type: 'image',
|
|
421
411
|
attachment: {
|
|
422
412
|
attachmentId: `packed-image-${String(width)}x${String(height)}`,
|
|
@@ -430,11 +420,12 @@ try {
|
|
|
430
420
|
try {
|
|
431
421
|
await visionCtx.plugin(MemorySettings).await()
|
|
432
422
|
await visionCtx.plugin(SelectorHost).await()
|
|
433
|
-
await visionCtx.plugin(LlmRuntime)
|
|
434
|
-
await visionCtx.plugin(SessionStore)
|
|
435
|
-
await visionCtx.plugin(SystemPrompt)
|
|
436
|
-
await visionCtx.plugin(ToolRuntime)
|
|
437
|
-
await visionCtx.plugin(
|
|
423
|
+
await visionCtx.plugin(LlmRuntime).await()
|
|
424
|
+
await visionCtx.plugin(SessionStore).await()
|
|
425
|
+
await visionCtx.plugin(SystemPrompt).await()
|
|
426
|
+
await visionCtx.plugin(ToolRuntime).await()
|
|
427
|
+
await visionCtx.plugin(SessionProjectionRegistry).await()
|
|
428
|
+
await visionCtx.plugin(TokenMeter).await()
|
|
438
429
|
const visionAudit = captureAudit(visionCtx)
|
|
439
430
|
await visionCtx.plugin(Runtime.default, {
|
|
440
431
|
profile: 'balanced',
|
|
@@ -448,8 +439,9 @@ try {
|
|
|
448
439
|
historyMinReclaimTokens: 1,
|
|
449
440
|
}).await()
|
|
450
441
|
|
|
451
|
-
// (a) A user image plus pure-text tool results
|
|
452
|
-
|
|
442
|
+
// (a) A user image plus pure-text tool results: the text rewrites must be
|
|
443
|
+
// exact and carry the vision tokenizer's repository/revision.
|
|
444
|
+
const visionText = Session.create(SessionId('packed-vision-text-session'))
|
|
453
445
|
visionText.append('turn/start', { turn: 1 })
|
|
454
446
|
visionText.append('request/header', {
|
|
455
447
|
reason: 'initial',
|
|
@@ -484,15 +476,15 @@ try {
|
|
|
484
476
|
}, { surfaceOp: 'append' })
|
|
485
477
|
visionText.append('step/end', { turn: 1, step: 1 })
|
|
486
478
|
visionText.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
|
487
|
-
appendToolTurn(visionText
|
|
488
|
-
appendToolTurn(visionText
|
|
479
|
+
appendToolTurn(visionText, 2, 'packed vision history evidence '.repeat(300), true, undefined, 'deepseek', VISION_MODEL)
|
|
480
|
+
appendToolTurn(visionText, 3, 'packed recent protected result', true, undefined, 'deepseek', VISION_MODEL)
|
|
489
481
|
visionText.append('turn/start', { turn: 4 })
|
|
490
482
|
const visionFresh = visionCtx.toolResultPruner.pruneSession(visionText, { stage: 'fresh', freshTurn: 1, freshStep: 1 })
|
|
491
483
|
const visionPressure = visionCtx.toolResultPruner.pruneSession(visionText, { stage: 'pressure' })
|
|
492
484
|
assert(visionFresh.pruned.length === 1 && visionPressure.pruned.length >= 1,
|
|
493
485
|
'packed vision text session did not rewrite pure-text results')
|
|
494
486
|
const visionRewrites = visionAudit
|
|
495
|
-
.filter(record => record.kind === 'rewrite' && record.sessionId === String(
|
|
487
|
+
.filter(record => record.kind === 'rewrite' && record.sessionId === String(visionText.id))
|
|
496
488
|
assert(visionRewrites.length >= 2, 'packed vision text session lacks aggregate and history rewrites')
|
|
497
489
|
for (const record of visionRewrites) {
|
|
498
490
|
assert(record.tokenizerId === VISION_TOKENIZER_REPOSITORY
|
|
@@ -503,8 +495,10 @@ try {
|
|
|
503
495
|
&& visionRewrites.some(record => record.component === 'history'),
|
|
504
496
|
'packed vision text session lacks an aggregate/history component rewrite')
|
|
505
497
|
|
|
506
|
-
// (b) An image-bearing tool result
|
|
507
|
-
|
|
498
|
+
// (b) An image-bearing tool result: its surface may be estimated, but every
|
|
499
|
+
// rewrite path still requires exact counts. The original event is untouched
|
|
500
|
+
// and the audits say why no rewrite was authorized.
|
|
501
|
+
const visionImage = Session.create(SessionId('packed-vision-image-tool-result'))
|
|
508
502
|
visionImage.append('turn/start', { turn: 1 })
|
|
509
503
|
visionImage.append('request/header', {
|
|
510
504
|
reason: 'initial',
|
|
@@ -546,7 +540,7 @@ try {
|
|
|
546
540
|
'packed vision image-bearing result was rewritten lossily')
|
|
547
541
|
const imageMeasurement = Runtime.measureForCompaction(visionCtx, visionImage)
|
|
548
542
|
const estimatedImageCount = imageMeasurement.measuredNodes
|
|
549
|
-
.find(
|
|
543
|
+
.find(node => node.seq === estimatedUserImage.seq)?.count
|
|
550
544
|
assert(estimatedImageCount?.kind === 'tokenizer-estimate',
|
|
551
545
|
`packed vision image surface is ${String(estimatedImageCount?.kind)}, expected tokenizer-estimate`)
|
|
552
546
|
assert(estimatedImageCount.tokens === 340,
|
|
@@ -559,14 +553,17 @@ try {
|
|
|
559
553
|
`packed vision image estimator revision is ${String(estimatedImageCount.estimatorRevision)}`)
|
|
560
554
|
assert(imageMeasurement.currentSurface.kind === 'tokenizer-estimate',
|
|
561
555
|
`packed vision current surface is ${imageMeasurement.currentSurface.kind}, expected tokenizer-estimate`)
|
|
562
|
-
|
|
563
|
-
|
|
556
|
+
// Tool-result events carry one envelope block whose nested content holds
|
|
557
|
+
// the original [text, image] blocks; the durable attachment reference must
|
|
558
|
+
// still be there verbatim.
|
|
559
|
+
const originalImage = visionImage.snapshotEvents()[imageResult.seq]
|
|
560
|
+
assert(originalImage?.type === 'tool/result'
|
|
564
561
|
&& JSON.stringify(originalImage).includes('packed-image-800x600')
|
|
565
562
|
&& JSON.stringify(originalImage).includes('"type":"image"'),
|
|
566
563
|
'packed vision image-bearing result is no longer intact on the surface')
|
|
567
564
|
for (const component of ['fresh', 'history']) {
|
|
568
565
|
assert(visionAudit.some(record => record.kind === 'component-evaluation'
|
|
569
|
-
&& record.sessionId === String(
|
|
566
|
+
&& record.sessionId === String(visionImage.id)
|
|
570
567
|
&& record.component === component
|
|
571
568
|
&& record.status === 'skipped'
|
|
572
569
|
&& record.reason === 'exact-tokenizer-unavailable'),
|
|
@@ -604,12 +601,12 @@ try {
|
|
|
604
601
|
() => Promise.resolve({ kind: 'enter', messages: [] }),
|
|
605
602
|
)
|
|
606
603
|
assert(decision.kind === 'enter', 'Native pre-step did not return enter')
|
|
607
|
-
assert(session.
|
|
604
|
+
assert(session.snapshotEvents().some(event => event.type === 'compaction/summary'),
|
|
608
605
|
'official BasicCompactionEngine did not commit Native summary')
|
|
609
606
|
|
|
610
607
|
await ctx.settings.update(namespace, { profile: 'native' })
|
|
611
|
-
const nativeSession =
|
|
612
|
-
appendToolTurn(nativeSession
|
|
608
|
+
const nativeSession = Session.create(SessionId('packed-native-tool-result'))
|
|
609
|
+
appendToolTurn(nativeSession, 1, 'packed native tool result '.repeat(800), false)
|
|
613
610
|
const nativeResult = ctx.toolResultPruner.pruneSession(nativeSession, { stage: 'pressure' })
|
|
614
611
|
assert(nativeResult.pruned.length === 1, 'installed Native tool-result profile did not rewrite')
|
|
615
612
|
|
|
@@ -619,8 +616,8 @@ try {
|
|
|
619
616
|
firstFrozen.history.enabled = false
|
|
620
617
|
firstFrozen.tailTrim.enabled = false
|
|
621
618
|
await ctx.settings.update(namespace, { profile: 'custom', custom: firstFrozen })
|
|
622
|
-
const frozenA =
|
|
623
|
-
appendToolTurn(frozenA
|
|
619
|
+
const frozenA = Session.create(SessionId('packed-policy-freeze-a'))
|
|
620
|
+
appendToolTurn(frozenA, 1, 'packed frozen first '.repeat(800), false)
|
|
624
621
|
assert(ctx.toolResultPruner.pruneSession(frozenA, {
|
|
625
622
|
stage: 'fresh', freshTurn: 1, freshStep: 1,
|
|
626
623
|
}).pruned.length === 1, 'first frozen policy did not run Fresh')
|
|
@@ -629,23 +626,23 @@ try {
|
|
|
629
626
|
edited.history.trigger += 123
|
|
630
627
|
await ctx.settings.update(namespace, { custom: edited })
|
|
631
628
|
frozenA.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
|
632
|
-
appendToolTurn(frozenA
|
|
629
|
+
appendToolTurn(frozenA, 2, 'packed frozen second '.repeat(800), false)
|
|
633
630
|
assert(ctx.toolResultPruner.pruneSession(frozenA, {
|
|
634
631
|
stage: 'fresh', freshTurn: 2, freshStep: 1,
|
|
635
632
|
}).pruned.length === 1, 'observed Session did not retain its complete frozen policy')
|
|
636
|
-
const frozenB =
|
|
637
|
-
appendToolTurn(frozenB
|
|
633
|
+
const frozenB = Session.create(SessionId('packed-policy-freeze-b'))
|
|
634
|
+
appendToolTurn(frozenB, 1, 'packed new disabled Fresh '.repeat(800), false)
|
|
638
635
|
assert(ctx.toolResultPruner.pruneSession(frozenB, {
|
|
639
636
|
stage: 'fresh', freshTurn: 1, freshStep: 1,
|
|
640
637
|
}).pruned.length === 0, 'new Session did not adopt the edited complete policy')
|
|
641
638
|
|
|
642
639
|
const rewrites = audit.filter(record => record.kind === 'rewrite')
|
|
643
|
-
const firstIndex =
|
|
644
|
-
&& record.sessionId === String(
|
|
640
|
+
const firstIndex = component => audit.findIndex(record => record.kind === 'rewrite'
|
|
641
|
+
&& record.sessionId === String(session.id) && record.component === component)
|
|
645
642
|
for (const component of ['fresh', 'aggregate', 'history', 'tail-trim']) {
|
|
646
643
|
assert(firstIndex(component) >= 0, `installed pipeline lacks ${component} rewrite`)
|
|
647
644
|
}
|
|
648
|
-
const routineHistory = rewrites.find(record => record.sessionId === String(
|
|
645
|
+
const routineHistory = rewrites.find(record => record.sessionId === String(session.id)
|
|
649
646
|
&& record.component === 'history')
|
|
650
647
|
assert(routineHistory?.historyMode === 'routine',
|
|
651
648
|
'installed full pipeline did not identify routine History')
|
|
@@ -653,23 +650,23 @@ try {
|
|
|
653
650
|
&& firstIndex('aggregate') < firstIndex('history')
|
|
654
651
|
&& firstIndex('history') < firstIndex('tail-trim'),
|
|
655
652
|
'installed component rewrite order is wrong')
|
|
656
|
-
const pipelineRewrites = rewrites.filter(record => record.sessionId === String(
|
|
653
|
+
const pipelineRewrites = rewrites.filter(record => record.sessionId === String(session.id))
|
|
657
654
|
assert(pipelineRewrites.every(record => Number.isSafeInteger(record.tokensBefore)
|
|
658
|
-
&& Number.isSafeInteger(record.tokensAfter) &&
|
|
655
|
+
&& Number.isSafeInteger(record.tokensAfter) && record.tokensBefore > record.tokensAfter),
|
|
659
656
|
'installed rewrite lacks exact decreasing token evidence')
|
|
660
657
|
assert(audit.some(record => record.kind === 'native-auto-compact'
|
|
661
|
-
&& record.sessionId === String(
|
|
658
|
+
&& record.sessionId === String(session.id)),
|
|
662
659
|
'installed Runtime did not audit official Native summary')
|
|
663
660
|
assert(rewrites.some(record => record.component === 'native-tool-result'
|
|
664
|
-
&& record.sessionId === String(
|
|
661
|
+
&& record.sessionId === String(nativeSession.id)),
|
|
665
662
|
'installed Runtime lacks Native tool-result audit')
|
|
666
663
|
const frozenAuditA = audit.find(record => record.kind === 'policy-frozen'
|
|
667
|
-
&& record.sessionId === String(
|
|
664
|
+
&& record.sessionId === String(frozenA.id))
|
|
668
665
|
const frozenAuditB = audit.find(record => record.kind === 'policy-frozen'
|
|
669
|
-
&& record.sessionId === String(
|
|
670
|
-
assert(
|
|
666
|
+
&& record.sessionId === String(frozenB.id))
|
|
667
|
+
assert(frozenAuditA?.settings?.custom?.fresh?.enabled === true,
|
|
671
668
|
'first installed policy-frozen record lacks original complete settings')
|
|
672
|
-
assert(
|
|
669
|
+
assert(frozenAuditB?.settings?.custom?.fresh?.enabled === false,
|
|
673
670
|
'new installed policy-frozen record lacks edited complete settings')
|
|
674
671
|
|
|
675
672
|
console.info(`PACKED_COMPONENTS_E2E ${JSON.stringify({
|
|
@@ -706,7 +703,7 @@ try {
|
|
|
706
703
|
tokensAfter: record.tokensAfter,
|
|
707
704
|
}
|
|
708
705
|
}),
|
|
709
|
-
customEvents:
|
|
706
|
+
customEvents: session.snapshotEvents().some(event => event.type === 'compaction/group-trim') ? 'present' : 'absent',
|
|
710
707
|
})}`)
|
|
711
708
|
} finally {
|
|
712
709
|
await ctx.fiber.dispose()
|