dsh-context-compression-improved 0.2.0 → 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.
Files changed (80) hide show
  1. package/.gitattributes +1 -1
  2. package/.github/workflows/ci.yml +1 -7
  3. package/CHANGELOG.ja.md +18 -21
  4. package/CHANGELOG.ko.md +17 -21
  5. package/CHANGELOG.md +16 -17
  6. package/CHANGELOG.zh.md +15 -18
  7. package/CONTRIBUTING.md +1 -1
  8. package/README.ja.md +1 -20
  9. package/README.ko.md +12 -30
  10. package/README.md +1 -19
  11. package/README.zh.md +5 -21
  12. package/THIRD_PARTY_NOTICES.md +2 -2
  13. package/docs/compat-0.1.5-report.md +77 -0
  14. package/docs/installation.ja.md +6 -4
  15. package/docs/installation.ko.md +9 -7
  16. package/docs/installation.md +3 -1
  17. package/docs/installation.zh.md +3 -2
  18. package/docs/repair-log.md +54 -33
  19. package/eslint.config.js +2 -2
  20. package/package.json +56 -36
  21. package/packages/selector/README.md +7 -12
  22. package/packages/selector/README.zh.md +6 -13
  23. package/packages/selector/THIRD_PARTY_NOTICES.md +2 -8
  24. package/packages/selector/cordis.patch.yml +7 -5
  25. package/packages/selector/dsh.plugin.json +2 -2
  26. package/packages/selector/lib/client.d.ts +31 -5
  27. package/packages/selector/lib/client.js +100 -82
  28. package/packages/selector/lib/index.d.ts +8 -20
  29. package/packages/selector/lib/index.js +9 -14
  30. package/packages/selector/lib/invariant.js +4 -4
  31. package/packages/selector/lib/pruner.d.ts +13 -4
  32. package/packages/selector/lib/pruner.js +91 -46
  33. package/packages/selector/lib/tail-trim.js +13 -4
  34. package/packages/selector/package.json +23 -23
  35. package/packages/selector/screenshots.json +1 -4
  36. package/packages/selector/src/client/CompressionProfileControls.tsx +5 -8
  37. package/packages/selector/src/client/CompressionProfileSelector.tsx +9 -5
  38. package/packages/selector/src/client/EstimatorControls.tsx +28 -32
  39. package/packages/selector/src/client/index.ts +101 -63
  40. package/packages/selector/src/client/locales.ts +0 -2
  41. package/packages/selector/src/client/preset-options.ts +49 -35
  42. package/packages/selector/src/client/settings-section.tsx +4 -6
  43. package/packages/selector/src/deepseek-v4-tokenizer.ts +0 -5
  44. package/packages/selector/src/index.ts +41 -46
  45. package/packages/selector/src/invariant.ts +5 -5
  46. package/packages/selector/src/pruner.ts +67 -36
  47. package/packages/selector/src/runtime/measurement.ts +6 -2
  48. package/packages/selector/src/runtime/session-events.ts +14 -1
  49. package/packages/selector/src/runtime/tail-trim.ts +4 -4
  50. package/packages/selector/src/runtime/tokenpilot/estimator.ts +34 -3
  51. package/packages/selector/src/runtime/types.ts +1 -1
  52. package/packages/selector/tests/auto-compact.client.spec.tsx +0 -13
  53. package/packages/selector/tests/built/client-artifact.spec.ts +5 -1
  54. package/packages/selector/tests/code-skeleton.client.spec.ts +2 -1
  55. package/packages/selector/tests/custom-contract.client.spec.ts +2 -1
  56. package/packages/selector/tests/estimator-channel.client.spec.tsx +16 -11
  57. package/packages/selector/tests/estimator-route-registration.host.spec.ts +12 -0
  58. package/packages/selector/tests/host-preset-overlay.host.spec.ts +6 -4
  59. package/packages/selector/tests/preset-options-write.client.spec.ts +87 -79
  60. package/packages/selector/tests/preset-overlay-loader.e2e.host.spec.ts +17 -12
  61. package/packages/selector/tests/preset-overlay.host.spec.ts +5 -2
  62. package/packages/selector/tests/profiles.client.spec.tsx +3 -3
  63. package/packages/selector/tests/public/package-contract.client.spec.ts +27 -3
  64. package/packages/selector/tests/runtime/public/public-runtime.spec.ts +103 -81
  65. package/packages/selector/tests/runtime/session-events.spec.ts +1 -1
  66. package/packages/selector/tests/settings-seat.client.spec.ts +86 -0
  67. package/packages/selector/tests/standing-generation.host.spec.ts +88 -71
  68. package/packages/selector/tests/subagent-cache-reuse.host.spec.ts +24 -21
  69. package/pnpm-workspace.yaml +52 -5
  70. package/scripts/capture-profile-baseline.mjs +42 -0
  71. package/scripts/generate-tokenizer-fixtures.py +5 -5
  72. package/scripts/generate-vision-fixtures.py +2 -2
  73. package/scripts/{packed-components-smoke.ts → packed-components-smoke.mjs} +116 -119
  74. package/scripts/{packed-install-e2e.ts → packed-install-e2e.mjs} +188 -160
  75. package/scripts/{verify-release.ts → verify-release.mjs} +70 -94
  76. package/tests/TEST_INVENTORY.md +6 -7
  77. package/scripts/capture-profile-baseline.ts +0 -80
  78. package/tsconfig.scripts.json +0 -13
  79. /package/{packages/selector/assets/screenshots → docs/assets}/context-compression-selector-profiles.jpg +0 -0
  80. /package/{packages/selector/assets/screenshots → docs/assets}/context-compression-selector-settings.png +0 -0
@@ -15,11 +15,11 @@ import CommandRuntime from '@deepseek-ai/dsh-commands'
15
15
  import LlmRuntime from '@deepseek-ai/dsh-llm'
16
16
  import SessionStore from '@deepseek-ai/dsh-session'
17
17
  import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
18
+ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
18
19
  import TokenMeter from '@deepseek-ai/dsh-token-meter'
19
20
  import ToolRuntime from '@deepseek-ai/dsh-tools'
20
21
  import {
21
22
  SettingsProvider,
22
- settingsNamespace,
23
23
  type SettingsNamespace,
24
24
  } from '@deepseek-ai/dsh-settings'
25
25
  import { pathToFileURL } from 'node:url'
@@ -37,6 +37,9 @@ import type {
37
37
  PresetOverlayMetadataIo,
38
38
  } from '../src/preset-overlay.ts'
39
39
 
40
+ // 0.1.5 removed the settingsNamespace() wrapper; namespaces are validated at runtime.
41
+ const nsBrand = (value: string): SettingsNamespace => value as unknown as SettingsNamespace
42
+
40
43
  let root: string | undefined
41
44
  let ctx: Context | undefined
42
45
 
@@ -176,22 +179,24 @@ async function assertStandingSwitch(
176
179
  sourceRoot = rootPath
177
180
  ctx = new Context()
178
181
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
179
- await ctx.plugin(Loader)
182
+ await ctx.plugin(Loader).await()
180
183
  ctx.loader.builtins.include = Include
181
184
  ctx.loader.builtins.group = Group
182
- await ctx.plugin(LlmRuntime)
183
- await ctx.plugin(SessionStore)
184
- await ctx.plugin(SystemPrompt, { persona: '' })
185
- await ctx.plugin(ToolRuntime)
186
- await ctx.plugin(AgentRegistry)
187
- await ctx.plugin(AgentLoop, { agents: [] })
188
- await ctx.plugin(CommandRuntime)
189
- await ctx.plugin(TokenMeter)
185
+ await ctx.plugin(LlmRuntime).await()
186
+ await ctx.plugin(SessionStore).await()
187
+ await ctx.plugin(SystemPrompt).await()
188
+ await ctx.plugin(ToolRuntime).await()
189
+ await ctx.plugin(AgentRegistry).await()
190
+ await ctx.plugin(AgentLoop).await()
191
+ await ctx.plugin(CommandRuntime).await()
192
+ await ctx.plugin(SessionProjectionRegistry).await()
193
+ await ctx.plugin(TokenMeter).await()
190
194
  await ctx.plugin(MemorySettings).await()
191
195
  await ctx.plugin({ apply }).await()
192
196
  await ctx.plugin(AgentPresets, {
193
197
  default: 'standard',
194
198
  roots: [{ path: rootPath, trust: 'system' }],
199
+ includeShippedRoot: false,
195
200
  includeUserRoot: false,
196
201
  }).await()
197
202
  const act = await arrange()
@@ -263,20 +268,22 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
263
268
 
264
269
  ctx = new Context()
265
270
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
266
- await ctx.plugin(Loader)
271
+ await ctx.plugin(Loader).await()
267
272
  ctx.loader.builtins.include = Include
268
273
  ctx.loader.builtins.group = Group
269
- await ctx.plugin(LlmRuntime)
270
- await ctx.plugin(SessionStore)
271
- await ctx.plugin(SystemPrompt, { persona: '' })
272
- await ctx.plugin(ToolRuntime)
273
- await ctx.plugin(AgentRegistry)
274
- await ctx.plugin(AgentLoop, { agents: [] })
275
- await ctx.plugin(CommandRuntime)
276
- await ctx.plugin(TokenMeter)
274
+ await ctx.plugin(LlmRuntime).await()
275
+ await ctx.plugin(SessionStore).await()
276
+ await ctx.plugin(SystemPrompt).await()
277
+ await ctx.plugin(ToolRuntime).await()
278
+ await ctx.plugin(AgentRegistry).await()
279
+ await ctx.plugin(AgentLoop).await()
280
+ await ctx.plugin(CommandRuntime).await()
281
+ await ctx.plugin(SessionProjectionRegistry).await()
282
+ await ctx.plugin(TokenMeter).await()
277
283
  await ctx.plugin(AgentPresets, {
278
284
  default: 'standard',
279
285
  roots: [{ path: rootPath, trust: 'system' }],
286
+ includeShippedRoot: false,
280
287
  includeUserRoot: false,
281
288
  }).await()
282
289
 
@@ -333,25 +340,27 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
333
340
  sourceRoot = rootPath
334
341
  ctx = new Context()
335
342
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
336
- await ctx.plugin(Loader)
343
+ await ctx.plugin(Loader).await()
337
344
  ctx.loader.builtins.include = Include
338
345
  ctx.loader.builtins.group = Group
339
- await ctx.plugin(LlmRuntime)
340
- await ctx.plugin(SessionStore)
341
- await ctx.plugin(SystemPrompt, { persona: '' })
342
- await ctx.plugin(ToolRuntime)
343
- await ctx.plugin(AgentRegistry)
344
- await ctx.plugin(AgentLoop, { agents: [] })
345
- await ctx.plugin(CommandRuntime)
346
- await ctx.plugin(TokenMeter)
346
+ await ctx.plugin(LlmRuntime).await()
347
+ await ctx.plugin(SessionStore).await()
348
+ await ctx.plugin(SystemPrompt).await()
349
+ await ctx.plugin(ToolRuntime).await()
350
+ await ctx.plugin(AgentRegistry).await()
351
+ await ctx.plugin(AgentLoop).await()
352
+ await ctx.plugin(CommandRuntime).await()
353
+ await ctx.plugin(SessionProjectionRegistry).await()
354
+ await ctx.plugin(TokenMeter).await()
347
355
  await ctx.plugin(MemorySettings).await()
348
356
  await ctx.plugin({ apply }).await()
349
357
  await ctx.plugin(AgentPresets, {
350
358
  default: 'standard',
351
359
  roots: [{ path: rootPath, trust: 'system' }],
360
+ includeShippedRoot: false,
352
361
  includeUserRoot: false,
353
362
  }).await()
354
- const namespace = settingsNamespace('context-compression')
363
+ const namespace = nsBrand('context-compression')
355
364
  await ctx.settings.update(namespace, { autoCompact: { thresholdPercent: 80 } })
356
365
  const bundle = ctx.plugin({ apply: (child) => {
357
366
  apply(child, { presetOverlay: true })
@@ -388,25 +397,27 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
388
397
  const rootPath = await presetsRoot()
389
398
  ctx = new Context()
390
399
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
391
- await ctx.plugin(Loader)
400
+ await ctx.plugin(Loader).await()
392
401
  ctx.loader.builtins.include = Include
393
402
  ctx.loader.builtins.group = Group
394
- await ctx.plugin(LlmRuntime)
395
- await ctx.plugin(SessionStore)
396
- await ctx.plugin(SystemPrompt, { persona: '' })
397
- await ctx.plugin(ToolRuntime)
398
- await ctx.plugin(AgentRegistry)
399
- await ctx.plugin(AgentLoop, { agents: [] })
400
- await ctx.plugin(CommandRuntime)
401
- await ctx.plugin(TokenMeter)
403
+ await ctx.plugin(LlmRuntime).await()
404
+ await ctx.plugin(SessionStore).await()
405
+ await ctx.plugin(SystemPrompt).await()
406
+ await ctx.plugin(ToolRuntime).await()
407
+ await ctx.plugin(AgentRegistry).await()
408
+ await ctx.plugin(AgentLoop).await()
409
+ await ctx.plugin(CommandRuntime).await()
410
+ await ctx.plugin(SessionProjectionRegistry).await()
411
+ await ctx.plugin(TokenMeter).await()
402
412
  await ctx.plugin(MemorySettings).await()
403
413
  await ctx.plugin({ apply }).await()
404
414
  await ctx.plugin(AgentPresets, {
405
415
  default: 'standard',
406
416
  roots: [{ path: rootPath, trust: 'system' }],
417
+ includeShippedRoot: false,
407
418
  includeUserRoot: false,
408
419
  }).await()
409
- const namespace = settingsNamespace('context-compression')
420
+ const namespace = nsBrand('context-compression')
410
421
  await ctx.settings.update(namespace, { autoCompact: { thresholdPercent: 73 } })
411
422
  const bundle = ctx.plugin({ apply: (child) => {
412
423
  apply(child, { presetOverlay: true })
@@ -451,7 +462,7 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
451
462
 
452
463
  it('switches the standing generation for an equal-length threshold change', async () => {
453
464
  await assertStandingSwitch(async () => {
454
- const namespace = settingsNamespace('context-compression')
465
+ const namespace = nsBrand('context-compression')
455
466
  await ctx!.settings.update(namespace, { autoCompact: { thresholdPercent: 70 } })
456
467
  return async () => ctx!.settings.update(namespace, { autoCompact: { thresholdPercent: 80 } })
457
468
  })
@@ -459,7 +470,7 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
459
470
 
460
471
  it('switches the standing generation for an equal-length source change at a fixed threshold', async () => {
461
472
  await assertStandingSwitch(async () => {
462
- const namespace = settingsNamespace('context-compression')
473
+ const namespace = nsBrand('context-compression')
463
474
  await ctx!.settings.update(namespace, { autoCompact: { thresholdPercent: 80 } })
464
475
  return async () => {
465
476
  await rewriteSourceMarker('marker-bbbbbbbbbb.mjs')
@@ -472,25 +483,27 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
472
483
  sourceRoot = rootPath
473
484
  ctx = new Context()
474
485
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
475
- await ctx.plugin(Loader)
486
+ await ctx.plugin(Loader).await()
476
487
  ctx.loader.builtins.include = Include
477
488
  ctx.loader.builtins.group = Group
478
- await ctx.plugin(LlmRuntime)
479
- await ctx.plugin(SessionStore)
480
- await ctx.plugin(SystemPrompt, { persona: '' })
481
- await ctx.plugin(ToolRuntime)
482
- await ctx.plugin(AgentRegistry)
483
- await ctx.plugin(AgentLoop, { agents: [] })
484
- await ctx.plugin(CommandRuntime)
485
- await ctx.plugin(TokenMeter)
489
+ await ctx.plugin(LlmRuntime).await()
490
+ await ctx.plugin(SessionStore).await()
491
+ await ctx.plugin(SystemPrompt).await()
492
+ await ctx.plugin(ToolRuntime).await()
493
+ await ctx.plugin(AgentRegistry).await()
494
+ await ctx.plugin(AgentLoop).await()
495
+ await ctx.plugin(CommandRuntime).await()
496
+ await ctx.plugin(SessionProjectionRegistry).await()
497
+ await ctx.plugin(TokenMeter).await()
486
498
  await ctx.plugin(MemorySettings).await()
487
499
  await ctx.plugin({ apply }).await()
488
500
  await ctx.plugin(AgentPresets, {
489
501
  default: 'standard',
490
502
  roots: [{ path: rootPath, trust: 'system' }],
503
+ includeShippedRoot: false,
491
504
  includeUserRoot: false,
492
505
  }).await()
493
- const namespace = settingsNamespace('context-compression')
506
+ const namespace = nsBrand('context-compression')
494
507
  await ctx.settings.update(namespace, { autoCompact: { thresholdPercent: 80 } })
495
508
 
496
509
  const presets = ctx.agentPresets as unknown as OverlayableAgentPresets
@@ -536,25 +549,27 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
536
549
  const rootPath = await presetsRoot()
537
550
  ctx = new Context()
538
551
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
539
- await ctx.plugin(Loader)
552
+ await ctx.plugin(Loader).await()
540
553
  ctx.loader.builtins.include = Include
541
554
  ctx.loader.builtins.group = Group
542
- await ctx.plugin(LlmRuntime)
543
- await ctx.plugin(SessionStore)
544
- await ctx.plugin(SystemPrompt, { persona: '' })
545
- await ctx.plugin(ToolRuntime)
546
- await ctx.plugin(AgentRegistry)
547
- await ctx.plugin(AgentLoop, { agents: [] })
548
- await ctx.plugin(CommandRuntime)
549
- await ctx.plugin(TokenMeter)
555
+ await ctx.plugin(LlmRuntime).await()
556
+ await ctx.plugin(SessionStore).await()
557
+ await ctx.plugin(SystemPrompt).await()
558
+ await ctx.plugin(ToolRuntime).await()
559
+ await ctx.plugin(AgentRegistry).await()
560
+ await ctx.plugin(AgentLoop).await()
561
+ await ctx.plugin(CommandRuntime).await()
562
+ await ctx.plugin(SessionProjectionRegistry).await()
563
+ await ctx.plugin(TokenMeter).await()
550
564
  await ctx.plugin(MemorySettings).await()
551
565
  await ctx.plugin({ apply }).await()
552
566
  await ctx.plugin(AgentPresets, {
553
567
  default: 'standard',
554
568
  roots: [{ path: rootPath, trust: 'system' }],
569
+ includeShippedRoot: false,
555
570
  includeUserRoot: false,
556
571
  }).await()
557
- const namespace = settingsNamespace('context-compression')
572
+ const namespace = nsBrand('context-compression')
558
573
  await ctx.settings.update(namespace, { autoCompact: { thresholdPercent: 73 } })
559
574
  const bundle = ctx.plugin({ apply: (child) => {
560
575
  apply(child, { presetOverlay: true })
@@ -581,25 +596,27 @@ describe('real AgentPresets standing generations with the overlay threshold', ()
581
596
  const rootPath = await presetsRoot()
582
597
  ctx = new Context()
583
598
  ctx.baseUrl = `${pathToFileURL(rootPath).href}/`
584
- await ctx.plugin(Loader)
599
+ await ctx.plugin(Loader).await()
585
600
  ctx.loader.builtins.include = Include
586
601
  ctx.loader.builtins.group = Group
587
- await ctx.plugin(LlmRuntime)
588
- await ctx.plugin(SessionStore)
589
- await ctx.plugin(SystemPrompt, { persona: '' })
590
- await ctx.plugin(ToolRuntime)
591
- await ctx.plugin(AgentRegistry)
592
- await ctx.plugin(AgentLoop, { agents: [] })
593
- await ctx.plugin(CommandRuntime)
594
- await ctx.plugin(TokenMeter)
602
+ await ctx.plugin(LlmRuntime).await()
603
+ await ctx.plugin(SessionStore).await()
604
+ await ctx.plugin(SystemPrompt).await()
605
+ await ctx.plugin(ToolRuntime).await()
606
+ await ctx.plugin(AgentRegistry).await()
607
+ await ctx.plugin(AgentLoop).await()
608
+ await ctx.plugin(CommandRuntime).await()
609
+ await ctx.plugin(SessionProjectionRegistry).await()
610
+ await ctx.plugin(TokenMeter).await()
595
611
  await ctx.plugin(MemorySettings).await()
596
612
  await ctx.plugin({ apply }).await()
597
613
  await ctx.plugin(AgentPresets, {
598
614
  default: 'standard',
599
615
  roots: [{ path: rootPath, trust: 'system' }],
616
+ includeShippedRoot: false,
600
617
  includeUserRoot: false,
601
618
  }).await()
602
- const namespace = settingsNamespace('context-compression')
619
+ const namespace = nsBrand('context-compression')
603
620
  await ctx.settings.update(namespace, { autoCompact: { thresholdPercent: 70 } })
604
621
  const bundle = ctx.plugin({ apply: (child) => {
605
622
  apply(child, { presetOverlay: true })
@@ -1,3 +1,4 @@
1
+ import { sessionEvents } from '../src/runtime/session-events.ts'
1
2
  import { afterEach, describe, expect, it } from 'vitest'
2
3
  import { Context } from '@deepseek-ai/cordis'
3
4
  import AgentLoop from '@deepseek-ai/dsh-agent-loop'
@@ -8,6 +9,7 @@ import { SessionId } from '@deepseek-ai/dsh-session'
8
9
  import SubagentRuntime from '@deepseek-ai/dsh-subagent'
9
10
  import * as Fork from '@deepseek-ai/dsh-subagent-fork-in-process'
10
11
  import * as Spawn from '@deepseek-ai/dsh-subagent-spawn-in-process'
12
+ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
11
13
  import TokenMeter from '@deepseek-ai/dsh-token-meter'
12
14
  import { PublicMockAdapter, publicTextResponse } from './support/mock-adapter.ts'
13
15
  import {
@@ -47,13 +49,14 @@ const envelope = (options: GenerateOptions, messageCount = options.messages.leng
47
49
 
48
50
  async function liveHarness(requests: Map<string, GenerateOptions[]>): Promise<Context> {
49
51
  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)
52
+ await mountAgentLoopTestDependencies(created, { systemPrompt: { personaPrefix: SYSTEM } })
53
+ await created.plugin(SessionProjectionRegistry).await()
54
+ await created.plugin(TokenMeter).await()
55
+ await created.plugin(AgentLoop).await()
56
+ await created.plugin(SubagentRuntime).await()
57
+ await created.plugin(Fork).await()
58
+ await created.plugin(Spawn).await()
59
+ await created.plugin(LlmDeepSeek).await()
57
60
  created.on('llm/stream', (options, next) => {
58
61
  const sessionId = String(options.sessionId)
59
62
  requests.set(sessionId, [...(requests.get(sessionId) ?? []), options])
@@ -64,11 +67,11 @@ async function liveHarness(requests: Map<string, GenerateOptions[]>): Promise<Co
64
67
 
65
68
  async function keylessHarness(requests: Map<string, GenerateOptions[]>): Promise<Context> {
66
69
  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' })
70
+ await mountAgentLoopTestDependencies(created, { systemPrompt: { personaPrefix: SYSTEM } })
71
+ await created.plugin(AgentLoop).await()
72
+ await created.plugin(SubagentRuntime).await()
73
+ await created.plugin(Fork).await()
74
+ await created.plugin(Spawn).await()
72
75
  created.llm.registerAdapter(['mock'], new PublicMockAdapter([
73
76
  publicTextResponse('cobalt-heron-42'),
74
77
  publicTextResponse('cobalt-heron-42'),
@@ -86,7 +89,7 @@ describe('parent/child prefix behavior (keyless full-loop E2E)', () => {
86
89
  it('runs parent, fork, and spawn through the real loop and session providers', async () => {
87
90
  const requests = new Map<string, GenerateOptions[]>()
88
91
  ctx = await keylessHarness(requests)
89
- const parent = ctx.agentLoop.create(SessionId('cache-parent-keyless-e2e'), {
92
+ const parent = await ctx.agentLoop.create(SessionId('cache-parent-keyless-e2e'), {
90
93
  provider: 'mock',
91
94
  model: 'mock',
92
95
  })
@@ -95,7 +98,7 @@ describe('parent/child prefix behavior (keyless full-loop E2E)', () => {
95
98
  source: { kind: 'user' },
96
99
  }))
97
100
  await parent.whenIdle()
98
- const parentEvents = parent.session.events.slice()
101
+ const parentEvents = sessionEvents(parent.session).slice()
99
102
  const parentRequest = requests.get(String(parent.id))?.at(-1)
100
103
  expect(parentRequest).toBeDefined()
101
104
 
@@ -112,8 +115,8 @@ describe('parent/child prefix behavior (keyless full-loop E2E)', () => {
112
115
  if (forkChild === undefined || forkRequest === undefined || parentRequest === undefined) {
113
116
  throw new Error('keyless fork did not publish its complete evidence')
114
117
  }
115
- expect(forkChild.session.header.seedLength).toBe(parentEvents.length)
116
- expect(forkChild.session.events.slice(0, parentEvents.length)).toEqual(parentEvents)
118
+ expect(forkChild.session.firstLiveSeq).toBe(parentEvents.length)
119
+ expect(sessionEvents(forkChild.session).slice(0, parentEvents.length)).toEqual(parentEvents)
117
120
  const parentStable = envelope(parentRequest)
118
121
  const forkStablePrefix = envelope(forkRequest, parentRequest.messages.length)
119
122
  expect(fingerprintStablePrefix(forkStablePrefix)).toBe(fingerprintStablePrefix(parentStable))
@@ -127,7 +130,7 @@ describe('parent/child prefix behavior (keyless full-loop E2E)', () => {
127
130
  await spawnRun.result
128
131
  const spawnChild = spawnRun.localAgent
129
132
  const spawnRequest = requests.get(String(spawnRun.id))?.at(-1)
130
- expect(spawnChild?.session.header.seedLength).toBeUndefined()
133
+ expect(spawnChild?.session.firstLiveSeq).toBe(0)
131
134
  expect(spawnRequest).toBeDefined()
132
135
  expect(JSON.stringify(spawnRequest?.messages)).not.toContain(PARENT_TEXT)
133
136
  expect(assessReusablePrefix({
@@ -158,7 +161,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('parent/child natural cache-prefi
158
161
  it('preserves the fork prefix, separates spawn, and records only official usage evidence', async () => {
159
162
  const requests = new Map<string, GenerateOptions[]>()
160
163
  ctx = await liveHarness(requests)
161
- const parent = ctx.agentLoop.create(SessionId('cache-parent-e2e'), {
164
+ const parent = await ctx.agentLoop.create(SessionId('cache-parent-e2e'), {
162
165
  provider: 'deepseek-official',
163
166
  model: 'deepseek-v4-flash',
164
167
  })
@@ -167,7 +170,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('parent/child natural cache-prefi
167
170
  source: { kind: 'user' },
168
171
  }))
169
172
  await parent.whenIdle()
170
- const parentEvents = parent.session.events.slice()
173
+ const parentEvents = sessionEvents(parent.session).slice()
171
174
  const parentRequest = requests.get(String(parent.id))?.at(-1)
172
175
  expect(parentRequest).toBeDefined()
173
176
 
@@ -186,7 +189,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('parent/child natural cache-prefi
186
189
  parentSession: parent.session.header.id,
187
190
  seedLength: parentEvents.length,
188
191
  })
189
- expect(forkChild.session.events.slice(0, parentEvents.length)).toEqual(parentEvents)
192
+ expect(sessionEvents(forkChild.session).slice(0, parentEvents.length)).toEqual(parentEvents)
190
193
 
191
194
  const parentStable = envelope(parentRequest!)
192
195
  const forkStablePrefix = envelope(forkRequest!, parentRequest!.messages.length)
@@ -234,7 +237,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('parent/child natural cache-prefi
234
237
  if (spawnChild === undefined) throw new Error('spawn did not publish a local agent')
235
238
  const spawnRequest = requests.get(String(spawnRun.id))?.at(-1)
236
239
  expect(spawnChild.session.header.parentSession).toBe(parent.session.header.id)
237
- expect(spawnChild.session.header.seedLength).toBeUndefined()
240
+ expect(spawnChild.session.firstLiveSeq).toBe(0)
238
241
  expect(spawnRequest).toBeDefined()
239
242
  expect(JSON.stringify(spawnRequest!.messages)).not.toContain(PARENT_TEXT)
240
243
  expect(assessReusablePrefix({
@@ -11,9 +11,56 @@ allowBuilds:
11
11
  esbuild: true
12
12
  "@huggingface/tokenizers": true
13
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
14
  overrides:
19
- js-yaml@<4.3.2: ^4.3.2
15
+ '@deepseek-ai/cordis': '4.0.1'
16
+ '@deepseek-ai/cordis-plugin-group': '1.0.1'
17
+ '@deepseek-ai/cordis-plugin-include': '1.0.6'
18
+ '@deepseek-ai/cordis-plugin-loader': '1.0.2'
19
+ '@deepseek-ai/dsh-agent': '0.1.5-rc.2'
20
+ '@deepseek-ai/dsh-agent-loop': '0.1.5-rc.2'
21
+ '@deepseek-ai/dsh-agent-loop-testkit': '0.1.5-rc.2'
22
+ '@deepseek-ai/dsh-agent-presets': '0.1.5-rc.2'
23
+ '@deepseek-ai/dsh-client-locale': '0.1.5-rc.2'
24
+ '@deepseek-ai/dsh-client-ui-primitives': '0.1.5-rc.2'
25
+ '@deepseek-ai/dsh-client-ui-settings': '0.1.5-rc.2'
26
+ '@deepseek-ai/dsh-client-ui-slots': '0.1.5-rc.2'
27
+ '@deepseek-ai/dsh-client-store': '0.1.5-rc.2'
28
+ '@deepseek-ai/dsh-client-ui-workspace': '0.1.5-rc.2'
29
+ '@deepseek-ai/dsh-command-compact': '0.1.5-rc.2'
30
+ '@deepseek-ai/dsh-commands': '0.1.5-rc.2'
31
+ '@deepseek-ai/dsh-compaction': '0.1.5-rc.2'
32
+ '@deepseek-ai/dsh-compaction-basic': '0.1.5-rc.2'
33
+ '@deepseek-ai/dsh-invariants': '0.1.5-rc.2'
34
+ '@deepseek-ai/dsh-llm': '0.1.5-rc.2'
35
+ '@deepseek-ai/dsh-llm-deepseek': '0.1.5-rc.2'
36
+ '@deepseek-ai/dsh-scope': '0.1.5-rc.2'
37
+ '@deepseek-ai/dsh-session': '0.1.5-rc.2'
38
+ '@deepseek-ai/dsh-settings': '0.1.5-rc.2'
39
+ '@deepseek-ai/dsh-subagent': '0.1.5-rc.2'
40
+ '@deepseek-ai/dsh-subagent-fork-in-process': '0.1.5-rc.2'
41
+ '@deepseek-ai/dsh-subagent-spawn-in-process': '0.1.5-rc.2'
42
+ '@deepseek-ai/dsh-system-prompt': '0.1.5-rc.2'
43
+ '@deepseek-ai/dsh-token-meter': '0.1.5-rc.2'
44
+ '@deepseek-ai/dsh-tools': '0.1.5-rc.2'
45
+ '@deepseek-ai/dsh-anonymous-user-id': '0.1.5-rc.2'
46
+ '@deepseek-ai/dsh-atomic-write': '0.1.5-rc.2'
47
+ '@deepseek-ai/dsh-attachment': '0.1.5-rc.2'
48
+ '@deepseek-ai/dsh-code-runtime': '0.1.5-rc.2'
49
+ '@deepseek-ai/dsh-credentials': '0.1.5-rc.2'
50
+ '@deepseek-ai/dsh-home-paths': '0.1.5-rc.2'
51
+ '@deepseek-ai/dsh-launch-environment': '0.1.5-rc.2'
52
+ '@deepseek-ai/dsh-llm-retry': '0.1.5-rc.2'
53
+ '@deepseek-ai/dsh-sandbox': '0.1.5-rc.2'
54
+ '@deepseek-ai/dsh-session-persistence': '0.1.5-rc.2'
55
+ '@deepseek-ai/dsh-session-projection': '0.1.5-rc.2'
56
+ '@deepseek-ai/dsh-subagent-in-process-driver': '0.1.5-rc.2'
57
+ '@deepseek-ai/dsh-user-approval': '0.1.5-rc.2'
58
+ '@deepseek-ai/schemastery': '3.18.2'
59
+ '@deepseek-ai/dsh-client-ui-session': '0.1.5-rc.2'
60
+ '@deepseek-ai/dsh-api-session-controller': '0.1.5-rc.2'
61
+ # Scoped, not global: @deepseek-ai/cordis-plugin-include (^4.1.0) and
62
+ # @deepseek-ai/dsh-agent-presets resolve js-yaml 4.3.1 from ranges that already
63
+ # allow the patched release, so pin the vulnerable ones without waiting on their
64
+ # next release. GHSA-2883-xcg3-v3hh: js-yaml < 4.3.2 does not bound CPU use for
65
+ # empty merge sources.
66
+ 'js-yaml@<4.3.2': '^4.3.2'
@@ -0,0 +1,42 @@
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/tokenpilot/profile-baseline.spec.ts as the backward-compat golden.
5
+ *
6
+ * Usage: node scripts/capture-profile-baseline.mjs [baseline-file]
7
+ */
8
+ import { writeFileSync } from 'node:fs'
9
+ import { createRequire } from 'node:module'
10
+
11
+ const require = createRequire(import.meta.url)
12
+ const runtime = require('../packages/runtime/lib/index.js')
13
+
14
+ const OLD_PROFILES = ['off', 'native', 'balanced', 'cache-strict', 'savings', 'adaptive', 'custom']
15
+ const baseConfig = runtime.resolveConfig({})
16
+ const OPTION_CASES = [
17
+ { label: 'defaults', options: {} },
18
+ { label: 'linkage', options: { contextWindowTokens: 128_000, autoCompactThresholdPercent: 80 } },
19
+ ]
20
+ const CUSTOM_DOCS = [
21
+ { label: 'default-custom', custom: undefined },
22
+ ]
23
+
24
+ const snapshot = { capturedAt: 'pre-tokenpilot-inspired', profiles: {} }
25
+ for (const profile of OLD_PROFILES) {
26
+ snapshot.profiles[profile] = {}
27
+ for (const optionCase of OPTION_CASES) {
28
+ for (const customCase of CUSTOM_DOCS) {
29
+ const key = `${optionCase.label}${customCase.label === 'default-custom' ? '' : `/${customCase.label}`}`
30
+ snapshot.profiles[profile][key] = runtime.resolvePolicy(
31
+ baseConfig,
32
+ profile,
33
+ customCase.custom,
34
+ optionCase.options,
35
+ )
36
+ }
37
+ }
38
+ }
39
+
40
+ const out = process.argv[2] ?? 'packages/runtime/tests/fixtures/profile-baseline.json'
41
+ writeFileSync(out, `${JSON.stringify(snapshot, null, 2)}\n`)
42
+ console.log(`baseline written: ${out}`)
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """Regenerate the tokenizer golden fixture for the Node loader equivalence tests.
3
3
 
4
- Loads each pinned ``tokenizer.json`` shipped in ``packages/selector/assets`` with
4
+ Loads each pinned ``tokenizer.json`` shipped in ``packages/runtime/assets`` with
5
5
  the official Hugging Face ``tokenizers`` Python library (the same Rust core the
6
6
  ``transformers`` tokenizer for these repositories uses) and records the token
7
7
  counts the Node runtime must reproduce exactly.
@@ -10,7 +10,7 @@ Usage:
10
10
  python3 scripts/generate-tokenizer-fixtures.py
11
11
 
12
12
  Requires: pip install tokenizers
13
- Output: packages/selector/tests/runtime/fixtures/tokenizer-golden.json
13
+ Output: packages/runtime/tests/fixtures/tokenizer-golden.json
14
14
  """
15
15
 
16
16
  import json
@@ -19,11 +19,11 @@ import platform
19
19
 
20
20
  REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
21
21
  ASSETS = {
22
- "deepseek-ai/DeepSeek-V4-Pro": REPO_ROOT / "packages/selector/assets/deepseek-v4",
22
+ "deepseek-ai/DeepSeek-V4-Pro": REPO_ROOT / "packages/runtime/assets/deepseek-v4",
23
23
  "deepseek-ai/DeepSeek-V4-Flash-Vision-Exp": REPO_ROOT
24
- / "packages/selector/assets/deepseek-v4-vision-exp",
24
+ / "packages/runtime/assets/deepseek-v4-vision-exp",
25
25
  }
26
- OUTPUT = REPO_ROOT / "packages/selector/tests/runtime/fixtures/tokenizer-golden.json"
26
+ OUTPUT = REPO_ROOT / "packages/runtime/tests/fixtures/tokenizer-golden.json"
27
27
 
28
28
  CASES = [
29
29
  ("english prose", "The quick brown fox jumps over the lazy dog."),
@@ -13,7 +13,7 @@ Usage:
13
13
  python3 scripts/generate-vision-fixtures.py [--workdir DIR]
14
14
 
15
15
  Requires: pip install torch pillow
16
- Output: packages/selector/tests/runtime/fixtures/vision-golden.json
16
+ Output: packages/runtime/tests/fixtures/vision-golden.json
17
17
  """
18
18
 
19
19
  import argparse
@@ -26,7 +26,7 @@ import types
26
26
  import urllib.request
27
27
 
28
28
  REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
29
- OUTPUT = REPO_ROOT / "packages/selector/tests/runtime/fixtures/vision-golden.json"
29
+ OUTPUT = REPO_ROOT / "packages/runtime/tests/fixtures/vision-golden.json"
30
30
  REVISION = "6821d6ad3681a4b137b066b76094fa82ebd0a380"
31
31
  BASE = f"https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash-Vision-Exp/resolve/{REVISION}"
32
32