billion-context-dsh 0.2.21 → 0.2.22

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.
@@ -0,0 +1,157 @@
1
+ /**
2
+ * M6 — runtime settings integration. Wires the engine's scalar knobs into the
3
+ * host's user-settings layer (`~/.dsh/settings.yaml`, section
4
+ * `compaction-acp`) through the official consumer seam
5
+ * `SettingsProvider.installSection` (@deepseek-ai/dsh-settings), so editing the file
6
+ * applies to RUNNING sessions without a restart.
7
+ *
8
+ * Layering (per key): schemastery schema default → composition-row subset
9
+ * (the `base` layer, filtered by `filterSettingsEntry`) → user section.
10
+ * The `/acp config` slash command reads and writes the same namespace
11
+ * through the `SettingsCommandSurface` built here.
12
+ *
13
+ * Deliberately NOT exposed through settings: `coreOverrides`, `countTokens`,
14
+ * `autoTools`, `autoCommand`, `prompts` (object/function values or
15
+ * construction-time registrations), and the `settingsEnabled` kill switch
16
+ * itself (a switch that turns off its own plumbing could not be reached if
17
+ * the plumbing broke).
18
+ * @module billion-context-dsh/settings
19
+ */
20
+ import z from '@deepseek-ai/schemastery';
21
+ import type { SettingsDescriptor, SettingsProvider } from '@deepseek-ai/dsh-settings';
22
+ /**
23
+ * The host settings namespace — same id as the bundle/composition row, so "the
24
+ * settings.yaml section" and "the cordis.patch.yml row" are one mental object.
25
+ * A plain string literal as of the 0.1.5 line: the seam's `settingsNamespace()`
26
+ * runtime helper is gone and the brand is applied at the call site instead
27
+ * (`installSection`'s `Namespace & SettingsNamespaceInput<Namespace>`).
28
+ */
29
+ export declare const ACP_SETTINGS_NAMESPACE = "compaction-acp";
30
+ /** The six knobs exposed to the runtime settings layer. Order defines /acp config listing order. */
31
+ export declare const SETTINGS_KEYS: readonly ['modelContextLimit', 'autoModelContextLimit', 'nudgeMinContextLimitPct', 'nudgeMaxContextLimitPct', 'nudgeEmergencyThresholdPct', 'autoNudge'];
32
+ export type SettingsKey = (typeof SETTINGS_KEYS)[number];
33
+ /** Resolved shape of one settings snapshot — what every consumer read returns. */
34
+ export interface AcpSettings {
35
+ /** Absent = auto-detection mode (probe the model's real window). */
36
+ readonly modelContextLimit?: number;
37
+ readonly autoModelContextLimit: boolean;
38
+ /** Absent = the kernel's own 0.45 floor stays in effect. */
39
+ readonly nudgeMinContextLimitPct?: number;
40
+ readonly nudgeMaxContextLimitPct: number;
41
+ readonly nudgeEmergencyThresholdPct: number;
42
+ readonly autoNudge: boolean;
43
+ }
44
+ /** Input shape (everything optional — omitted keys fall back to defaults). */
45
+ export type AcpSettingsInput = Partial<AcpSettings>;
46
+ /**
47
+ * Engine defaults for the settings-exposed keys — MUST mirror
48
+ * `DEFAULT_CONFIG` in src/index.ts (locked together by tests/settings.test.ts,
49
+ * which compares these against the real DEFAULT_CONFIG field by field).
50
+ */
51
+ export declare const SETTING_DEFAULTS: {
52
+ readonly autoModelContextLimit: true;
53
+ readonly nudgeMaxContextLimitPct: 0.7;
54
+ readonly nudgeEmergencyThresholdPct: 0.85;
55
+ readonly autoNudge: true;
56
+ };
57
+ /**
58
+ * The subset of `AcpConfig` the settings layer may see. Declared structurally
59
+ * (instead of importing AcpConfig) so this module stays dependency-free —
60
+ * src/index.ts's `AcpConfig` satisfies it as-is.
61
+ */
62
+ export interface AcpSettingsCompositionEntry {
63
+ readonly modelContextLimit?: number;
64
+ readonly autoModelContextLimit?: boolean;
65
+ readonly nudgeMinContextLimitPct?: number;
66
+ readonly nudgeMaxContextLimitPct?: number;
67
+ readonly nudgeEmergencyThresholdPct?: number;
68
+ readonly autoNudge?: boolean;
69
+ }
70
+ /**
71
+ * Filter a composition-row config down to the settings-known scalar keys.
72
+ * This filtered subset is the ONLY thing handed to the settings layer as its
73
+ * `base`: the raw row also carries prompts/coreOverrides/countTokens — object
74
+ * and function values that would flow into the stored resolved snapshot (the
75
+ * settings resolver does not reject unknown keys) and pollute describe()/clone
76
+ * paths downstream.
77
+ */
78
+ export declare function filterSettingsEntry(entry: AcpSettingsCompositionEntry): AcpSettingsInput;
79
+ /** Apply the engine defaults to a (possibly partial) settings input. */
80
+ export declare function resolveAcpSettings(input: AcpSettingsInput): AcpSettings;
81
+ /**
82
+ * The settings schema. Defaults here are the ENGINE defaults (0.70/0.85),
83
+ * not the kernel's 0.75/0.95 — an untouched namespace must reproduce exactly
84
+ * today's behavior. Integer constraint uses `.step(1).min(1)` because
85
+ * schemastery 3.18.x has no `.int()`/`.positive()` helpers.
86
+ */
87
+ export declare const AcpSettingsSchema: z<Schemastery.ObjectS<{
88
+ modelContextLimit: z<number, number>;
89
+ autoModelContextLimit: z<boolean, boolean>;
90
+ nudgeMinContextLimitPct: z<number, number>;
91
+ nudgeMaxContextLimitPct: z<number, number>;
92
+ nudgeEmergencyThresholdPct: z<number, number>;
93
+ autoNudge: z<boolean, boolean>;
94
+ }>, Schemastery.ObjectT<{
95
+ modelContextLimit: z<number, number>;
96
+ autoModelContextLimit: z<boolean, boolean>;
97
+ nudgeMinContextLimitPct: z<number, number>;
98
+ nudgeMaxContextLimitPct: z<number, number>;
99
+ nudgeEmergencyThresholdPct: z<number, number>;
100
+ autoNudge: z<boolean, boolean>;
101
+ }>>;
102
+ /** What changed between two settings snapshots, and what the engine must do about it. */
103
+ export interface SettingsChangeEffect {
104
+ /**
105
+ * The per-route window cache (which also caches probe FAILURES) must be
106
+ * dropped so the next step re-resolves windows under the new limits.
107
+ */
108
+ clearWindowCache: boolean;
109
+ /**
110
+ * Re-enabling nudges clears the per-turn dedup map: entries written while
111
+ * nudging was off must not suppress the first fresh nudge.
112
+ */
113
+ clearNudgeDedup: boolean;
114
+ /** Human-readable order-anomaly warnings. Accepted, not rejected — a rejected write cannot fix an externally-edited file anyway. */
115
+ readonly warnings: readonly string[];
116
+ }
117
+ /** Pure diff used by the engine's change handler (unit-testable without a context). */
118
+ export declare function describeSettingsChange(prev: AcpSettings, next: AcpSettings): SettingsChangeEffect;
119
+ /** Result of parsing a `/acp config set` value. `null` means "reset this key". */
120
+ export type ParsedSettingValue = {
121
+ ok: true;
122
+ value: number | boolean | null;
123
+ } | {
124
+ ok: false;
125
+ reason: string;
126
+ };
127
+ /**
128
+ * Four-step value parser for `/acp config set` — deliberately NOT bare
129
+ * JSON.parse, which rejects the most common human inputs (`.7` throws a
130
+ * SyntaxError and the raw string would then fail schema validation; `null`
131
+ * would silently mean "unset" only by convention). Order:
132
+ * 1. `true` / `false` literals → booleans;
133
+ * 2. anything Number() accepts finitely (`.7`, `2e5`, `200000`) → number;
134
+ * 3. `null` (word) → reset-this-key sentinel;
135
+ * 4. otherwise rejected with guidance.
136
+ */
137
+ export declare function parseSettingValue(raw: string): ParsedSettingValue;
138
+ /** Everything `/acp config` needs from the engine. Fakes in tests implement this directly. */
139
+ export interface SettingsCommandSurface {
140
+ /** False in processes without a settings provider (plain npm-install compositions): the command degrades to advice instead of failing. */
141
+ readonly available: boolean;
142
+ /** Current effective values (works with or without a provider). */
143
+ snapshot(): AcpSettings;
144
+ /** Our namespace's descriptor (layers + revision), or undefined while unregistered. */
145
+ describe(): SettingsDescriptor | undefined;
146
+ /** Merge a patch into the user section and persist it. */
147
+ update(patch: AcpSettingsInput): Promise<void>;
148
+ /** Replace the whole user section ({} resets everything to base/defaults). */
149
+ replaceSection(section: Record<string, unknown>): Promise<void>;
150
+ }
151
+ /**
152
+ * Build the command surface over a lazily-captured settings service. The
153
+ * engine captures the service through a parallel `ctx.inject(['settings'])`,
154
+ * so the reference may legitimately be undefined for the whole process life
155
+ * (headless/plain compositions have no settings provider).
156
+ */
157
+ export declare function makeSettingsCommandSurface(getService: () => SettingsProvider | undefined, getSnapshot: () => AcpSettings): SettingsCommandSurface;
package/dist/state.d.ts CHANGED
@@ -19,7 +19,18 @@
19
19
  import type { Session } from '@deepseek-ai/dsh-session';
20
20
  import { type CompressionState } from 'acp-kernel';
21
21
  export declare class AcpStateStore {
22
+ /**
23
+ * Live kernel states, capped by an LRU policy (issue #113): once the cap is
24
+ * reached the coldest session's state is dropped, and its next access
25
+ * rehydrates through stateFor's log-rebuild path below. Rehydration is
26
+ * deterministic — bN ids are recorded in the durable event or synthesised
27
+ * in ledger order, and run ids continue after the rehydrated max — so block
28
+ * identity survives eviction exactly as it survives a restart. Kernel
29
+ * fields that reset on eviction (tokenSnapshot, nudge cadence, stats
30
+ * counters) all self-heal on the session's next turn.
31
+ */
22
32
  private readonly states;
33
+ constructor(limit?: number);
23
34
  /** Kernel state for one session, initialised on first access. */
24
35
  stateFor(session: Session): CompressionState;
25
36
  set(session: Session, state: CompressionState): void;
package/dist/tools.d.ts CHANGED
@@ -10,15 +10,20 @@
10
10
  * @module billion-context-dsh/tools
11
11
  */
12
12
  import { type ToolDefinition } from '@deepseek-ai/dsh-tools';
13
- import { type CompressionCore } from 'acp-kernel';
13
+ import { type CompressionCore, type SearchDoc } from 'acp-kernel';
14
14
  import type { Agent } from '@deepseek-ai/dsh-agent';
15
+ import type { Session } from '@deepseek-ai/dsh-session';
15
16
  import type { AcpStateStore } from './state.ts';
16
17
  import { type KernelConfigInput } from './config.ts';
17
- import type { AcpWindow } from './window.ts';
18
+ import { type AcpWindow } from './window.ts';
18
19
  import { type ResolvedPrompts } from './prompts.ts';
20
+ import type { SettingsCommandSurface } from './settings.ts';
21
+ import type { PresetName } from './presets.ts';
19
22
  export interface ToolEnvironment extends KernelConfigInput {
20
23
  readonly kernel: CompressionCore;
21
24
  readonly store: AcpStateStore;
25
+ /** Display-only: the named preset that produced the nudge thresholds above, if any (`/acp status` names it). Never read by the kernel path. */
26
+ readonly preset?: PresetName;
22
27
  /** Resolve the effective context window for an agent (optional: status falls back to modelContextLimit). */
23
28
  readonly windowFor?: (agent: Agent) => Promise<AcpWindow>;
24
29
  /** Resolved prompt templates (optional: falls back to DEFAULT_RESOLVED). */
@@ -30,6 +35,12 @@ export interface ToolEnvironment extends KernelConfigInput {
30
35
  * (strict providers reject that sequence with HTTP 400).
31
36
  */
32
37
  readonly compressCallIdsToHide?: Set<string>;
38
+ /**
39
+ * Read/write access to the runtime settings layer for `/acp config`.
40
+ * Absent surfaces (never expected — the engine always builds one) would
41
+ * degrade the command to advice text.
42
+ */
43
+ readonly settingsCommand?: SettingsCommandSurface;
33
44
  }
34
45
  /**
35
46
  * Resolve the effective context window for a tool or command run: probe the
@@ -40,5 +51,105 @@ export interface ToolEnvironment extends KernelConfigInput {
40
51
  * for pressure decisions even when auto-detection had found a larger window).
41
52
  */
42
53
  export declare function resolveEffectiveWindow(env: ToolEnvironment, agent: Agent): Promise<AcpWindow>;
54
+ export declare const compressParameters: {
55
+ readonly arguments: {
56
+ readonly type: 'json';
57
+ readonly description: 'Tolerated wrapped-arguments form (model-generated); unwrapped in handleCompress. Prefer passing content directly.';
58
+ };
59
+ readonly topic: {
60
+ readonly type: 'string';
61
+ readonly description: 'Fallback topic for entries without their own.';
62
+ };
63
+ readonly content: {
64
+ readonly type: 'array';
65
+ readonly description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary. Required — pass it directly, not wrapped in an arguments key.';
66
+ readonly items: {
67
+ readonly type: 'object';
68
+ readonly properties: {
69
+ readonly startSeq: {
70
+ readonly required: true;
71
+ readonly oneOf: readonly [{
72
+ readonly type: 'integer';
73
+ readonly description: 'First surface seq of the range.';
74
+ }, {
75
+ readonly type: 'string';
76
+ readonly description: 'Seq as text; a trailing #callId fragment is ignored.';
77
+ }];
78
+ };
79
+ readonly endSeq: {
80
+ readonly required: true;
81
+ readonly oneOf: readonly [{
82
+ readonly type: 'integer';
83
+ readonly description: 'Inclusive last surface seq of the range.';
84
+ }, {
85
+ readonly type: 'string';
86
+ readonly description: 'Seq as text; a trailing #callId fragment is ignored.';
87
+ }];
88
+ };
89
+ readonly summary: {
90
+ readonly type: 'string';
91
+ readonly required: true;
92
+ readonly description: 'Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters.';
93
+ };
94
+ readonly topic: {
95
+ readonly type: 'string';
96
+ readonly description: 'Short label (3-5 words) for this range.';
97
+ };
98
+ readonly verifiedReadings: {
99
+ readonly type: 'array';
100
+ readonly items: {
101
+ readonly type: 'string';
102
+ };
103
+ readonly description: 'Optional: acceptance readings that are already green before this compression (e.g. "t0-fastpath 8/8", "closedloop 414/414"). Stored structurally on the compaction/summary event and recovered by verifiedReadingsOf, so later steps need not re-run the checks.';
104
+ };
105
+ };
106
+ readonly additionalProperties: false;
107
+ };
108
+ };
109
+ };
110
+ /**
111
+ * Pure gate helpers for the compress tool's CURRENT-instruction-row rejection.
112
+ *
113
+ * Decision history (issue #71 review): the first draft only WARNED when a
114
+ * manual compress range swallowed a current injected row (F7), because the
115
+ * compression is safe and self-healing. The owner reversed that during PR1
116
+ * review: compressing a CURRENT row has NO legitimate outcome — the host
117
+ * re-injects the newest AGENTS.md copy unconditionally the moment it leaves
118
+ * the surface (presence gate, deepseek-harness
119
+ * packages/context/agent-instructions/src/index.ts:137/:163), so the tokens
120
+ * come straight back and the call is pure waste — and a hard reject keeps the
121
+ * manual path consistent with the system-side GC's iron rule (PR2: never
122
+ * clear a group's newest row). STALE copies stay compressible: removing them
123
+ * while the newest stays visible is the actual cleanup and triggers no
124
+ * re-injection. The range table (buildCompressibleSeqRanges) never offers
125
+ * these rows, so the gate only fires on hand-built ranges.
126
+ *
127
+ * `guardedRowsInSpan` is the overlap probe. It takes the POSITIONAL span the
128
+ * transaction will actually shadow (`shadowedSeqsOf`), never a numeric
129
+ * `start <= seq <= end` interval: the surface is locally non-monotonic after
130
+ * earlier replacements (a checkpoint seq spliced ahead of older residual
131
+ * nodes), so a tier-2 distill of two checkpoints can carry a CURRENT
132
+ * instruction row numerically inside its edges while the sliced span excludes
133
+ * it — the interval probe rejected exactly the call the nudge hands the model
134
+ * (issue #71 review B1). Probing the slice also keeps guard and effect in
135
+ * agreement: `shadowedSeqsOf` is what the transaction prices and
136
+ * `assertProvenance` verifies.
137
+ * `protectedRowRejectionNote` renders the rejection the model sees: it names
138
+ * the offending seqs AND the compressible slices left in the span, so the model
139
+ * can re-cut (or split into two calls) instead of retrying the same call.
140
+ * `guardedSurfaceSeqsOf` supplies the protected set.
141
+ */
142
+ export declare function guardedRowsInSpan(guarded: ReadonlySet<number>, shadowed: readonly number[]): number[];
143
+ export declare function protectedRowRejectionNote(start: number, end: number, hits: readonly number[], shadowed: readonly number[]): string;
144
+ /**
145
+ * Build the unified SearchDoc[] from the log: one block doc per ledger entry
146
+ * (ref = compactionId, so `decompress({ blockId })` closes the loop) plus one
147
+ * message doc per shadowed ORIGINAL (expanded through distilled parents; each
148
+ * seq is claimed by the earliest/innermost block that covered it, mirroring
149
+ * pi's owner map — decompress on that block recovers the original).
150
+ * Cached per log snapshot (see searchDocsCache). Exported for the issue #133
151
+ * regression tests (not part of the public API — index.ts re-exports only).
152
+ */
153
+ export declare function buildSearchDocs(session: Session): SearchDoc[];
43
154
  /** Build the four ACP model tools bound to one engine. */
44
155
  export declare function makeTools(env: ToolEnvironment): ToolDefinition[];
package/dist/window.d.ts CHANGED
@@ -25,9 +25,11 @@ export interface AcpWindow {
25
25
  readonly source: 'explicit' | 'auto' | 'projection' | 'default';
26
26
  /**
27
27
  * Route the window was resolved for. 'auto' reports the probed route;
28
- * 'projection' returns also set it, mirroring agent.options — which can be
29
- * stale after a mid-session model switch (inert today: windowSourceLabel
30
- * never reads these fields for the projection source).
28
+ * 'projection' returns also set it, from the session's LIVE route (its last
29
+ * `request/context` event) — NOT `agent.options`, which is a stale snapshot
30
+ * after a mid-session model switch; `agent.options` is the fallback only
31
+ * before the session has recorded any route. (Inert today: windowSourceLabel
32
+ * never reads these fields for the projection source.)
31
33
  */
32
34
  readonly provider?: string;
33
35
  readonly model?: string;
@@ -66,6 +68,33 @@ export declare function windowSourceLabel(window: AcpWindow): string;
66
68
  * Returns null when the host exposes no projection or disclosed no window.
67
69
  */
68
70
  export declare function projectedContextWindow(agent: Agent): number | null;
71
+ /**
72
+ * Read the LIVE model route from the session's last `request/context` event.
73
+ * After a mid-session model switch `agent.options` is a stale snapshot (it
74
+ * names the PREVIOUS route), so the per-route output cap must be resolved
75
+ * against this live route instead — otherwise the cap lags one switch behind
76
+ * (a 32K cap from a just-left model subtracted from the new model's window).
77
+ * Returns null before the session has recorded any route, so callers fall
78
+ * back to `agent.options`. Never throws, like `probeModelWindow`: the caller
79
+ * runs inside `agent/pre-step`, which has no surrounding try.
80
+ */
81
+ export declare function liveRoute(agent: Agent): {
82
+ provider: string;
83
+ model: string;
84
+ } | null;
85
+ /**
86
+ * The route the per-route output cap and the compression provenance must be
87
+ * resolved against, in ONE place: the session's live `request/context` route,
88
+ * falling back to `agent.options` only before the session has recorded any
89
+ * route. `windowFor` (src/index.ts), the `compress` tool (src/tools.ts) and
90
+ * `/acp compress` (src/commands.ts) all need this exact pair; three hand-copied
91
+ * copies is precisely how a stale-route bug gets fixed in one call site and
92
+ * left behind in the others.
93
+ */
94
+ export declare function routeFor(agent: Agent): {
95
+ provider: string;
96
+ model: string;
97
+ };
69
98
  /** The model window plus the adapter's per-request output cap, in one probe. */
70
99
  export interface ModelWindowProbe {
71
100
  /** The model's total context window in tokens, when disclosed. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "billion-context-dsh",
3
- "version": "0.2.21",
3
+ "version": "0.2.22",
4
4
  "description": "Active Context Pruning (ACP) for the DeepSeek Harness — model-driven context management as a CompactionEngine backend.",
5
5
  "keywords": [
6
6
  "deepseek",
@@ -43,27 +43,35 @@
43
43
  "scripts": {
44
44
  "typecheck": "tsc --noEmit",
45
45
  "build": "tsup && tsc --emitDeclarationOnly",
46
- "test": "node --import tsx --test tests/*.test.ts tests/kernel-upstream/*.test.ts"
46
+ "test": "node --import tsx --test tests/*.test.ts tests/kernel-upstream/*.test.ts",
47
+ "test:e2e": "node scripts/e2e/run-e2e.mjs"
47
48
  },
48
49
  "peerDependencies": {
49
50
  "@deepseek-ai/cordis": "^4.0.1",
50
- "@deepseek-ai/dsh-compaction": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.4",
51
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.4",
52
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.4",
53
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.4"
51
+ "@deepseek-ai/dsh-compaction": ">=0.1.5-alpha.1 <0.1.6-0",
52
+ "@deepseek-ai/dsh-session": ">=0.1.5-alpha.1 <0.1.6-0",
53
+ "@deepseek-ai/dsh-llm": ">=0.1.5-alpha.1 <0.1.6-0",
54
+ "@deepseek-ai/dsh-tools": ">=0.1.5-alpha.1 <0.1.6-0",
55
+ "@deepseek-ai/dsh-settings": ">=0.1.5-alpha.1 <0.1.6-0",
56
+ "@deepseek-ai/schemastery": "^3.18.2"
54
57
  },
55
58
  "devDependencies": {
56
- "acp-kernel": "0.0.29",
57
- "@deepseek-ai/cordis": "4.0.1",
58
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
59
- "@deepseek-ai/dsh-commands": "0.1.0-rc.6",
60
- "@deepseek-ai/dsh-compaction": "0.1.0-rc.6",
61
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
62
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
63
- "@deepseek-ai/dsh-session-projection": "0.1.0-rc.6",
64
- "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.6",
65
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
66
- "@deepseek-ai/schemastery": "3.18.1",
59
+ "acp-kernel": "0.0.63",
60
+ "@deepseek-ai/cordis": "4.0.2",
61
+ "@deepseek-ai/dsh-agent": "0.1.5-rc.2",
62
+ "@deepseek-ai/dsh-agent-loop": "0.1.5-rc.2",
63
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.1.5-rc.2",
64
+ "@deepseek-ai/dsh-commands": "0.1.5-rc.2",
65
+ "@deepseek-ai/dsh-compaction": "0.1.5-rc.2",
66
+ "@deepseek-ai/dsh-llm": "0.1.5-rc.2",
67
+ "@deepseek-ai/dsh-llm-deepseek": "0.1.5-rc.2",
68
+ "@deepseek-ai/dsh-session": "0.1.5-rc.2",
69
+ "@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.5-rc.2",
70
+ "@deepseek-ai/dsh-session-projection": "0.1.5-rc.2",
71
+ "@deepseek-ai/dsh-settings": "0.1.5-rc.2",
72
+ "@deepseek-ai/dsh-token-meter": "0.1.5-rc.2",
73
+ "@deepseek-ai/dsh-tools": "0.1.5-rc.2",
74
+ "@deepseek-ai/schemastery": "3.18.2",
67
75
  "@types/node": "^26.1.2",
68
76
  "semver": "^7.7.1",
69
77
  "tsup": "^8.5.1",
@@ -1,36 +0,0 @@
1
- /**
2
- * Local tool-pairing balance checks over the session surface.
3
- *
4
- * UPSTREAM: `@deepseek-ai/dsh-compaction@0.1.2-rc.1` reads the REMOVED
5
- * `session.events` API in its balance cache — `extendCache` does
6
- * `const events = session.events` and `eventForSeq` does `events[seq]`, so on
7
- * every dsh 0.1.2 host the official `toolPairingBalancedBefore/After` helpers
8
- * throw `TypeError: Cannot read properties of undefined (reading '<seq>')`.
9
- * The host's API docs require compaction backends to use these helpers for
10
- * edge checks, and the host's own `dsh-compaction-basic` calls them too, so
11
- * ALL compaction on a 0.1.2-rc.1 host crashes (reproduced offline and pinned
12
- * in issue #124; tracked in docs/dsh-porting-verification.md).
13
- *
14
- * This module mirrors the host's algorithm line for line (per-session cache
15
- * keyed by `surface.replaceGeneration`, the `cutBalanced` fold, identical
16
- * error messages) with ONE deliberate difference: events are read through the
17
- * cross-version accessor `eventAtOf` (src/session-events.ts), which works on
18
- * both the 0.1.0/0.1.1 lines (`events[seq]`) and 0.1.2+ (`eventAt(seq)`).
19
- * DELETE this module and switch `src/region.ts` back to
20
- * `@deepseek-ai/dsh-compaction`'s helpers the moment the host fix ships.
21
- */
22
- import type { Session } from '@deepseek-ai/dsh-session';
23
- /**
24
- * Whether the cut immediately before a current surface sequence is tool-pairing balanced.
25
- * @param session - session whose surface is checked.
26
- * @param seq - event sequence whose leading cut is checked.
27
- * @returns true when no unanswered tool call crosses the cut.
28
- */
29
- export declare function toolPairingBalancedBefore(session: Session, seq: number): boolean;
30
- /**
31
- * Whether the cut immediately after a current surface sequence is tool-pairing balanced.
32
- * @param session - session whose surface is checked.
33
- * @param seq - event sequence whose trailing cut is checked.
34
- * @returns true when no unanswered tool call crosses the cut.
35
- */
36
- export declare function toolPairingBalancedAfter(session: Session, seq: number): boolean;