billion-context-dsh 0.1.5 → 0.1.6

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/README.en.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [English](./README.en.md) | [中文](./README.md)
4
4
 
5
5
  > **⚠️ Beta notice — not for production use**
6
- > This project (**v0.1.5**) is a work-in-progress beta. The [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) itself is also in **public beta**. **Do not use either in engineering / production environments** — expect breaking changes and rough edges.
6
+ > This project (**v0.1.6**) is a work-in-progress beta. The [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) itself is also in **public beta**. **Do not use either in engineering / production environments** — expect breaking changes and rough edges.
7
7
 
8
8
  <p align="center">
9
9
  <strong>Built with gratitude on top of these projects</strong> — please give them a ⭐:
@@ -73,7 +73,7 @@ That's it. Then add a composition row where a compaction backend is expected —
73
73
  - id: compaction-acp
74
74
  name: 'billion-context-dsh'
75
75
  config:
76
- modelContextLimit: 128000 # default; the pressure window
76
+ modelContextLimit: 128000 # optional; omit to auto-detect the model's real window (fallback 128000)
77
77
  ```
78
78
 
79
79
  **Per-mode — an agent preset's `compaction` realm.** First *disable (or delete) the realm's existing `dsh-compaction-basic` row*, then mount this engine — two backends cannot coexist in the same realm:
@@ -87,7 +87,7 @@ That's it. Then add a composition row where a compaction backend is expected —
87
87
  - id: compaction-acp
88
88
  name: 'billion-context-dsh'
89
89
  config:
90
- modelContextLimit: 128000 # default; the pressure window
90
+ modelContextLimit: 128000 # optional; omit to auto-detect the model's real window (fallback 128000)
91
91
  ```
92
92
 
93
93
  > **One context manager per agent.** Two backends providing `ctx.compaction` collide — never run both in the same realm. Full install & verification guide: [docs/INSTALL.md](docs/INSTALL.md).
@@ -142,10 +142,11 @@ This project reuses `acp-kernel`'s compression core and `billion-context-pi`'s d
142
142
 
143
143
  | Key | Default | Meaning |
144
144
  |---|---|---|
145
- | `modelContextLimit` | `128000` | Context window used for the kernel's pressure decisions |
146
- | `nudgeMinContextLimitPct` | kernel default `0.45` | Nudge window lower bound (usage fraction) same default as billion-context-pi |
147
- | `nudgeMaxContextLimitPct` | kernel default `0.75` | Over-limit line: above this the nudge fires regardless of growth |
148
- | `nudgeEmergencyThresholdPct` | kernel default `0.95` | Emergency nudge (bypasses the per-turn dedup) |
145
+ | `modelContextLimit` | auto-detected (fallback `128000`) | Context window used for the kernel's pressure decisions; an explicit value wins and skips the probe |
146
+ | `autoModelContextLimit` | `true` | Probe the model's real window from the model API (`agent.ctx.llm.resolveModelInfo`); fall back to the default on failure, `acp_status` shows the window source |
147
+ | `nudgeMinContextLimitPct` | kernel default `0.45` | Nudge window lower bound (usage fraction) validation only; the growth-driven trigger has no percentage floor — same default as billion-context-pi |
148
+ | `nudgeMaxContextLimitPct` | engine default `0.70` (kernel/pi default `0.75`) | Over-limit line: above this the nudge fires regardless of growth — deliberately below the host compaction-basic 80% auto-compaction line so the forced nudge fires first; an explicit value wins |
149
+ | `nudgeEmergencyThresholdPct` | engine default `0.85` (kernel/pi default `0.95`) | Emergency nudge (bypasses the per-turn dedup) — lowered from `0.95`: at 95% the model has no room to act and the 80% auto-compaction line shadows it; an explicit value wins |
149
150
  | `coreOverrides` | — | Any other acp-kernel `Config` override (billion-context-pi's `coreOverrides` escape hatch) |
150
151
  | `autoTools` | `true` | Register the four model tools on `ctx.tools` |
151
152
  | `autoCommand` | `true` | Register the `/acp` command on `ctx.commands` |
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [中文](./README.md) | [English](./README.en.md)
4
4
 
5
5
  > **⚠️ 测试版声明——请勿用于生产环境**
6
- > 本项目(**v0.1.5**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
6
+ > 本项目(**v0.1.6**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
7
7
 
8
8
  <p align="center">
9
9
  <strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
@@ -75,7 +75,7 @@ npm install billion-context-dsh
75
75
  - id: compaction-acp
76
76
  name: 'billion-context-dsh'
77
77
  config:
78
- modelContextLimit: 128000 # 默认;压力窗口
78
+ modelContextLimit: 128000 # 可选;省略时自动探测模型真实窗口(回退 128000)
79
79
  ```
80
80
 
81
81
  **单模式生效(agent preset 的 `compaction` realm)**。先在该 realm 内*禁用(或删除)原有的 `dsh-compaction-basic` 行*,再插入本引擎——同一 realm 内两个后端不能并存:
@@ -89,7 +89,7 @@ npm install billion-context-dsh
89
89
  - id: compaction-acp
90
90
  name: 'billion-context-dsh'
91
91
  config:
92
- modelContextLimit: 128000 # 默认;压力窗口
92
+ modelContextLimit: 128000 # 可选;省略时自动探测模型真实窗口(回退 128000)
93
93
  ```
94
94
 
95
95
  > **每个 agent 只留一个上下文管理器。** 两个后端同时 provide `ctx.compaction` 会冲突——同一 realm 内切勿并存。完整安装与验证指南见 [docs/INSTALL.md](docs/INSTALL.md)。
@@ -144,10 +144,11 @@ DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)
144
144
 
145
145
  | 键 | 默认值 | 含义 |
146
146
  |---|---|---|
147
- | `modelContextLimit` | `128000` | 用于内核压力决策的上下文窗口 |
148
- | `nudgeMinContextLimitPct` | 内核默认 `0.45` | Nudge 窗口下界(用量占比)——与 billion-context-pi 相同的默认值 |
149
- | `nudgeMaxContextLimitPct` | 内核默认 `0.75` | 过限线:超过此值则无论增长与否都触发 nudge |
150
- | `nudgeEmergencyThresholdPct` | 内核默认 `0.95` | 紧急 nudge(绕过每轮去重) |
147
+ | `modelContextLimit` | 自动探测(回退 `128000`) | 用于内核压力决策的上下文窗口;显式配置时优先且跳过探测 |
148
+ | `autoModelContextLimit` | `true` | 从模型 API 自动探测真实窗口(`agent.ctx.llm.resolveModelInfo`);探测失败回退默认值,`acp_status` 展示窗口来源 |
149
+ | `nudgeMinContextLimitPct` | 内核默认 `0.45` | Nudge 窗口下界(用量占比)——仅作配置校验,增长路径的触发没有百分比下限——与 billion-context-pi 相同的默认值 |
150
+ | `nudgeMaxContextLimitPct` | engine 默认 `0.70`(内核/pi 默认 `0.75`) | 过限线:超过此值则无论增长与否都触发 nudge——刻意低于宿主 compaction-basic 的 80% 自动压缩线,保证强制 nudge 先触发;显式配置优先 |
151
+ | `nudgeEmergencyThresholdPct` | engine 默认 `0.85`(内核/pi 默认 `0.95`) | 紧急 nudge(绕过每轮去重)——从 `0.95` 下调:95% 时模型已无操作空间且会被 80% 自动压缩线遮蔽;显式配置优先 |
151
152
  | `coreOverrides` | — | 任何其他 acp-kernel `Config` 覆盖(billion-context-pi 的 `coreOverrides` 逃生口) |
152
153
  | `autoTools` | `true` | 在 `ctx.tools` 注册四个模型工具 |
153
154
  | `autoCommand` | `true` | 在 `ctx.commands` 注册 `/acp` 命令 |
package/dist/config.d.ts CHANGED
@@ -7,15 +7,19 @@
7
7
  * growth ratio 5%, protected last messages 5). Every knob is optional — an
8
8
  * omitted value keeps the kernel default, so the behavior matches the Pi
9
9
  * adapter exactly unless a deployment opts out.
10
+ *
11
+ * NOTE: `AcpCompactionEngine` (src/index.ts) ships its own engine-level
12
+ * defaults 0.70/0.85 for the two nudge thresholds on top of this layer, so an
13
+ * engine with no explicit config lands on 0.70/0.85, not 0.75/0.95.
10
14
  * @module billion-context-dsh/config
11
15
  */
12
16
  import { type Config } from 'acp-kernel';
13
17
  /** The kernel-facing knobs shared by the nudge path and the compress tool. */
14
18
  export interface KernelConfigInput {
15
19
  readonly modelContextLimit: number;
16
- /** Nudge window lower bound (usage fraction). Kernel default: 0.45. */
20
+ /** Nudge window lower bound (usage fraction; validation only — the growth-driven trigger has no percentage floor). Kernel default: 0.45. */
17
21
  readonly nudgeMinContextLimitPct?: number;
18
- /** Nudge window upper bound — over-limit. Kernel default: 0.75. */
22
+ /** Nudge window upper bound — over-limit guarantee line. Kernel default: 0.75. */
19
23
  readonly nudgeMaxContextLimitPct?: number;
20
24
  /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default: 0.95. */
21
25
  readonly nudgeEmergencyThresholdPct?: number;
package/dist/index.d.ts CHANGED
@@ -28,23 +28,44 @@
28
28
  import type { Context } from '@deepseek-ai/cordis';
29
29
  import { CompactionEngine, type CompactionAgentContext, type CompactionResult, type CompactionTrigger, type ManualCompactAgentContext } from '@deepseek-ai/dsh-compaction';
30
30
  import { type CompressionCore } from 'acp-kernel';
31
+ import type { Agent } from '@deepseek-ai/dsh-agent';
31
32
  import { AcpStateStore } from './state.ts';
33
+ import { type AcpWindow } from './window.ts';
32
34
  export { AcpStateStore } from './state.ts';
33
35
  export { kernelConfigFor, type KernelConfigInput } from './config.ts';
34
36
  export { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts';
35
37
  export { makeTools, type ToolEnvironment } from './tools.ts';
36
38
  export { acpCommand } from './commands.ts';
37
39
  export { buildNudge, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts';
40
+ export { DEFAULT_CONTEXT_WINDOW, detectContextWindow, windowSourceLabel, type AcpWindow, } from './window.ts';
38
41
  export { rebuildBlockLedger, resolveSurfaceRange, runCompactionTransaction, shadowedSeqsOf, findOpenTurn, assertNoActiveCompaction, blockRegistry, blockRefForSummarySeq, compactionIdsOfKernelBlocks, summarySeqOfKernelBlock, expandShadowedSeqs, type AcpBlockLedgerEntry, type CompactionTransactionInput, } from './region.ts';
39
42
  export { eventsToCoreMessages, projectEvent, surfaceEventsOf, extractEventText } from './messages.ts';
40
43
  export interface AcpConfig {
41
- /** The context window used for pressure decisions. Default 128000. */
42
- readonly modelContextLimit: number;
43
- /** Nudge window lower bound (usage fraction). Kernel default 0.45 same as billion-context-pi. */
44
+ /**
45
+ * The context window used for pressure decisions, in tokens. When omitted,
46
+ * `autoModelContextLimit` (default true) probes the model's real window via
47
+ * `agent.ctx.llm.resolveModelInfo(provider, model)`; an explicit value
48
+ * always wins and disables the probe.
49
+ */
50
+ readonly modelContextLimit?: number;
51
+ /** Probe the model's real context window from the LLM runtime. Default true. */
52
+ readonly autoModelContextLimit: boolean;
53
+ /** Nudge window lower bound (usage fraction; validation only — the growth-driven trigger has no percentage floor). Kernel default 0.45 — same as billion-context-pi. */
44
54
  readonly nudgeMinContextLimitPct?: number;
45
- /** Nudge window upper bound (over-limit). Kernel default 0.75 — same as billion-context-pi. */
55
+ /**
56
+ * Nudge window upper bound — over-limit guarantee line: above this the
57
+ * kernel injects a nudge regardless of growth or cadence. Engine default
58
+ * 0.70 (deliberately BELOW the kernel/billion-context-pi default 0.75 and
59
+ * the host compaction-basic auto-compaction line 0.80, so the forced nudge
60
+ * always fires first); an explicit value wins.
61
+ */
46
62
  readonly nudgeMaxContextLimitPct?: number;
47
- /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default 0.95 — same as billion-context-pi. */
63
+ /**
64
+ * Emergency nudge threshold (bypasses the per-turn dedup). Engine default
65
+ * 0.85 (down from the kernel/billion-context-pi default 0.95: 95% leaves
66
+ * the model no room to act before the API rejects, and the host's 80%
67
+ * compaction-basic line shadows it in standard/code/cordis modes).
68
+ */
48
69
  readonly nudgeEmergencyThresholdPct?: number;
49
70
  /** Any other acp-kernel Config override (billion-context-pi's `coreOverrides` escape hatch). */
50
71
  readonly coreOverrides?: Partial<import('acp-kernel').Config>;
@@ -69,7 +90,17 @@ export declare class AcpCompactionEngine extends CompactionEngine {
69
90
  /** Resolved engine configuration. */
70
91
  readonly config: AcpConfig;
71
92
  private readonly lastNudgeTurn;
93
+ /** Per provider/model route the resolved window (probe failures cached too). */
94
+ private readonly windowCache;
72
95
  constructor(ctx: Context, config?: Partial<AcpConfig>);
96
+ /**
97
+ * Resolve the effective context window for an agent. An explicitly
98
+ * configured `modelContextLimit` always wins (no probe). Otherwise probe the
99
+ * model's real window via `agent.ctx.llm.resolveModelInfo` (cached per
100
+ * provider/model route, probe failures cached too) and fall back to
101
+ * DEFAULT_CONTEXT_WINDOW when auto-detection is disabled or unavailable.
102
+ */
103
+ windowFor(agent: Agent): Promise<AcpWindow>;
73
104
  /** ACP is model-driven: automatic pressure policy never summarizes by itself. */
74
105
  compactIfNeeded(_agent: CompactionAgentContext, _trigger: CompactionTrigger, signal: AbortSignal): Promise<CompactionResult | null>;
75
106
  /** Explicit idle-session compaction: ACP leaves the decision to the model. */
package/dist/index.js CHANGED
@@ -528,6 +528,28 @@ function kernelConfigFor(input) {
528
528
  return defaultConfig(input.modelContextLimit, overrides);
529
529
  }
530
530
 
531
+ // src/window.ts
532
+ var DEFAULT_CONTEXT_WINDOW = 128e3;
533
+ function windowSourceLabel(window) {
534
+ if (window.source === "explicit") return "configured";
535
+ if (window.source === "auto") {
536
+ return `auto-detected from ${window.provider ?? "?"}/${window.model ?? "?"}`;
537
+ }
538
+ return "default (auto-detection unavailable)";
539
+ }
540
+ async function detectContextWindow(agent, provider, model) {
541
+ const llm = agent.ctx?.get?.("llm");
542
+ if (llm?.resolveModelInfo === void 0) return null;
543
+ try {
544
+ const info = await llm.resolveModelInfo(provider, model);
545
+ const window = info?.context?.contextWindow;
546
+ if (typeof window === "number" && Number.isInteger(window) && window > 0) return window;
547
+ return null;
548
+ } catch {
549
+ return null;
550
+ }
551
+ }
552
+
531
553
  // src/tools.ts
532
554
  function textOutput() {
533
555
  return {
@@ -746,18 +768,21 @@ ${original}`.toLowerCase();
746
768
  };
747
769
  }
748
770
  var statusParameters = {};
749
- function handleStatus(env, _args, exec) {
750
- const session = requireAgent(exec).session;
771
+ async function handleStatus(env, _args, exec) {
772
+ const agent = requireAgent(exec);
773
+ const session = agent.session;
751
774
  const ledger = rebuildBlockLedger(session.events);
752
775
  const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0);
753
776
  const coreMessages = eventsToCoreMessages(surfaceEventsOf(session));
754
777
  const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens2(message.text ?? ""), 0);
755
- const limit = env.modelContextLimit;
778
+ const window = env.windowFor === void 0 ? { limit: env.modelContextLimit, source: "explicit" } : await env.windowFor(agent);
779
+ const limit = window.limit;
756
780
  const lines = [
757
781
  `ACP status \u2014 session ${session.id}`,
758
782
  ` blocks: ${ledger.length}`,
759
783
  ` tokens compressed: ${totalTokens}`,
760
784
  ` estimated context: ${estimated} / ${limit} (${Math.round(estimated / limit * 100)}%)`,
785
+ ` context window: ${limit} (${windowSourceLabel(window)})`,
761
786
  ` surface: ${surfaceSummary(session)}`
762
787
  ];
763
788
  for (const block of ledger.slice(0, 10)) {
@@ -800,7 +825,7 @@ function makeTools(env) {
800
825
  parameters: statusParameters,
801
826
  output: textOutput(),
802
827
  execute(args, exec) {
803
- return Promise.resolve(handleStatus(env, args, exec));
828
+ return handleStatus(env, args, exec);
804
829
  }
805
830
  })
806
831
  ];
@@ -808,18 +833,20 @@ function makeTools(env) {
808
833
 
809
834
  // src/commands.ts
810
835
  import { defaultCountTokens as defaultCountTokens3 } from "acp-kernel";
811
- function statusText(env, agent) {
836
+ async function statusText(env, agent) {
812
837
  const session = agent.session;
813
838
  const ledger = rebuildBlockLedger(session.events);
814
839
  const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0);
815
840
  const coreMessages = eventsToCoreMessages(surfaceEventsOf(session));
816
841
  const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens3(message.text ?? ""), 0);
817
- const limit = env.modelContextLimit;
842
+ const window = env.windowFor === void 0 ? { limit: env.modelContextLimit, source: "explicit" } : await env.windowFor(agent);
843
+ const limit = window.limit;
818
844
  const lines = [
819
845
  `ACP status \u2014 session ${session.id}`,
820
846
  ` blocks: ${ledger.length}`,
821
847
  ` tokens compressed: ${totalTokens}`,
822
- ` estimated context: ${estimated} / ${limit} (${Math.round(estimated / limit * 100)}%)`
848
+ ` estimated context: ${estimated} / ${limit} (${Math.round(estimated / limit * 100)}%)`,
849
+ ` context window: ${limit} (${windowSourceLabel(window)})`
823
850
  ];
824
851
  for (const block of ledger.slice(0, 10)) {
825
852
  const tier = block.tier > 1 ? ` [T${block.tier}]` : "";
@@ -877,7 +904,7 @@ function acpCommand(env) {
877
904
  handler: async (invocation) => {
878
905
  const raw = invocation.rawInput.trim();
879
906
  if (raw === "" || raw === "status") {
880
- return { kind: "success", text: statusText(env, invocation.agent) };
907
+ return { kind: "success", text: await statusText(env, invocation.agent) };
881
908
  }
882
909
  if (raw.startsWith("compress")) {
883
910
  return { kind: "success", text: compressText(env, invocation.agent, raw.slice("compress".length).trim().split(/\s+/)) };
@@ -975,10 +1002,17 @@ var ACP_SYSTEM_PROMPT_ORDER = 150;
975
1002
 
976
1003
  // src/index.ts
977
1004
  var DEFAULT_CONFIG = {
978
- modelContextLimit: 128e3,
1005
+ autoModelContextLimit: true,
979
1006
  autoTools: true,
980
1007
  autoCommand: true,
981
- autoNudge: true
1008
+ autoNudge: true,
1009
+ // Nudge thresholds: engine defaults 0.70/0.85 — deliberately below the
1010
+ // kernel/billion-context-pi 0.75/0.95. 0.95 leaves no room to act before
1011
+ // the API rejects, and the host's compaction-basic line (thresholdRatio
1012
+ // 0.80) shadows it in standard/code/cordis modes; 0.70 keeps the forced
1013
+ // over-limit nudge ahead of that 80% line. Explicit values always win.
1014
+ nudgeMaxContextLimitPct: 0.7,
1015
+ nudgeEmergencyThresholdPct: 0.85
982
1016
  };
983
1017
  function resolveAcpConfig(config = {}) {
984
1018
  return { ...DEFAULT_CONFIG, ...config };
@@ -991,6 +1025,8 @@ var AcpCompactionEngine = class extends CompactionEngine {
991
1025
  /** Resolved engine configuration. */
992
1026
  config;
993
1027
  lastNudgeTurn = /* @__PURE__ */ new Map();
1028
+ /** Per provider/model route the resolved window (probe failures cached too). */
1029
+ windowCache = /* @__PURE__ */ new Map();
994
1030
  constructor(ctx, config = {}) {
995
1031
  super(ctx);
996
1032
  this.config = resolveAcpConfig(config);
@@ -999,11 +1035,13 @@ var AcpCompactionEngine = class extends CompactionEngine {
999
1035
  const env = {
1000
1036
  kernel: this.kernel,
1001
1037
  store: this.store,
1002
- modelContextLimit: this.config.modelContextLimit,
1038
+ // Initial value before any probe; windowFor() replaces it per pre-step.
1039
+ modelContextLimit: this.config.modelContextLimit ?? DEFAULT_CONTEXT_WINDOW,
1003
1040
  nudgeMinContextLimitPct: this.config.nudgeMinContextLimitPct,
1004
1041
  nudgeMaxContextLimitPct: this.config.nudgeMaxContextLimitPct,
1005
1042
  nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,
1006
- coreOverrides: this.config.coreOverrides
1043
+ coreOverrides: this.config.coreOverrides,
1044
+ windowFor: (agent) => this.windowFor(agent)
1007
1045
  };
1008
1046
  const tools = ctx.get("tools");
1009
1047
  if (tools !== void 0) {
@@ -1041,7 +1079,8 @@ var AcpCompactionEngine = class extends CompactionEngine {
1041
1079
  ctx.on("agent/pre-step", async (payload, next) => {
1042
1080
  const decision = await next();
1043
1081
  if (decision.kind === "reject") return decision;
1044
- const outcome = buildNudge(payload.agent, env, this.lastNudgeTurn);
1082
+ const window = await this.windowFor(payload.agent);
1083
+ const outcome = buildNudge(payload.agent, { ...env, modelContextLimit: window.limit }, this.lastNudgeTurn);
1045
1084
  if (outcome === null) return decision;
1046
1085
  return { kind: "enter", messages: [...decision.messages, outcome.message] };
1047
1086
  });
@@ -1055,6 +1094,32 @@ var AcpCompactionEngine = class extends CompactionEngine {
1055
1094
  });
1056
1095
  }
1057
1096
  }
1097
+ /**
1098
+ * Resolve the effective context window for an agent. An explicitly
1099
+ * configured `modelContextLimit` always wins (no probe). Otherwise probe the
1100
+ * model's real window via `agent.ctx.llm.resolveModelInfo` (cached per
1101
+ * provider/model route, probe failures cached too) and fall back to
1102
+ * DEFAULT_CONTEXT_WINDOW when auto-detection is disabled or unavailable.
1103
+ */
1104
+ async windowFor(agent) {
1105
+ if (this.config.modelContextLimit !== void 0) {
1106
+ return { limit: this.config.modelContextLimit, source: "explicit" };
1107
+ }
1108
+ const provider = agent.options.provider ?? "";
1109
+ const model = agent.options.model ?? "";
1110
+ const key = `${provider}\0${model}`;
1111
+ const cached = this.windowCache.get(key);
1112
+ if (cached !== void 0) return cached;
1113
+ let window;
1114
+ if (!this.config.autoModelContextLimit) {
1115
+ window = { limit: DEFAULT_CONTEXT_WINDOW, source: "default", provider, model };
1116
+ } else {
1117
+ const detected = await detectContextWindow(agent, provider, model);
1118
+ window = detected === null ? { limit: DEFAULT_CONTEXT_WINDOW, source: "default", provider, model } : { limit: detected, source: "auto", provider, model };
1119
+ }
1120
+ this.windowCache.set(key, window);
1121
+ return window;
1122
+ }
1058
1123
  /** ACP is model-driven: automatic pressure policy never summarizes by itself. */
1059
1124
  async compactIfNeeded(_agent, _trigger, signal) {
1060
1125
  signal.throwIfAborted();
@@ -1084,6 +1149,7 @@ export {
1084
1149
  ACP_SYSTEM_PROMPT_ORDER,
1085
1150
  AcpCompactionEngine,
1086
1151
  AcpStateStore,
1152
+ DEFAULT_CONTEXT_WINDOW,
1087
1153
  acpCommand,
1088
1154
  assertNoActiveCompaction,
1089
1155
  blockRefForSummarySeq,
@@ -1091,6 +1157,7 @@ export {
1091
1157
  buildNudge,
1092
1158
  compactionIdsOfKernelBlocks,
1093
1159
  index_default as default,
1160
+ detectContextWindow,
1094
1161
  eventsToCoreMessages,
1095
1162
  expandShadowedSeqs,
1096
1163
  extractEventText,
@@ -1104,6 +1171,7 @@ export {
1104
1171
  runCompactionTransaction,
1105
1172
  shadowedSeqsOf,
1106
1173
  summarySeqOfKernelBlock,
1107
- surfaceEventsOf
1174
+ surfaceEventsOf,
1175
+ windowSourceLabel
1108
1176
  };
1109
1177
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/state.ts","../src/region.ts","../src/messages.ts","../src/tools.ts","../src/config.ts","../src/commands.ts","../src/nudge.ts","../src/system-prompt.ts"],"sourcesContent":["/**\n * billion-context-dsh — Active Context Pruning (ACP) for the DeepSeek Harness,\n * delivered as a `CompactionEngine` backend.\n *\n * The model decides when and what to compress (pure ACP semantics):\n * - the `compress` tool durably shadows a surface range with the model-written\n * summary (no second LLM summarization call — the ACP cost win);\n * - the original events stay in the append-only session log, so `decompress`,\n * `search_context`, and replay always work;\n * - refs are surface seqs carried by the injected nudge's range table (DSH\n * has no in-memory message rewrite hook — see docs/dsh-porting-verification.md);\n * - automatic policy never summarizes by itself: it nudges the model.\n *\n * Mount it wherever a compaction backend is expected:\n *\n * ```yaml\n * - id: compaction-billion-context\n * name: 'billion-context-dsh'\n * config:\n * modelContextLimit: 128000\n * ```\n *\n * The package registers `ctx.compaction` plus the four model tools and the\n * `/acp` command when the hosting composition provides `ctx.tools` /\n * `ctx.commands`.\n * @module billion-context-dsh\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n CompactionEngine,\n ManualCompactionError,\n type CompactionAgentContext,\n type CompactionResult,\n type CompactionTrigger,\n type ManualCompactAgentContext,\n} from '@deepseek-ai/dsh-compaction'\nimport { createCore, type CompressionCore } from 'acp-kernel'\nimport type {} from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { makeTools, type ToolEnvironment } from './tools.ts'\nimport { acpCommand } from './commands.ts'\nimport { buildNudge } from './nudge.ts'\nimport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\n\nexport { AcpStateStore } from './state.ts'\nexport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nexport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\nexport { makeTools, type ToolEnvironment } from './tools.ts'\nexport { acpCommand } from './commands.ts'\nexport { buildNudge, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts'\nexport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n findOpenTurn,\n assertNoActiveCompaction,\n blockRegistry,\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n summarySeqOfKernelBlock,\n expandShadowedSeqs,\n type AcpBlockLedgerEntry,\n type CompactionTransactionInput,\n} from './region.ts'\nexport { eventsToCoreMessages, projectEvent, surfaceEventsOf, extractEventText } from './messages.ts'\n\nexport interface AcpConfig {\n /** The context window used for pressure decisions. Default 128000. */\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction). Kernel default 0.45 — same as billion-context-pi. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound (over-limit). Kernel default 0.75 — same as billion-context-pi. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default 0.95 — same as billion-context-pi. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (billion-context-pi's `coreOverrides` escape hatch). */\n readonly coreOverrides?: Partial<import('acp-kernel').Config>\n /** Register the four model tools on `ctx.tools`. Default true. */\n readonly autoTools: boolean\n /** Register the `/acp` command on `ctx.commands`. Default true. */\n readonly autoCommand: boolean\n /** Inject the nudge into `agent/pre-step` when the kernel recommends it. Default true. */\n readonly autoNudge: boolean\n}\n\nconst DEFAULT_CONFIG: AcpConfig = {\n modelContextLimit: 128000,\n autoTools: true,\n autoCommand: true,\n autoNudge: true,\n}\n\nexport function resolveAcpConfig(config: Partial<AcpConfig> = {}): AcpConfig {\n return { ...DEFAULT_CONFIG, ...config }\n}\n\n/**\n * The ACP compaction backend. Subclasses the seam exactly like\n * `dsh-compaction-basic`; swaps summarization-driven compaction for\n * model-driven block compression without touching the agent loop.\n */\nexport class AcpCompactionEngine extends CompactionEngine {\n /** The framework-agnostic ACP compression core, reused verbatim. */\n readonly kernel: CompressionCore\n /** Per-session kernel state. */\n readonly store: AcpStateStore\n /** Resolved engine configuration. */\n readonly config: AcpConfig\n\n private readonly lastNudgeTurn = new Map<string, number>()\n\n constructor(ctx: Context, config: Partial<AcpConfig> = {}) {\n super(ctx)\n this.config = resolveAcpConfig(config)\n this.kernel = createCore({})\n this.store = new AcpStateStore()\n\n const env: ToolEnvironment = {\n kernel: this.kernel,\n store: this.store,\n modelContextLimit: this.config.modelContextLimit,\n nudgeMinContextLimitPct: this.config.nudgeMinContextLimitPct,\n nudgeMaxContextLimitPct: this.config.nudgeMaxContextLimitPct,\n nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,\n coreOverrides: this.config.coreOverrides,\n }\n\n // Tools and commands may not be registered yet on cold start: cordis\n // starts unrelated composition rows concurrently, so the first\n // `ctx.get('tools')` can legitimately be undefined even though the row\n // ships later in the file. HMR-style reloads always see them (already\n // present), but a fresh process races — the tools silently vanished on\n // restart. Register eagerly, then re-attempt when the service appears\n // (`internal/service`) or the app finishes booting (`ready`); guard so a\n // late callback never double-registers.\n const tools = ctx.get('tools')\n if (tools !== undefined) {\n for (const tool of makeTools(env)) tools.register(tool)\n } else {\n let done = false\n const registerTools = (): void => {\n if (done) return\n const registry = ctx.get('tools')\n if (registry === undefined) return\n done = true\n for (const tool of makeTools(env)) registry.register(tool)\n }\n ctx.on('internal/service', (name: unknown) => {\n if (name === 'tools') registerTools()\n })\n }\n const commands = ctx.get('commands')\n if (commands !== undefined) {\n commands.register(acpCommand(env))\n } else {\n let done = false\n const registerCommand = (): void => {\n if (done) return\n const registry = ctx.get('commands')\n if (registry === undefined) return\n done = true\n registry.register(acpCommand(env))\n }\n ctx.on('internal/service', (name: unknown) => {\n if (name === 'commands') registerCommand()\n })\n }\n if (this.config.autoNudge) {\n ctx.on('agent/pre-step', async (payload, next) => {\n const decision = await next()\n if (decision.kind === 'reject') return decision\n const outcome = buildNudge(payload.agent, env, this.lastNudgeTurn)\n if (outcome === null) return decision\n return { kind: 'enter', messages: [...decision.messages, outcome.message] }\n })\n }\n // The load-bearing ACP guidance lives in the system prompt ONCE; nudges\n // stay short and advisory (model-driven: the model decides).\n const systemPrompt = ctx.get('systemPrompt')\n if (systemPrompt !== undefined) {\n systemPrompt.section({\n name: 'billion-context-dsh',\n order: ACP_SYSTEM_PROMPT_ORDER,\n text: ACP_SYSTEM_PROMPT,\n })\n }\n }\n\n /** ACP is model-driven: automatic pressure policy never summarizes by itself. */\n override async compactIfNeeded(\n _agent: CompactionAgentContext,\n _trigger: CompactionTrigger,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /** Explicit idle-session compaction: ACP leaves the decision to the model. */\n override async compactNow(\n _agent: ManualCompactAgentContext,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /**\n * The model-driven path lands through the `compress` tool, which runs the\n * full durable transaction directly. This seam method rejects with guidance:\n * automatic summarization is exactly what ACP replaces.\n */\n override async compactRegion(\n _start: number,\n _end: number,\n _agent: CompactionAgentContext,\n signal?: AbortSignal,\n ): Promise<CompactionResult> {\n signal?.throwIfAborted()\n throw new ManualCompactionError(\n 'summary',\n 'billion-context-dsh is model-driven: use the compress tool instead of automatic summarization',\n )\n }\n}\n\nexport default AcpCompactionEngine\n","/**\n * M2 — per-session ACP kernel state.\n *\n * The in-memory map holds the exact acp-kernel `CompressionState` while a\n * session is live. Durability does not rely on a sidecar file: every durable\n * compression writes a `compaction/summary` event whose shadowed range and\n * summary re-derive the block ledger (`rebuildBlockLedger` in region.ts), so a\n * restarted engine can answer decompress/search/status from the session log\n * alone — DSH's \"log is the source of truth\" model.\n *\n * Tier-2/3 distillation additionally requires the kernel state to KNOW the\n * blocks: `syncBlocks` deactivates a block whose consumed messages are absent\n * from the message array, and `resolveBoundaries` refuses to anchor a block\n * ref it cannot find — so on first access for a session that already has\n * durable blocks (e.g. after a server restart), the kernel blocks are\n * REHYDRATED from the ledger before use. Live updates continue through `set`.\n * @module billion-context-dsh/state\n */\n\nimport type { Session, SessionEvent } from '@deepseek-ai/dsh-session'\nimport { createInitialState, type CompressionBlock, type CompressionState } from 'acp-kernel'\nimport { rebuildBlockLedger } from './region.ts'\n\n/** Rebuild kernel `CompressionBlock`s from the durable ledger (no kernel run needed). */\nfunction rebuildKernelBlocks(events: readonly SessionEvent[]): CompressionBlock[] {\n const ledger = rebuildBlockLedger(events)\n if (ledger.length === 0) return []\n // Durable compactionId → kernel block ref (bN), recorded or synthesised.\n const kernelIdOf = new Map<string, string>()\n const parentKernelIds = new Map<string, string[]>()\n let next = 1\n for (const entry of ledger) {\n let kernelBlockId: string\n if (entry.kernelBlockId !== undefined && /^b\\d+$/.test(entry.kernelBlockId)) {\n kernelBlockId = entry.kernelBlockId\n const num = Number(kernelBlockId.slice(1))\n if (Number.isInteger(num)) next = Math.max(next, num + 1)\n } else {\n kernelBlockId = `b${next}`\n next += 1\n }\n kernelIdOf.set(entry.blockId, kernelBlockId)\n parentKernelIds.set(\n entry.blockId,\n entry.parentBlockIds\n .map((parent) => kernelIdOf.get(parent))\n .filter((id): id is string => id !== undefined),\n )\n }\n const consumed = new Set<string>()\n for (const entry of ledger) {\n for (const parent of entry.parentBlockIds) consumed.add(parent)\n }\n const blocks: CompressionBlock[] = []\n for (const entry of ledger) {\n const blockId = kernelIdOf.get(entry.blockId)!\n // The kernel anchors a block by its effectiveMessageIds. Since the tier\n // feature, the transaction records the kernel block's raw coverage\n // (direct/effective message ids) verbatim, so rehydration is faithful —\n // a tier-2 block's coverage is its parents' ORIGINALS, not the checkpoint\n // node it shadows. Legacy blocks fall back to the shadowed seqs (tier 1)\n // or the checkpoint node (tier > 1; multi-tool-call assistant messages in\n // legacy blocks lose bare-seq coverage — a documented legacy limitation).\n const direct = entry.directMessageIds ?? [...entry.shadowedSeqs.map(String)]\n const effective = entry.effectiveMessageIds\n ?? (entry.tier > 1\n ? (entry.summarySeq === undefined ? [...entry.shadowedSeqs.map(String)] : [String(entry.summarySeq)])\n : [...entry.shadowedSeqs.map(String)])\n blocks.push({\n blockId,\n runId: `r${blocks.length + 1}`,\n tier: entry.tier,\n summary: entry.summary,\n directMessageIds: [...direct],\n effectiveMessageIds: [...effective],\n directBlockIds: parentKernelIds.get(entry.blockId) ?? [],\n compressedTokens: entry.shadowedTokenCount,\n createdAt: entry.createdAt,\n survivedCount: 0,\n generation: 'young',\n active: !consumed.has(entry.blockId),\n })\n }\n return blocks\n}\n\n/** The next kernel block id after the rehydrated blocks (or the initial 1). */\nfunction nextBlockIdAfter(events: readonly SessionEvent[]): number {\n const blocks = rebuildKernelBlocks(events)\n let max = 0\n for (const block of blocks) {\n const num = Number(block.blockId.slice(1))\n if (Number.isInteger(num)) max = Math.max(max, num)\n }\n return max + 1\n}\n\nexport class AcpStateStore {\n private readonly states = new Map<string, CompressionState>()\n\n /** Kernel state for one session, initialised on first access. */\n stateFor(session: Session): CompressionState {\n const id = session.id\n const existing = this.states.get(id)\n if (existing !== undefined) return existing\n const state = createInitialState()\n if (session.events.some((event) => event.type === 'compaction/summary')) {\n state.blocks = rebuildKernelBlocks(session.events)\n state.nextBlockId = nextBlockIdAfter(session.events)\n }\n this.states.set(id, state)\n return state\n }\n\n set(session: Session, state: CompressionState): void {\n this.states.set(session.id, state)\n }\n\n delete(session: Session): void {\n this.states.delete(session.id)\n }\n}\n","/**\n * M5 — durable region transaction and the log-rebuilt block ledger.\n *\n * Modeled on `dsh-compaction-basic/src/region.ts` (which is package-internal\n * and not exported by the seam): validate the surface range and tool-call/result\n * pairing, take the durable `compaction/start` lock, record `compaction/summary`\n * as the shadow price, land the `user/message` surface replacement carrying the\n * summary under `compactCheckpointSource`, and release the lock with\n * `compaction/end`. The original events stay in the append-only log, so\n * decompress/search/status can rebuild everything from the log.\n * @module billion-context-dsh/region\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { Session, SessionEvent, SessionEventMap } from '@deepseek-ai/dsh-session'\nimport {\n CompactionId,\n compactCheckpointSource,\n toolPairingBalancedAfter,\n toolPairingBalancedBefore,\n} from '@deepseek-ai/dsh-compaction'\nimport { createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'\nimport { defaultCountTokens } from 'acp-kernel'\nimport { extractEventText, extractText } from './messages.ts'\n\n/** One durable ACP block as rebuilt from the session log. */\nexport interface AcpBlockLedgerEntry {\n /** The compaction transaction id (stable block identity). */\n readonly blockId: string\n readonly summary: string\n readonly shadowedSeqs: readonly number[]\n readonly shadowedTokenCount: number\n readonly start: number\n readonly end: number\n /** Compression tier: 1 (message range), 2 (distills tier-1 blocks), 3 (distills tier-2 blocks). Legacy blocks default to 1. */\n readonly tier: 1 | 2 | 3\n /** Compaction ids of the blocks this block distilled (parents). Empty for tier-1 blocks. */\n readonly parentBlockIds: readonly string[]\n /** The acp-kernel block id (`bN`) created for this transaction — absent for legacy blocks (synthesised by order). */\n readonly kernelBlockId?: string\n /** The surface seq of this block's checkpoint summary node (derived from the log; null when the node is gone). */\n readonly summarySeq?: number\n /** The kernel block's raw direct/effective message ids at creation (recorded since the tier feature; absent for legacy). */\n readonly directMessageIds?: readonly string[]\n readonly effectiveMessageIds?: readonly string[]\n /** Unix epoch ms of the compaction/summary event. */\n readonly createdAt: number\n}\n\n/** The open turn number, or null when the log ends between turns. */\nexport function findOpenTurn(events: readonly SessionEvent[]): number | null {\n let open: number | null = null\n for (const event of events) {\n if (event.type === 'turn/start') open = event.data.turn\n else if (event.type === 'turn/end' && event.data.turn === open) open = null\n }\n return open\n}\n\n/** Reject a second concurrent compaction for the same session. */\nexport function assertNoActiveCompaction(events: readonly SessionEvent[]): void {\n let active = false\n for (const event of events) {\n if (event.type === 'compaction/start') active = true\n else if (event.type === 'compaction/end') active = false\n }\n if (active) {\n throw new Error('billion-context-dsh: another compaction is already active for this session')\n }\n}\n\n/**\n * Whether the surface node at `seq` projects to CoreMessage(s) whose ref key\n * is the bare seq — user messages, tool results, and text-only or SINGLE\n * tool-call assistant messages all do. Multi-tool-call assistant messages\n * project to `${seq}#${callId}` ids (projectEvent) and therefore carry NO\n * bare-`${seq}` ref, so compress's byRaw lookup can never resolve them as\n * range edges. resolveSurfaceRange treats such edges as unbalanced and shifts\n * them to the nearest clean cut.\n */\nfunction hasPlainRef(session: Session, seq: number): boolean {\n const event = session.events[seq]\n if (event === undefined) return false\n switch (event.type) {\n case 'user/message':\n case 'tool/result':\n return extractEventText(event).trim().length > 0\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = Array.isArray(content)\n ? content.filter(\n (block) => block !== null && typeof block === 'object' && (block as { type?: string }).type === 'tool-call',\n )\n : []\n if (calls.length > 1) return false\n // One tool-call: projectEvent emits a bare-seq CoreMessage unconditionally.\n // Zero: only when the text is non-empty.\n return calls.length === 1 || extractEventText(event).trim().length > 0\n }\n default:\n return false\n }\n}\n\n/**\n * Validate one inclusive surface span and adjust its edges to a\n * tool-pairing-balanced range whose boundaries carry a bare-seq ref. Missing\n * or reversed ranges still throw. An edge that sits inside a tool-call/result\n * pair — or on a multi-tool-call assistant message that has no bare-seq ref —\n * is first nudged inward to the nearest clean cut; if that collapses the range\n * (e.g. the model asked for a SINGLE tool result, which can never be balanced\n * alone), the range EXPANDS outward to the enclosing clean pair instead — a\n * lone tool message is almost always a \"consumed output\" the model genuinely\n * wants to compress. The returned range is what a caller should actually shadow.\n */\nexport function resolveSurfaceRange(\n session: Session,\n start: number,\n end: number,\n): { start: number; end: number } {\n const nodes = session.surface.nodes\n const requestedStartIdx = nodes.indexOf(start)\n const requestedEndIdx = nodes.indexOf(end)\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} not in the current surface — `\n + 'surface seqs are sparse message nodes (only user/message, assistant/message, '\n + 'tool/result events); consult acp_status for the current surface range',\n )\n }\n if (requestedStartIdx > requestedEndIdx) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n // Belt-and-braces: the surface can be locally out of order after surface\n // replacements, so index order alone does not guarantee value order.\n if (start > end) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n // A boundary must be BOTH tool-pairing-balanced AND carry a bare-seq ref.\n const cleanBefore = (index: number): boolean =>\n toolPairingBalancedBefore(session, nodes[index]!) && hasPlainRef(session, nodes[index]!)\n const cleanAfter = (index: number): boolean =>\n toolPairingBalancedAfter(session, nodes[index]!) && hasPlainRef(session, nodes[index]!)\n let startIdx = requestedStartIdx\n let endIdx = requestedEndIdx\n // First pass: nudge inward to the nearest clean cuts.\n while (startIdx <= endIdx && !cleanBefore(startIdx)) {\n startIdx += 1\n }\n while (endIdx >= startIdx && !cleanAfter(endIdx)) {\n endIdx -= 1\n }\n if (startIdx <= endIdx && nodes[startIdx]! <= nodes[endIdx]!) {\n return { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n // Second pass: the inward pass collapsed (a lone tool message) — expand\n // outward from the REQUESTED span to the smallest clean enclosing pair.\n startIdx = requestedStartIdx\n endIdx = requestedEndIdx\n while (startIdx > 0 && !cleanBefore(startIdx)) {\n startIdx -= 1\n }\n while (endIdx < nodes.length - 1 && !cleanAfter(endIdx)) {\n endIdx += 1\n }\n // Value order guard: the surface is locally non-monotonic after replacements\n // (a checkpoint seq inserted ahead of older residual nodes), so index order\n // alone is not enough — never return a span whose end seq is numerically\n // BEFORE its start seq. The caller (nudge / compress) skips such a span.\n if (cleanBefore(startIdx) && cleanAfter(endIdx) && nodes[startIdx]! <= nodes[endIdx]!) {\n return { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n throw new Error(\n `billion-context-dsh: no tool-pairing-balanced range around seq ${start}..${end} — `\n + 'narrow the range or consult acp_status for the current surface',\n )\n}\n\n/** The surface seqs shadowed by the inclusive positional span. */\nexport function shadowedSeqsOf(session: Session, start: number, end: number): number[] {\n const nodes = session.surface.nodes\n const startIdx = nodes.indexOf(start)\n const endIdx = nodes.indexOf(end)\n return nodes.slice(startIdx, endIdx + 1)\n}\n\nexport interface CompactionTransactionInput {\n readonly start: number\n readonly end: number\n readonly shadowedSeqs: readonly number[]\n readonly summary: ContentBlock[]\n readonly shadowedTokenCount: number\n readonly provider: string\n readonly model: string\n /** Compression tier of this block (default 1). */\n readonly tier?: 1 | 2 | 3\n /** The acp-kernel block id (`bN`) created by the kernel for this transaction. */\n readonly kernelBlockId?: string\n /** Compaction ids of the blocks distilled into this one. */\n readonly parentBlockIds?: readonly string[]\n /** The kernel block's direct/effective message ids (raw CoreMessage ids) — recorded for faithful rehydration. */\n readonly directMessageIds?: readonly string[]\n readonly effectiveMessageIds?: readonly string[]\n}\n\n/**\n * ACP tier extension fields carried on `compaction/summary` events. The\n * upstream dsh-compaction event type does not know them, so reads and writes\n * go through this precise intersection (never `any`).\n */\nexport interface AcpCompactionSummaryFields {\n /** Compression tier (1/2/3) — 1 = message range, 2 = distills tier-1, 3 = distills tier-2. */\n readonly tier?: 1 | 2 | 3\n /** The acp-kernel block id (`bN`) created for this transaction. */\n readonly kernelBlockId?: string\n /** Durable compaction ids of the blocks distilled into this one. */\n readonly parentBlockIds?: readonly string[]\n /**\n * The kernel block's direct message ids (raw CoreMessage ids) at creation —\n * recorded so a restarted engine rehydrates the SAME coverage (a tier-2\n * block's coverage is its parents' originals, not the checkpoint node).\n */\n readonly directMessageIds?: readonly string[]\n /** The kernel block's effective message ids (raw CoreMessage ids) at creation. */\n readonly effectiveMessageIds?: readonly string[]\n}\n\ntype CompactionSummaryData = SessionEventMap['compaction/summary']\n\n/** Read a `compaction/summary` event's data including the ACP tier extension fields. */\nexport function readCompactionSummary(event: SessionEvent): CompactionSummaryData & AcpCompactionSummaryFields {\n return event.data as CompactionSummaryData & AcpCompactionSummaryFields\n}\n\n/**\n * Run one durable compression transaction. Throws on invalid state; on success\n * the four events are in the log and the surface has one summary node.\n */\nexport function runCompactionTransaction(\n session: Session,\n input: CompactionTransactionInput,\n): { compactionId: string; seqs: number[] } {\n assertNoActiveCompaction(session.events)\n const turn = findOpenTurn(session.events)\n const compactionId = CompactionId(randomUUID())\n const seqs: number[] = []\n\n seqs.push(session.append('compaction/start', { compactionId, turn }).seq)\n seqs.push(session.append('compaction/summary', {\n compactionId,\n summary: input.summary,\n shadowedRange: { start: input.start, end: input.end },\n shadowedSeqs: [...input.shadowedSeqs],\n shadowedTokenCount: input.shadowedTokenCount,\n provider: input.provider,\n model: input.model,\n tier: input.tier ?? 1,\n ...(input.kernelBlockId === undefined ? {} : { kernelBlockId: input.kernelBlockId }),\n ...(input.parentBlockIds === undefined || input.parentBlockIds.length === 0\n ? {}\n : { parentBlockIds: [...input.parentBlockIds] }),\n ...(input.directMessageIds === undefined ? {} : { directMessageIds: [...input.directMessageIds] }),\n ...(input.effectiveMessageIds === undefined ? {} : { effectiveMessageIds: [...input.effectiveMessageIds] }),\n } as CompactionSummaryData & AcpCompactionSummaryFields).seq)\n\n const message = createUserMessage({\n content: input.summary,\n source: compactCheckpointSource(compactionId),\n })\n seqs.push(session.append('user/message', message, {\n surfaceOp: { op: 'replace', start: input.start, end: input.end },\n sourceEventSeqs: [...input.shadowedSeqs],\n }).seq)\n\n seqs.push(session.append('compaction/end', { compactionId, turn }).seq)\n return { compactionId, seqs }\n}\n\n/** The seq of a compaction's checkpoint summary node in the log (visible or shadowed). */\nfunction summarySeqOfCompaction(events: readonly SessionEvent[], compactionId: string): number | null {\n for (const event of events) {\n if (event.type !== 'user/message') continue\n const source = (event.data as { source?: { plugin?: string; compactionId?: string } }).source\n if (source?.plugin === 'compact' && source.compactionId === compactionId) return event.seq\n }\n return null\n}\n\n/** Rebuild the block ledger from the durable log (no kernel state needed). */\nexport function rebuildBlockLedger(events: readonly SessionEvent[]): AcpBlockLedgerEntry[] {\n const ledger: AcpBlockLedgerEntry[] = []\n for (const event of events) {\n if (event.type !== 'compaction/summary') continue\n const data = readCompactionSummary(event)\n // Blocks written before the token-accounting fix carry shadowedTokenCount\n // 0; backfill from the shadowed originals still in the log so acp_status\n // reports real reclaimed tokens.\n let shadowedTokenCount = data.shadowedTokenCount\n if (shadowedTokenCount === 0) {\n shadowedTokenCount = 0\n for (const seq of data.shadowedSeqs) {\n const original = events[seq]\n if (original !== undefined) shadowedTokenCount += defaultCountTokens(extractEventText(original))\n }\n }\n const tier = data.tier === 2 || data.tier === 3 ? data.tier : 1\n const parentBlockIds: string[] = Array.isArray(data.parentBlockIds) ? [...data.parentBlockIds] : []\n const directMessageIds: string[] | undefined = Array.isArray(data.directMessageIds) ? [...data.directMessageIds] : undefined\n const effectiveMessageIds: string[] | undefined = Array.isArray(data.effectiveMessageIds) ? [...data.effectiveMessageIds] : undefined\n const summarySeq = summarySeqOfCompaction(events, data.compactionId)\n ledger.push({\n blockId: data.compactionId,\n summary: extractText(data.summary),\n shadowedSeqs: [...data.shadowedSeqs],\n shadowedTokenCount,\n start: data.shadowedRange.start,\n end: data.shadowedRange.end,\n tier,\n parentBlockIds,\n ...(typeof data.kernelBlockId === 'string' ? { kernelBlockId: data.kernelBlockId } : {}),\n ...(summarySeq === null ? {} : { summarySeq }),\n ...(directMessageIds === undefined ? {} : { directMessageIds }),\n ...(effectiveMessageIds === undefined ? {} : { effectiveMessageIds }),\n createdAt: event.time,\n })\n }\n return ledger\n}\n\n/** One self-computed compressible span of the current surface. */\nexport interface SeqCompressibleRange {\n readonly start: number\n readonly end: number\n readonly count: number\n readonly tokens: number\n}\n\n/** Whether a surface user message is a compaction checkpoint node (already compressed). */\nfunction isCheckpointNode(event: SessionEvent): boolean {\n if (event.type !== 'user/message') return false\n const source = (event.data as { source?: { plugin?: string } }).source\n return source?.plugin === 'compact'\n}\n\n/**\n * Compute compressible spans directly from the surface — independent of the\n * kernel's ref map, which can drift after surface replacements in long\n * sessions and hide large tool results from the nudge range table. Skips the\n * recent protected tail, the last user message, and compaction checkpoints;\n * edges are then balanced through resolveSurfaceRange. Ranges are ordered by\n * size (largest reclaimed first).\n */\nexport function buildCompressibleSeqRanges(\n session: Session,\n opts: { preserveRecent?: number } = {},\n): SeqCompressibleRange[] {\n const nodes = session.surface.nodes\n const preserve = opts.preserveRecent ?? 5\n const protectedSeqs = new Set<number>()\n for (const seq of nodes.slice(-preserve)) protectedSeqs.add(seq)\n for (let index = nodes.length - 1; index >= 0; index -= 1) {\n const event = session.events[nodes[index]!]\n if (event?.type === 'user/message' && !isCheckpointNode(event)) {\n protectedSeqs.add(nodes[index]!)\n break\n }\n }\n const raw: SeqCompressibleRange[] = []\n let cur: SeqCompressibleRange | null = null\n const flush = (): void => {\n if (cur !== null) raw.push(cur)\n cur = null\n }\n for (const seq of nodes) {\n const event = session.events[seq]\n if (event === undefined || protectedSeqs.has(seq) || isCheckpointNode(event)) {\n flush()\n continue\n }\n // Surface nodes can be locally out of order after surface replacements in\n // long sessions; a node with a SMALLER seq than the running segment would\n // produce a reversed range (e.g. 110295..106762). Break the segment so\n // ranges always stay start <= end.\n if (cur !== null && seq < cur.start) {\n flush()\n cur = null\n }\n const tokens = defaultCountTokens(extractEventText(event))\n if (cur === null) {\n cur = { start: seq, end: seq, count: 1, tokens }\n } else {\n cur = { start: cur.start, end: seq, count: cur.count + 1, tokens: cur.tokens + tokens }\n }\n }\n flush()\n const out: SeqCompressibleRange[] = []\n for (const range of raw) {\n try {\n const { start, end } = resolveSurfaceRange(session, range.start, range.end)\n const count = range.count\n out.push({ start, end, count, tokens: range.tokens })\n } catch {\n // Cannot be balanced into a compressible span — skip.\n }\n }\n return out.sort((a, b) => b.tokens - a.tokens)\n}\n\n/**\n * A compact human-readable description of the current surface for the model:\n * node count plus the first/last message seqs. Surface seqs are sparse (the\n * event log interleaves non-message events and expanded delta batches), so a\n * model that never saw the nudge range table — e.g. low-pressure sessions\n * where no nudge fires — cannot guess its own seq space. acp_status and the\n * nudge's range table both surface this so compress edges can be located\n * without blind probing.\n */\nexport function surfaceSummary(session: Session): string {\n const nodes = session.surface.nodes\n if (nodes.length === 0) return 'empty'\n const first = nodes[0]!\n const last = nodes[nodes.length - 1]!\n return `${nodes.length} nodes, seqs ${first}..${last}`\n}\n\n/** One block as seen by the tier machinery: durable id ↔ kernel ref (`bN`). */\nexport interface AcpBlockRegistryEntry {\n /** The durable compaction id. */\n readonly blockId: string\n /** The acp-kernel block ref (`bN`); synthesised by log order for legacy blocks. */\n readonly kernelBlockId: string\n readonly tier: 1 | 2 | 3\n /** The surface seq of this block's checkpoint summary node (null when gone). */\n readonly summarySeq: number | null\n /** True until a LATER block distills this one. Only active blocks are distillable. */\n readonly active: boolean\n readonly parentBlockIds: readonly string[]\n}\n\n/**\n * Rebuild the compactionId ↔ kernel-block-ref registry from the durable log.\n * Legacy blocks (pre-tier, no recorded `kernelBlockId`) are synthesised as\n * `b1`, `b2`, … in log order; recorded ids are kept as-is. A block is active\n * until a later block lists it as a parent.\n */\nexport function blockRegistry(session: Session): AcpBlockRegistryEntry[] {\n const ledger = rebuildBlockLedger(session.events)\n const kernelIdOf = new Map<string, string>()\n const raw: AcpBlockRegistryEntry[] = []\n let next = 1\n for (const entry of ledger) {\n let kernelBlockId: string\n if (entry.kernelBlockId !== undefined && /^b\\d+$/.test(entry.kernelBlockId)) {\n kernelBlockId = entry.kernelBlockId\n const num = Number(kernelBlockId.slice(1))\n if (Number.isInteger(num)) next = Math.max(next, num + 1)\n } else {\n kernelBlockId = `b${next}`\n next += 1\n }\n kernelIdOf.set(entry.blockId, kernelBlockId)\n raw.push({\n blockId: entry.blockId,\n kernelBlockId,\n tier: entry.tier,\n summarySeq: entry.summarySeq ?? null,\n active: true,\n parentBlockIds: [...entry.parentBlockIds],\n })\n }\n const consumed = new Set<string>()\n for (const entry of raw) {\n for (const parent of entry.parentBlockIds) consumed.add(parent)\n }\n return raw.map((entry) => ({\n ...entry,\n active: !consumed.has(entry.blockId),\n }))\n}\n\n/**\n * The kernel block ref (`bN`) for a surface seq, when that seq is the\n * checkpoint summary node of a block — the edge the model must use to\n * distill (T2/T3). Active blocks distill; a stale (already-distilled) node\n * still maps to its `bN` so the kernel reports \"already compressed\" instead\n * of silently folding the summary as a plain message. Returns null for\n * anything else (plain messages, non-checkpoint nodes).\n */\nexport function blockRefForSummarySeq(session: Session, seq: number): string | null {\n const event = session.events[seq]\n if (event?.type !== 'user/message') return null\n const source = (event.data as { source?: { plugin?: string; compactionId?: string } }).source\n if (source?.plugin !== 'compact' || source.compactionId === undefined) return null\n const entry = blockRegistry(session).find((r) => r.blockId === source.compactionId)\n if (entry === undefined) return null\n return entry.kernelBlockId\n}\n\n/** The durable compaction ids distilled by the given kernel block refs (`bN`). */\nexport function compactionIdsOfKernelBlocks(session: Session, kernelBlockIds: readonly string[]): string[] {\n if (kernelBlockIds.length === 0) return []\n const byKernel = new Map(blockRegistry(session).map((r) => [r.kernelBlockId, r.blockId]))\n return kernelBlockIds\n .map((id) => byKernel.get(id))\n .filter((id): id is string => id !== undefined)\n}\n\n/** The checkpoint summary seq of an ACTIVE kernel block (`bN`), or null. */\nexport function summarySeqOfKernelBlock(session: Session, kernelBlockId: string): number | null {\n const entry = blockRegistry(session).find((r) => r.kernelBlockId === kernelBlockId)\n return entry?.active ? entry.summarySeq : null\n}\n\n/** The durable block whose checkpoint node sits at `seq` (or null). */\nfunction checkpointBlockIdOf(events: readonly SessionEvent[], seq: number): string | null {\n const event = events[seq]\n if (event?.type !== 'user/message') return null\n const source = (event.data as { source?: { plugin?: string; compactionId?: string } }).source\n if (source?.plugin !== 'compact' || source.compactionId === undefined) return null\n return source.compactionId\n}\n\n/**\n * The shadowed seqs of a block, recursing into distilled parent blocks: a\n * tier-2 block shadows its parent's checkpoint node, so recovering its\n * originals requires expanding that node into the parent block's own shadowed\n * seqs. Cycle-safe (a block can never be its own ancestor).\n */\nexport function expandShadowedSeqs(session: Session, blockId: string): number[] {\n const ledger = rebuildBlockLedger(session.events)\n const byId = new Map(ledger.map((entry) => [entry.blockId, entry]))\n const root = byId.get(blockId)\n if (root === undefined) return []\n const out: number[] = []\n const seen = new Set<string>()\n const visit = (entry: AcpBlockLedgerEntry): void => {\n if (seen.has(entry.blockId)) return\n seen.add(entry.blockId)\n for (const seq of entry.shadowedSeqs) {\n const childId = checkpointBlockIdOf(session.events, seq)\n const child = childId === null ? undefined : byId.get(childId)\n if (child !== undefined) visit(child)\n else out.push(seq)\n }\n }\n visit(root)\n return out\n}\n","/**\n * M1 — session-log projection: DSH surface events → acp-kernel CoreMessage.\n *\n * The ACP kernel is message-array based; DSH is event-log based. This module\n * is the bridge in the direction the engine needs (projectEvent /\n * eventsToCoreMessages). The reverse direction (CoreMessage[] → session\n * appends) is the M5 region transaction's job.\n * Mirrors billion-context-pi's `projectMessage`/`entriesToCoreMessages`\n * against DSH event shapes (see V-verification: SurfaceEventType =\n * 'user/message' | 'assistant/message' | 'tool/result').\n * @module billion-context-pi-dsh/messages\n */\n\nimport type { CoreMessage } from 'acp-kernel'\nimport type { SessionEvent } from '@deepseek-ai/dsh-session'\n\n/**\n * Extract plain text from a DSH content block array or string.\n *\n * Recursive: a real DSH `tool-result` block is `{ type: 'tool-result',\n * toolCallId, content: ContentBlock[] }` — the inner `content` array holds\n * the actual `text` blocks, so a top-level-only walk would drop every tool\n * result from the projection (and with it the seq's ref assignment, breaking\n * compress boundary resolution). Nested arrays are flattened depth-first.\n */\nexport function extractText(content: unknown): string {\n if (typeof content === 'string') return content\n if (!Array.isArray(content)) return ''\n const parts: string[] = []\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const b = block as { type?: unknown; text?: unknown; content?: unknown }\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text)\n } else if (Array.isArray(b.content)) {\n parts.push(extractText(b.content))\n }\n }\n return parts.join('\\n')\n}\n\ninterface ToolCallBlock {\n type: 'tool-call'\n id?: string\n name?: string\n arguments?: unknown\n}\n\nfunction toolCallsOf(content: unknown): ToolCallBlock[] {\n if (!Array.isArray(content)) return []\n return content.filter((b): b is ToolCallBlock => (b as { type?: string }).type === 'tool-call')\n}\n\nfunction stringifyArgs(args: unknown): string {\n if (!args) return ''\n if (typeof args === 'string') return args\n try {\n return JSON.stringify(args)\n } catch {\n return String(args)\n }\n}\n\n/**\n * Project one surface message event into CoreMessage(s).\n * - user/message → user text (verbatim content)\n * - assistant/message → assistant text, or one CoreMessage per tool-call\n * - tool/result → tool result (toolName/toolCallId, role 'tool')\n * Non-surface events project to nothing.\n */\nexport function projectEvent(event: SessionEvent): CoreMessage[] {\n switch (event.type) {\n case 'user/message': {\n const text = extractText((event.data as { content?: unknown }).content)\n return text.length > 0 ? [{ id: String(event.seq), role: 'user', contentType: 'text', text }] : []\n }\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = toolCallsOf(content)\n const text = extractText(content)\n if (calls.length === 0) {\n return text.trim().length > 0\n ? [{ id: String(event.seq), role: 'assistant', contentType: 'text', text }]\n : []\n }\n if (calls.length === 1) {\n const call = calls[0]!\n const argStr = stringifyArgs(call.arguments)\n const body = argStr && text ? `${text}\\n${argStr}` : argStr || text\n return [{\n id: String(event.seq),\n role: 'assistant',\n contentType: 'tool-call',\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: body,\n }]\n }\n return calls.map((call) => ({\n id: `${event.seq}#${call.id ?? ''}`,\n role: 'assistant' as const,\n contentType: 'tool-call' as const,\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: stringifyArgs(call.arguments) || text,\n }))\n }\n case 'tool/result': {\n const message = (event.data as {\n message?: { content?: unknown; toolName?: string; toolCallId?: string }\n }).message\n const text = extractText(message?.content)\n if (text.length === 0) return []\n return [{\n id: String(event.seq),\n role: 'tool',\n contentType: 'tool-result',\n toolName: message?.toolName ?? '',\n toolCallId: message?.toolCallId ?? '',\n text,\n }]\n }\n default:\n return []\n }\n}\n\n/** Project a session's message events into CoreMessage[] in log order. */\nexport function eventsToCoreMessages(events: readonly SessionEvent[]): CoreMessage[] {\n const out: CoreMessage[] = []\n for (const event of events) out.push(...projectEvent(event))\n return out\n}\n\n/** The surface-visible message events of a session, in model-visible order. */\nexport function surfaceEventsOf(session: import('@deepseek-ai/dsh-session').Session): SessionEvent[] {\n return session.surface.nodes\n .map((seq) => session.events[seq])\n .filter((event): event is SessionEvent => event !== undefined)\n}\n\n/**\n * ALL message-type events in log order — the visible surface PLUS everything\n * shadowed by compression. The ACP kernel deactivates any block whose consumed\n * message ids are absent from the array it is given (syncBlocks), and refuses\n * to anchor a block boundary that cannot find its messages, so T2/T3\n * distillation requires the full log, not just the visible surface.\n */\nexport function allLogMessages(session: import('@deepseek-ai/dsh-session').Session): CoreMessage[] {\n return eventsToCoreMessages(session.events)\n}\n\n/** Extract the model-facing text of any surface message event. */\nexport function extractEventText(event: SessionEvent): string {\n switch (event.type) {\n case 'user/message':\n return extractText((event.data as { content?: unknown }).content)\n case 'assistant/message':\n return extractText((event.data as { message?: { content?: unknown } }).message?.content)\n case 'tool/result':\n return extractText((event.data as { message?: { content?: unknown } }).message?.content)\n default:\n return ''\n }\n}\n","/**\n * M3 — the four model tools: compress / decompress / search_context /\n * acp_status, registered through `ctx.tools` (defineTool).\n *\n * compress is the heart of ACP: the model writes the summary and the tool\n * lands it as a durable surface replacement (no second LLM summarization\n * call). decompress recovers shadowed content read-only from the log (DSH\n * keeps the originals — V5). search_context scores blocks rebuilt from the\n * log. acp_status reports the block ledger and pressure.\n * @module billion-context-dsh/tools\n */\n\nimport { defineTool, type ToolDefinition, type ToolRunContext } from '@deepseek-ai/dsh-tools'\nimport { defaultCountTokens, type CompressionCore } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { AcpStateStore } from './state.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nimport {\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n expandShadowedSeqs,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n surfaceSummary,\n} from './region.ts'\nimport { allLogMessages, eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\n\nexport interface ToolEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\ninterface TextOutput {\n text: string\n}\n\nfunction textOutput(): {\n schema: { type: 'object'; properties: { text: { type: 'string' } }; additionalProperties: boolean }\n render: (args: unknown, value: TextOutput) => import('@deepseek-ai/dsh-llm').ContentBlock[]\n} {\n return {\n schema: {\n type: 'object',\n properties: { text: { type: 'string' } },\n additionalProperties: false,\n },\n render: (_args, value) => [{ type: 'text', text: value.text }],\n }\n}\n\nfunction requireAgent(exec: ToolRunContext): Agent {\n if (exec.agent === undefined) {\n throw new Error('billion-context-dsh: tool requires an agent execution context')\n }\n return exec.agent\n}\n\nconst compressParameters = {\n topic: { type: 'string' as const, description: 'Fallback topic for entries without their own.' },\n content: {\n type: 'array' as const,\n required: true,\n description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary.',\n items: {\n type: 'object' as const,\n properties: {\n startSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'First surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n endSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'Inclusive last surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n summary: { type: 'string' as const, description: 'Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters.' },\n topic: { type: 'string' as const, description: 'Short label (3-5 words) for this range.' },\n },\n additionalProperties: false,\n },\n },\n} as const\n\n/** Normalize a seq arg: number, \"295\", or \"295#call_00_xxx\" → 295. */\nfunction parseSeq(value: number | string): number {\n const text = String(value).split('#')[0]!.trim()\n const seq = Number(text)\n if (!Number.isInteger(seq) || seq < 0) {\n throw new Error(`billion-context-dsh: invalid seq \"${String(value)}\" — use a surface seq like 295`)\n }\n return seq\n}\n\ninterface CompressArgs {\n topic?: string\n content: Array<{ startSeq: number | string; endSeq: number | string; summary: string; topic?: string }>\n}\n\n/** Resolve seq → kernel ref, then applyCompression and land the transaction. */\nasync function handleCompress(env: ToolEnvironment, args: CompressArgs, exec: ToolRunContext): Promise<TextOutput> {\n const agent = requireAgent(exec)\n const session = agent.session\n const state = env.store.stateFor(session)\n // The kernel gets the FULL log (visible + shadowed): syncBlocks deactivates\n // a block whose consumed messages are absent, and resolveBoundaries refuses\n // to anchor a block ref it cannot find, so tier-2/3 distillation needs the\n // originals present. The token count stays a surface measurement.\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = surfaceMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const config = kernelConfigFor(env)\n\n // Assign refs / advance state exactly like a turn would.\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n const byRaw = turn.state.messageRefs.byRaw\n\n const ranges = args.content.map((range) => {\n const startSeq = parseSeq(range.startSeq)\n const endSeq = parseSeq(range.endSeq)\n // Balance edges FIRST: the requested edges may sit on multi-tool-call\n // assistant messages, which project to `${seq}#${callId}` CoreMessage ids\n // and therefore have NO bare-`${seq}` ref. resolveSurfaceRange shifts them\n // to clean tool-pairing-balanced cuts that always carry a bare ref, so the\n // resolved refs exist and the shadowed span matches the returned range.\n const { start, end } = resolveSurfaceRange(session, startSeq, endSeq)\n // An edge on an ACTIVE block's checkpoint summary node resolves to the\n // kernel block ref (bN) — the boundary that makes applyCompression distill\n // (tier 2/3) instead of folding the summary as a plain message.\n const startBlockRef = blockRefForSummarySeq(session, start)\n const endBlockRef = blockRefForSummarySeq(session, end)\n const startRef = startBlockRef ?? byRaw[String(start)]\n const endRef = endBlockRef ?? byRaw[String(end)]\n if (startRef === undefined || endRef === undefined) {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} has no assigned ref — `\n + 'the range must be on the current surface (run acp_status for the live seq list)',\n )\n }\n return {\n startSeq,\n endSeq,\n start,\n end,\n startRef,\n endRef,\n summary: range.summary,\n ...(range.topic ?? args.topic) === undefined ? {} : { topic: range.topic ?? args.topic },\n }\n })\n\n const applied = env.kernel.applyCompression({\n ranges: ranges.map(({ startRef, endRef, summary, topic }) => ({ startRef, endRef, summary, topic })),\n messages: coreMessages,\n state: turn.state,\n config,\n // Deliberately NOT overriding protectedMessageIds: with the full log the\n // kernel's recent/last-user protection is computed over the same\n // non-block-covered messages as the visible feed, so default behavior is\n // preserved. Any 'Excluded N protected message(s)' warning is surfaced.\n })\n if (applied.result.errors.length > 0) {\n return { text: `compress failed: ${applied.result.errors.join('; ')}` }\n }\n env.store.set(session, applied.state)\n\n // Match freshly created kernel blocks to the requested ranges by their\n // range key (the kernel stamps startRef/endRef onto each new block).\n const previousIds = new Set(turn.state.blocks.map((block) => block.blockId))\n const newBlocks = applied.state.blocks.filter((block) => !previousIds.has(block.blockId))\n const blockByRangeKey = new Map(newBlocks.map((block) => [`${block.startRef}::${block.endRef}`, block]))\n // Warnings carry two shapes: range-prefixed (\"Skipped range (a..b) — …\")\n // attributable to a specific range, and free-form (\"Excluded N protected\n // message(s) …\") attributable to the call as a whole.\n const warningByRangeKey = new Map<string, string[]>()\n const freeWarnings: string[] = []\n for (const warning of applied.result.warnings) {\n const match = /^Skipped range \\((.+?)\\.\\.(.+?)\\)/.exec(warning)\n if (match !== null) {\n const key = `${match[1]}::${match[2]}`\n const list = warningByRangeKey.get(key) ?? []\n list.push(warning)\n warningByRangeKey.set(key, list)\n } else {\n freeWarnings.push(warning)\n }\n }\n\n const lines: string[] = []\n let skippedRanges = 0\n for (let index = 0; index < ranges.length; index += 1) {\n const range = ranges[index]!\n const original = args.content[index]!\n const key = `${range.startRef}::${range.endRef}`\n const block = blockByRangeKey.get(key)\n if (block === undefined) {\n // The kernel skipped this range (already compressed / overlapped): no\n // kernel block was created, so no durable transaction is landed — the\n // ledger must never record a block the kernel does not know.\n skippedRanges += 1\n const warnings = warningByRangeKey.get(key) ?? []\n for (const warning of warnings) lines.push(` ${warning}`)\n continue\n }\n // The edges were already balanced above; shadow exactly that span.\n const { start, end } = range\n const shadowed = shadowedSeqsOf(session, start, end)\n // Estimate the reclaimed tokens from the actual shadowed messages so the\n // durable ledger (compaction/summary.shadowedTokenCount) reports a real\n // number instead of 0.\n let shadowedTokens = 0\n for (const seq of shadowed) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokens += defaultCountTokens(extractEventText(event))\n }\n const tier = block.tier === 2 || block.tier === 3 ? block.tier : 1\n const parentBlockIds = compactionIdsOfKernelBlocks(session, block.directBlockIds)\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: original.summary }],\n shadowedTokenCount: shadowedTokens,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n tier,\n kernelBlockId: block.blockId,\n ...(parentBlockIds.length === 0 ? {} : { parentBlockIds }),\n // Record the kernel block's raw coverage so a restarted engine\n // rehydrates the SAME effective messages (a tier-2 block's coverage is\n // its parents' originals, not the checkpoint node).\n directMessageIds: block.directMessageIds,\n effectiveMessageIds: block.effectiveMessageIds,\n })\n const adjusted = start !== range.startSeq || end !== range.endSeq\n const tierLabel = tier === 1 ? '' : `, tier ${tier}`\n lines.push(\n ` block ${compactionId.slice(0, 8)}: seqs ${start}..${end}, ${shadowed.length} messages shadowed${tierLabel}`\n + (adjusted ? ` (adjusted from ${range.startSeq}..${range.endSeq} to balanced edges)` : ''),\n )\n }\n\n const summaryLine = `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.`\n const warningLines = [...freeWarnings.map((warning) => ` ${warning}`), ...lines]\n const footer = skippedRanges > 0\n ? ` (${skippedRanges} range(s) skipped — see warnings above)`\n : ''\n return { text: `${summaryLine}\\n${[...warningLines, footer].filter((line) => line !== '').join('\\n')}` }\n}\n\nconst decompressParameters = {\n blockId: { type: 'string' as const, required: true, description: 'Block id from acp_status or search_context (the compaction id).' },\n} as const\n\ninterface DecompressArgs {\n blockId: string\n}\n\nfunction handleDecompress(_env: ToolEnvironment, args: DecompressArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args.blockId))\n if (block === undefined) {\n return { text: `decompress: block \"${args.blockId}\" not found (see acp_status for the block list)` }\n }\n const parts: string[] = []\n // Tier-2/3 blocks shadow parent checkpoint nodes: expand to the originals.\n for (const seq of expandShadowedSeqs(session, block.blockId)) {\n const event = session.events[seq]\n const text = event === undefined ? '' : extractEventText(event)\n if (text.length > 0) parts.push(`[seq ${seq}] ${text}`)\n }\n const tierNote = block.tier > 1 ? ` (tier ${block.tier}, distills ${block.parentBlockIds.length} block(s))` : ''\n return {\n text: `Block ${block.blockId} — ${block.summary}${tierNote}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`,\n }\n}\n\nconst searchParameters = {\n query: { type: 'string' as const, required: true, description: 'Search terms to find inside compressed blocks.' },\n limit: { type: 'integer' as const, description: 'Maximum results (default 5).' },\n} as const\n\ninterface SearchArgs {\n query: string\n limit?: number\n}\n\nfunction handleSearch(_env: ToolEnvironment, args: SearchArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const terms = args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n const scored: Array<{ blockId: string; score: number; summary: string }> = []\n for (const block of ledger) {\n const original = block.shadowedSeqs\n .map((seq) => extractEventText(session.events[seq]!))\n .join('\\n')\n const haystack = `${block.summary}\\n${original}`.toLowerCase()\n let score = 0\n for (const term of terms) score += haystack.split(term).length - 1\n if (score > 0) scored.push({ blockId: block.blockId, score, summary: block.summary })\n }\n scored.sort((a, b) => b.score - a.score)\n const top = scored.slice(0, args.limit ?? 5)\n if (top.length === 0) return { text: `search_context: no matches for \"${args.query}\"` }\n return {\n text: `Matches for \"${args.query}\":\\n`\n + top.map((hit) => ` - ${hit.blockId} (score ${hit.score}): ${hit.summary.slice(0, 160)}`).join('\\n')\n + '\\n\\nDecompress with: decompress({ blockId })',\n }\n}\n\nconst statusParameters = {} as const\n\ninterface StatusArgs {\n [key: string]: never\n}\n\nfunction handleStatus(env: ToolEnvironment, _args: StatusArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const limit = env.modelContextLimit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ` surface: ${surfaceSummary(session)}`,\n ]\n for (const block of ledger.slice(0, 10)) {\n lines.push(` - ${block.blockId.slice(0, 8)}: seqs ${block.start}..${block.end} (${block.shadowedSeqs.length} msgs) — ${block.summary.slice(0, 80)}`)\n }\n return { text: lines.join('\\n') }\n}\n\n/** Build the four ACP model tools bound to one engine. */\nexport function makeTools(env: ToolEnvironment): ToolDefinition[] {\n return [\n defineTool({\n name: 'compress',\n description:\n 'Replace older conversation ranges with dense summaries you write. '\n + 'Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). '\n + 'Batch multiple unrelated ranges in one call (each content entry becomes its own block); keep ranges disjoint. '\n + 'Never compress content the current step is actively using.',\n parameters: compressParameters,\n output: textOutput(),\n async execute(args, exec) {\n return handleCompress(env, args as CompressArgs, exec)\n },\n }),\n defineTool({\n name: 'decompress',\n description: 'Recover the original content of a compressed block by its blockId (read-only; does not unshadow the range).',\n parameters: decompressParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleDecompress(env, args as DecompressArgs, exec))\n },\n }),\n defineTool({\n name: 'search_context',\n description: 'Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.',\n parameters: searchParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleSearch(env, args as SearchArgs, exec))\n },\n }),\n defineTool({\n name: 'acp_status',\n description: 'Report the ACP block ledger: compressed blocks, reclaimed tokens, and current context pressure.',\n parameters: statusParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleStatus(env, args as StatusArgs, exec))\n },\n }),\n ]\n}\n","/**\n * Kernel configuration assembly — the DSH counterpart of billion-context-pi's\n * `resolveConfig`: build acp-kernel's `Config` from adapter-level knobs.\n *\n * Defaults are deliberately the acp-kernel `defaultConfig` values (the same\n * defaults billion-context-pi ships: nudge window 45%–75%, emergency 95%,\n * growth ratio 5%, protected last messages 5). Every knob is optional — an\n * omitted value keeps the kernel default, so the behavior matches the Pi\n * adapter exactly unless a deployment opts out.\n * @module billion-context-dsh/config\n */\n\nimport { defaultConfig, type Config } from 'acp-kernel'\n\n/** The kernel-facing knobs shared by the nudge path and the compress tool. */\nexport interface KernelConfigInput {\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction). Kernel default: 0.45. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound — over-limit. Kernel default: 0.75. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default: 0.95. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (the billion-context-pi escape hatch). */\n readonly coreOverrides?: Partial<Config>\n}\n\n/**\n * Assemble the kernel config: `defaultConfig(limit)` merged with the optional\n * nudge thresholds (merged into the defaults, never replacing them wholesale)\n * and any additional `coreOverrides`.\n */\nexport function kernelConfigFor(input: KernelConfigInput): Config {\n const nudgePatch: Partial<Config['nudge']> = {}\n if (input.nudgeMinContextLimitPct !== undefined) nudgePatch.minContextLimitPct = input.nudgeMinContextLimitPct\n if (input.nudgeMaxContextLimitPct !== undefined) nudgePatch.maxContextLimitPct = input.nudgeMaxContextLimitPct\n if (input.nudgeEmergencyThresholdPct !== undefined) nudgePatch.emergencyThresholdPct = input.nudgeEmergencyThresholdPct\n\n const overrides: Partial<Config> = { ...input.coreOverrides }\n if (Object.keys(nudgePatch).length > 0) {\n overrides.nudge = { ...defaultConfig(input.modelContextLimit).nudge, ...nudgePatch }\n }\n return defaultConfig(input.modelContextLimit, overrides)\n}\n","/**\n * M4 — the `/acp` slash command: a human-friendly window into the same\n * machinery the model tools expose (status, one-shot compress, decompress).\n * @module billion-context-dsh/commands\n */\n\nimport type { CommandDefinition } from '@deepseek-ai/dsh-commands'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { ToolEnvironment } from './tools.ts'\nimport {\n blockRefForSummarySeq,\n expandShadowedSeqs,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n} from './region.ts'\nimport { eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\nimport { defaultConfig, defaultCountTokens } from 'acp-kernel'\n\nfunction statusText(env: ToolEnvironment, agent: Agent): string {\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const limit = env.modelContextLimit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ]\n for (const block of ledger.slice(0, 10)) {\n const tier = block.tier > 1 ? ` [T${block.tier}]` : ''\n lines.push(` - ${block.blockId.slice(0, 8)}${tier}: seqs ${block.start}..${block.end} — ${block.summary.slice(0, 80)}`)\n }\n return lines.join('\\n')\n}\n\nfunction compressText(env: ToolEnvironment, agent: Agent, args: string[]): string {\n if (args.length < 3) {\n return '/acp compress <startSeq> <endSeq> <summary...>'\n }\n const startSeq = Number(args[0])\n const endSeq = Number(args[1])\n const summary = args.slice(2).join(' ')\n if (!Number.isInteger(startSeq) || !Number.isInteger(endSeq)) {\n return '/acp compress: startSeq and endSeq must be integers'\n }\n const session = agent.session\n const { start, end } = resolveSurfaceRange(session, startSeq, endSeq)\n // A checkpoint summary node can only be distilled through the kernel (the\n // compress tool); /acp compress is a plain T1 range transaction, so refuse\n // rather than silently folding the summary as a message.\n if (blockRefForSummarySeq(session, start) !== null || blockRefForSummarySeq(session, end) !== null) {\n return '/acp compress: the range touches a compressed block summary node — distill it with the compress tool (seq-based batch), not /acp compress'\n }\n const shadowed = shadowedSeqsOf(session, startSeq, endSeq)\n let shadowedTokens = 0\n for (const seq of shadowed) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokens += defaultCountTokens(extractEventText(event))\n }\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: summary }],\n shadowedTokenCount: shadowedTokens,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n })\n return `Compressed seqs ${start}..${end} (${shadowed.length} messages) as block ${compactionId.slice(0, 8)}`\n}\n\nfunction decompressText(_env: ToolEnvironment, agent: Agent, args: string[]): string {\n if (args.length < 1) return '/acp decompress <blockId>'\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args[0]!))\n if (block === undefined) return `block \"${args[0]}\" not found (see /acp status)`\n // Tier-2/3 blocks shadow parent checkpoint nodes: expand to the originals.\n const parts = expandShadowedSeqs(session, block.blockId)\n .map((seq) => extractEventText(session.events[seq]!))\n .filter((text) => text.length > 0)\n return `Block ${block.blockId} — ${block.summary}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`\n}\n\n/** Register the /acp command (idempotent per engine). */\nexport function acpCommand(env: ToolEnvironment): CommandDefinition {\n return {\n name: 'acp',\n description:\n 'Active Context Pruning — model-driven context compression. '\n + 'Usage: /acp status | /acp compress <startSeq> <endSeq> <summary> | /acp decompress <blockId>',\n handler: async (invocation) => {\n const raw = invocation.rawInput.trim()\n if (raw === '' || raw === 'status') {\n return { kind: 'success', text: statusText(env, invocation.agent) }\n }\n if (raw.startsWith('compress')) {\n return { kind: 'success', text: compressText(env, invocation.agent, raw.slice('compress'.length).trim().split(/\\s+/) ) }\n }\n if (raw.startsWith('decompress')) {\n return { kind: 'success', text: decompressText(env, invocation.agent, raw.slice('decompress'.length).trim().split(/\\s+/)) }\n }\n return { kind: 'error', text: `unknown /acp subcommand \"${raw.split(/\\s+/)[0]}\" — use status | compress | decompress` }\n },\n }\n}\n","/**\n * M4 — ACP nudge: the kernel's compression recommendation, rendered as an\n * injected user message with a seq-based compressible-range table (D1:\n * \"seq is the ref\" — DSH has no in-memory message rewrite hook, so the model\n * targets ranges by surface seq rather than by <acp> tags).\n * @module billion-context-dsh/nudge\n */\n\nimport {\n defaultCountTokens,\n type CompressionCore,\n type CoreMessage,\n type NudgeDecision,\n} from 'acp-kernel'\nimport { createUserMessage, type UserMessage } from '@deepseek-ai/dsh-llm'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { allLogMessages, eventsToCoreMessages, surfaceEventsOf } from './messages.ts'\nimport { buildCompressibleSeqRanges, findOpenTurn, summarySeqOfKernelBlock, surfaceSummary } from './region.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\n\n/** Kernel inputs the nudge path shares with the compress tool. */\nexport interface NudgeEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\nexport interface NudgeOutcome {\n readonly message: UserMessage\n readonly emergency: boolean\n}\n\n/**\n * Render the compressible-range table as seq refs for the model.\n * Computed directly from the surface (not the kernel's ref map, which can\n * drift and hide large tool results) — see buildCompressibleSeqRanges.\n */\nexport function rangeTable(session: import('@deepseek-ai/dsh-session').Session): string {\n const ranges = buildCompressibleSeqRanges(session).slice(0, 6)\n if (ranges.length === 0) return ''\n const lines = ranges.map((range) => ` - seq ${range.start}..${range.end} — ${range.count} messages, ~${range.tokens} tokens`)\n return [\n '',\n `Surface: ${surfaceSummary(session)}`,\n 'Compressible ranges (suggestions only — compress any consumed span; refs are surface seqs):',\n ...lines,\n 'Compress with: compress({ content: [{ startSeq, endSeq, summary }] }) — content is an array: batch multiple unrelated segments in one call, each entry its own block. Keep ranges disjoint.',\n ].join('\\n')\n}\n\n/**\n * The token count driving pressure decisions. Prefer the host token meter's\n * SURFACE tokens (the input-side heuristic total — matches acp_status and the\n * UI's message-token view). `totalTokens` is request-AND-RESPONSE pressure and\n * can far exceed the window in long sessions (observed 230% against a real\n * ~20% occupancy), so it is not used. Falls back to the fast heuristic when\n * the meter is absent (tests, minimal hosts).\n */\nfunction measuredTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n const meter = agent.ctx?.get?.('tokenMeter') as\n | { measure?: (session: unknown) => { surfaceTokens?: number } }\n | undefined\n const surface = meter?.measure?.(agent.session)?.surfaceTokens\n if (typeof surface === 'number' && surface > 0) return surface\n return coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n}\n\n/**\n * Decide and build one nudge message for the agent's next pre-step. Returns\n * null when the kernel recommends no nudge or one was already injected for the\n * current turn (emergency nudges always bypass the dedup). Also advances the\n * in-memory kernel state (ref assignment) so the compress tool can resolve\n * seq → mNNNNN refs.\n */\nexport function buildNudge(\n agent: Agent,\n env: NudgeEnvironment,\n lastNudgeTurn: Map<string, number>,\n): NudgeOutcome | null {\n const session = agent.session\n const state = env.store.stateFor(session)\n // Full log for the kernel (so block anchors survive — see handleCompress);\n // the measured token count stays a SURFACE measurement.\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = measuredTokenCount(agent, surfaceMessages)\n const config = kernelConfigFor(env)\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n\n const nudge = turn.nudge\n if (nudge === undefined || !nudge.shouldInject) return null\n const emergency = nudge.breakdown?.emergencyOverride === 1\n\n const turnNumber = findOpenTurn(session.events) ?? 0\n const alreadyShown = !emergency && lastNudgeTurn.get(session.id) === turnNumber\n if (alreadyShown) return null\n lastNudgeTurn.set(session.id, turnNumber)\n\n const text = buildNudgeText(nudge, emergency, session)\n const message = createUserMessage({\n content: [{ type: 'text', text }],\n source: { kind: 'plugin', plugin: 'acp-nudge' },\n })\n return { message, emergency }\n}\n\n/**\n * A short ADVISORY nudge — ACP is model-driven, the model decides whether and\n * when to compress. Full guidance (tools, philosophy, summary rules) lives in\n * the system prompt once, not in every nudge.\n */\nexport function buildNudgeText(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n): string {\n // Cap the reported percentage at 100: a broken measurement (e.g. response\n // pressure folded in) must never surface as an absurd \"230%\" to the model.\n const pct = Math.round(Math.min(nudge.contextUsage, 1) * 100)\n const frame = emergency\n ? `⚠️ Context usage is at ${pct}% of the window — nearly full. Consider compressing consumed ranges soon so working context stays available; the choice and timing are yours.`\n : `Context usage is at ${pct}%. This is a suggestion, not a requirement — you decide whether and when to compress.`\n const guidance = 'Compress by need, not by percentage: replace only ranges you have genuinely consumed, with dense self-contained summaries.'\n const parts = [frame, '', guidance]\n if ((nudge.tier === 2 || nudge.tier === 3) && (nudge.tierTargetBlocks?.length ?? 0) > 0) {\n const targets = nudge.tierTargetBlocks!\n const summarySeqs = targets\n .map((block) => summarySeqOfKernelBlock(session, block.blockId))\n .filter((seq): seq is number => seq !== null)\n const pending = nudge.tier === 2 ? nudge.breakdown?.pendingT2 : nudge.breakdown?.pendingT3\n const tokens = typeof pending === 'number' ? pending : 0\n parts.push(\n `Tier ${nudge.tier}: ${targets.length} tier-${nudge.tier - 1} block(s) distillable (${tokens} tokens) — compress their summary node(s) [seqs ${summarySeqs.join(', ')}] to reclaim the original messages.`,\n )\n }\n parts.push(rangeTable(session))\n return parts.join('\\n')\n}\n","/**\n * M4 — the ACP system-prompt section (DSH counterpart of billion-context-pi's\n * ACP_SYSTEM_PROMPT): the load-bearing compression guidance lives here, ONCE,\n * instead of being re-sent with every nudge. The nudge itself stays a short,\n * advisory notice — ACP is model-driven, the model decides whether and when\n * to compress (never \"compress now\").\n * @module billion-context-dsh/system-prompt\n */\n\nimport { COMPRESS_PHILOSOPHY } from 'acp-kernel'\n\nexport const ACP_SYSTEM_PROMPT = `Active Context Pruning — model-driven context management\n\nYOU decide whether and when to compress context. Nothing forces you: the injected \"nudge\" is a suggestion, not an order, and you may ignore it when compression would not help. Compress only ranges you have genuinely consumed (read tool outputs, finished explorations, superseded steps) that the current work no longer needs verbatim.\n\n${COMPRESS_PHILOSOPHY}\n\nCompression tools (refs are SURFACE SEQS, not ids):\n- compress: replace one or more seq ranges, each with your own dense summary. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated segments in one call (each entry becomes its own block): compress({ content: [{ startSeq: 1, endSeq: 5, summary: '...' }, { startSeq: 12, endSeq: 18, summary: '...' }] }). Keep ranges disjoint — overlapping entries in one batch are skipped. Edges are auto-balanced to tool-call/result boundaries; a trailing #callId fragment in a seq is ignored. Ranges must be on the current surface — stale seqs fail with guidance.\n- decompress: recover a compressed block's original content, read-only. decompress({ blockId }).\n- search_context: find information inside compressed blocks BEFORE decompressing. search_context({ query }).\n- acp_status: current context usage and the live compressible-range list. Run it before compressing when in doubt.\n\nTiered compression: each compressed block appears on the surface as one summary node. Compressing that node again DISTILLS the block (tier 2): the parent summary folds into your new summary and the original messages are freed. Distilling a tier-2 block yields tier 3. Distill when a summary itself is consumed — decompress on the tier-2 block recovers the full originals.\n\nWhen you write a summary, it becomes the ONLY record of that range: keep file paths, signatures, exact values, decisions, and error strings verbatim so a later reader (or you, after decompress) can continue without the original. Never reuse historical seqs — the surface moves as messages land and compress; verify with acp_status.`\n\n/** System-prompt section order: tool guidance lives in 100–199. */\nexport const ACP_SYSTEM_PROMPT_ORDER = 150\n"],"mappings":"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","names":["defaultCountTokens","defaultCountTokens","defaultCountTokens","defaultCountTokens","createUserMessage","defaultCountTokens","createUserMessage"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/state.ts","../src/region.ts","../src/messages.ts","../src/tools.ts","../src/config.ts","../src/window.ts","../src/commands.ts","../src/nudge.ts","../src/system-prompt.ts"],"sourcesContent":["/**\n * billion-context-dsh — Active Context Pruning (ACP) for the DeepSeek Harness,\n * delivered as a `CompactionEngine` backend.\n *\n * The model decides when and what to compress (pure ACP semantics):\n * - the `compress` tool durably shadows a surface range with the model-written\n * summary (no second LLM summarization call — the ACP cost win);\n * - the original events stay in the append-only session log, so `decompress`,\n * `search_context`, and replay always work;\n * - refs are surface seqs carried by the injected nudge's range table (DSH\n * has no in-memory message rewrite hook — see docs/dsh-porting-verification.md);\n * - automatic policy never summarizes by itself: it nudges the model.\n *\n * Mount it wherever a compaction backend is expected:\n *\n * ```yaml\n * - id: compaction-billion-context\n * name: 'billion-context-dsh'\n * config:\n * modelContextLimit: 128000\n * ```\n *\n * The package registers `ctx.compaction` plus the four model tools and the\n * `/acp` command when the hosting composition provides `ctx.tools` /\n * `ctx.commands`.\n * @module billion-context-dsh\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n CompactionEngine,\n ManualCompactionError,\n type CompactionAgentContext,\n type CompactionResult,\n type CompactionTrigger,\n type ManualCompactAgentContext,\n} from '@deepseek-ai/dsh-compaction'\nimport { createCore, type CompressionCore } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { makeTools, type ToolEnvironment } from './tools.ts'\nimport { acpCommand } from './commands.ts'\nimport { buildNudge } from './nudge.ts'\nimport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\nimport { DEFAULT_CONTEXT_WINDOW, detectContextWindow, type AcpWindow } from './window.ts'\n\nexport { AcpStateStore } from './state.ts'\nexport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nexport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\nexport { makeTools, type ToolEnvironment } from './tools.ts'\nexport { acpCommand } from './commands.ts'\nexport { buildNudge, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts'\nexport {\n DEFAULT_CONTEXT_WINDOW,\n detectContextWindow,\n windowSourceLabel,\n type AcpWindow,\n} from './window.ts'\nexport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n findOpenTurn,\n assertNoActiveCompaction,\n blockRegistry,\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n summarySeqOfKernelBlock,\n expandShadowedSeqs,\n type AcpBlockLedgerEntry,\n type CompactionTransactionInput,\n} from './region.ts'\nexport { eventsToCoreMessages, projectEvent, surfaceEventsOf, extractEventText } from './messages.ts'\n\nexport interface AcpConfig {\n /**\n * The context window used for pressure decisions, in tokens. When omitted,\n * `autoModelContextLimit` (default true) probes the model's real window via\n * `agent.ctx.llm.resolveModelInfo(provider, model)`; an explicit value\n * always wins and disables the probe.\n */\n readonly modelContextLimit?: number\n /** Probe the model's real context window from the LLM runtime. Default true. */\n readonly autoModelContextLimit: boolean\n /** Nudge window lower bound (usage fraction; validation only — the growth-driven trigger has no percentage floor). Kernel default 0.45 — same as billion-context-pi. */\n readonly nudgeMinContextLimitPct?: number\n /**\n * Nudge window upper bound — over-limit guarantee line: above this the\n * kernel injects a nudge regardless of growth or cadence. Engine default\n * 0.70 (deliberately BELOW the kernel/billion-context-pi default 0.75 and\n * the host compaction-basic auto-compaction line 0.80, so the forced nudge\n * always fires first); an explicit value wins.\n */\n readonly nudgeMaxContextLimitPct?: number\n /**\n * Emergency nudge threshold (bypasses the per-turn dedup). Engine default\n * 0.85 (down from the kernel/billion-context-pi default 0.95: 95% leaves\n * the model no room to act before the API rejects, and the host's 80%\n * compaction-basic line shadows it in standard/code/cordis modes).\n */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (billion-context-pi's `coreOverrides` escape hatch). */\n readonly coreOverrides?: Partial<import('acp-kernel').Config>\n /** Register the four model tools on `ctx.tools`. Default true. */\n readonly autoTools: boolean\n /** Register the `/acp` command on `ctx.commands`. Default true. */\n readonly autoCommand: boolean\n /** Inject the nudge into `agent/pre-step` when the kernel recommends it. Default true. */\n readonly autoNudge: boolean\n}\n\nconst DEFAULT_CONFIG: AcpConfig = {\n autoModelContextLimit: true,\n autoTools: true,\n autoCommand: true,\n autoNudge: true,\n // Nudge thresholds: engine defaults 0.70/0.85 — deliberately below the\n // kernel/billion-context-pi 0.75/0.95. 0.95 leaves no room to act before\n // the API rejects, and the host's compaction-basic line (thresholdRatio\n // 0.80) shadows it in standard/code/cordis modes; 0.70 keeps the forced\n // over-limit nudge ahead of that 80% line. Explicit values always win.\n nudgeMaxContextLimitPct: 0.7,\n nudgeEmergencyThresholdPct: 0.85,\n}\n\nexport function resolveAcpConfig(config: Partial<AcpConfig> = {}): AcpConfig {\n return { ...DEFAULT_CONFIG, ...config }\n}\n\n/**\n * The ACP compaction backend. Subclasses the seam exactly like\n * `dsh-compaction-basic`; swaps summarization-driven compaction for\n * model-driven block compression without touching the agent loop.\n */\nexport class AcpCompactionEngine extends CompactionEngine {\n /** The framework-agnostic ACP compression core, reused verbatim. */\n readonly kernel: CompressionCore\n /** Per-session kernel state. */\n readonly store: AcpStateStore\n /** Resolved engine configuration. */\n readonly config: AcpConfig\n\n private readonly lastNudgeTurn = new Map<string, number>()\n /** Per provider/model route the resolved window (probe failures cached too). */\n private readonly windowCache = new Map<string, AcpWindow>()\n\n constructor(ctx: Context, config: Partial<AcpConfig> = {}) {\n super(ctx)\n this.config = resolveAcpConfig(config)\n this.kernel = createCore({})\n this.store = new AcpStateStore()\n\n const env: ToolEnvironment = {\n kernel: this.kernel,\n store: this.store,\n // Initial value before any probe; windowFor() replaces it per pre-step.\n modelContextLimit: this.config.modelContextLimit ?? DEFAULT_CONTEXT_WINDOW,\n nudgeMinContextLimitPct: this.config.nudgeMinContextLimitPct,\n nudgeMaxContextLimitPct: this.config.nudgeMaxContextLimitPct,\n nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,\n coreOverrides: this.config.coreOverrides,\n windowFor: (agent) => this.windowFor(agent),\n }\n\n // Tools and commands may not be registered yet on cold start: cordis\n // starts unrelated composition rows concurrently, so the first\n // `ctx.get('tools')` can legitimately be undefined even though the row\n // ships later in the file. HMR-style reloads always see them (already\n // present), but a fresh process races — the tools silently vanished on\n // restart. Register eagerly, then re-attempt when the service appears\n // (`internal/service`) or the app finishes booting (`ready`); guard so a\n // late callback never double-registers.\n const tools = ctx.get('tools')\n if (tools !== undefined) {\n for (const tool of makeTools(env)) tools.register(tool)\n } else {\n let done = false\n const registerTools = (): void => {\n if (done) return\n const registry = ctx.get('tools')\n if (registry === undefined) return\n done = true\n for (const tool of makeTools(env)) registry.register(tool)\n }\n ctx.on('internal/service', (name: unknown) => {\n if (name === 'tools') registerTools()\n })\n }\n const commands = ctx.get('commands')\n if (commands !== undefined) {\n commands.register(acpCommand(env))\n } else {\n let done = false\n const registerCommand = (): void => {\n if (done) return\n const registry = ctx.get('commands')\n if (registry === undefined) return\n done = true\n registry.register(acpCommand(env))\n }\n ctx.on('internal/service', (name: unknown) => {\n if (name === 'commands') registerCommand()\n })\n }\n if (this.config.autoNudge) {\n ctx.on('agent/pre-step', async (payload, next) => {\n const decision = await next()\n if (decision.kind === 'reject') return decision\n const window = await this.windowFor(payload.agent)\n const outcome = buildNudge(payload.agent, { ...env, modelContextLimit: window.limit }, this.lastNudgeTurn)\n if (outcome === null) return decision\n return { kind: 'enter', messages: [...decision.messages, outcome.message] }\n })\n }\n // The load-bearing ACP guidance lives in the system prompt ONCE; nudges\n // stay short and advisory (model-driven: the model decides).\n const systemPrompt = ctx.get('systemPrompt')\n if (systemPrompt !== undefined) {\n systemPrompt.section({\n name: 'billion-context-dsh',\n order: ACP_SYSTEM_PROMPT_ORDER,\n text: ACP_SYSTEM_PROMPT,\n })\n }\n }\n\n /**\n * Resolve the effective context window for an agent. An explicitly\n * configured `modelContextLimit` always wins (no probe). Otherwise probe the\n * model's real window via `agent.ctx.llm.resolveModelInfo` (cached per\n * provider/model route, probe failures cached too) and fall back to\n * DEFAULT_CONTEXT_WINDOW when auto-detection is disabled or unavailable.\n */\n async windowFor(agent: Agent): Promise<AcpWindow> {\n if (this.config.modelContextLimit !== undefined) {\n return { limit: this.config.modelContextLimit, source: 'explicit' }\n }\n const provider = agent.options.provider ?? ''\n const model = agent.options.model ?? ''\n const key = `${provider}\\0${model}`\n const cached = this.windowCache.get(key)\n if (cached !== undefined) return cached\n let window: AcpWindow\n if (!this.config.autoModelContextLimit) {\n window = { limit: DEFAULT_CONTEXT_WINDOW, source: 'default', provider, model }\n } else {\n const detected = await detectContextWindow(agent, provider, model)\n window = detected === null\n ? { limit: DEFAULT_CONTEXT_WINDOW, source: 'default', provider, model }\n : { limit: detected, source: 'auto', provider, model }\n }\n this.windowCache.set(key, window)\n return window\n }\n\n /** ACP is model-driven: automatic pressure policy never summarizes by itself. */\n override async compactIfNeeded(\n _agent: CompactionAgentContext,\n _trigger: CompactionTrigger,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /** Explicit idle-session compaction: ACP leaves the decision to the model. */\n override async compactNow(\n _agent: ManualCompactAgentContext,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /**\n * The model-driven path lands through the `compress` tool, which runs the\n * full durable transaction directly. This seam method rejects with guidance:\n * automatic summarization is exactly what ACP replaces.\n */\n override async compactRegion(\n _start: number,\n _end: number,\n _agent: CompactionAgentContext,\n signal?: AbortSignal,\n ): Promise<CompactionResult> {\n signal?.throwIfAborted()\n throw new ManualCompactionError(\n 'summary',\n 'billion-context-dsh is model-driven: use the compress tool instead of automatic summarization',\n )\n }\n}\n\nexport default AcpCompactionEngine\n","/**\n * M2 — per-session ACP kernel state.\n *\n * The in-memory map holds the exact acp-kernel `CompressionState` while a\n * session is live. Durability does not rely on a sidecar file: every durable\n * compression writes a `compaction/summary` event whose shadowed range and\n * summary re-derive the block ledger (`rebuildBlockLedger` in region.ts), so a\n * restarted engine can answer decompress/search/status from the session log\n * alone — DSH's \"log is the source of truth\" model.\n *\n * Tier-2/3 distillation additionally requires the kernel state to KNOW the\n * blocks: `syncBlocks` deactivates a block whose consumed messages are absent\n * from the message array, and `resolveBoundaries` refuses to anchor a block\n * ref it cannot find — so on first access for a session that already has\n * durable blocks (e.g. after a server restart), the kernel blocks are\n * REHYDRATED from the ledger before use. Live updates continue through `set`.\n * @module billion-context-dsh/state\n */\n\nimport type { Session, SessionEvent } from '@deepseek-ai/dsh-session'\nimport { createInitialState, type CompressionBlock, type CompressionState } from 'acp-kernel'\nimport { rebuildBlockLedger } from './region.ts'\n\n/** Rebuild kernel `CompressionBlock`s from the durable ledger (no kernel run needed). */\nfunction rebuildKernelBlocks(events: readonly SessionEvent[]): CompressionBlock[] {\n const ledger = rebuildBlockLedger(events)\n if (ledger.length === 0) return []\n // Durable compactionId → kernel block ref (bN), recorded or synthesised.\n const kernelIdOf = new Map<string, string>()\n const parentKernelIds = new Map<string, string[]>()\n let next = 1\n for (const entry of ledger) {\n let kernelBlockId: string\n if (entry.kernelBlockId !== undefined && /^b\\d+$/.test(entry.kernelBlockId)) {\n kernelBlockId = entry.kernelBlockId\n const num = Number(kernelBlockId.slice(1))\n if (Number.isInteger(num)) next = Math.max(next, num + 1)\n } else {\n kernelBlockId = `b${next}`\n next += 1\n }\n kernelIdOf.set(entry.blockId, kernelBlockId)\n parentKernelIds.set(\n entry.blockId,\n entry.parentBlockIds\n .map((parent) => kernelIdOf.get(parent))\n .filter((id): id is string => id !== undefined),\n )\n }\n const consumed = new Set<string>()\n for (const entry of ledger) {\n for (const parent of entry.parentBlockIds) consumed.add(parent)\n }\n const blocks: CompressionBlock[] = []\n for (const entry of ledger) {\n const blockId = kernelIdOf.get(entry.blockId)!\n // The kernel anchors a block by its effectiveMessageIds. Since the tier\n // feature, the transaction records the kernel block's raw coverage\n // (direct/effective message ids) verbatim, so rehydration is faithful —\n // a tier-2 block's coverage is its parents' ORIGINALS, not the checkpoint\n // node it shadows. Legacy blocks fall back to the shadowed seqs (tier 1)\n // or the checkpoint node (tier > 1; multi-tool-call assistant messages in\n // legacy blocks lose bare-seq coverage — a documented legacy limitation).\n const direct = entry.directMessageIds ?? [...entry.shadowedSeqs.map(String)]\n const effective = entry.effectiveMessageIds\n ?? (entry.tier > 1\n ? (entry.summarySeq === undefined ? [...entry.shadowedSeqs.map(String)] : [String(entry.summarySeq)])\n : [...entry.shadowedSeqs.map(String)])\n blocks.push({\n blockId,\n runId: `r${blocks.length + 1}`,\n tier: entry.tier,\n summary: entry.summary,\n directMessageIds: [...direct],\n effectiveMessageIds: [...effective],\n directBlockIds: parentKernelIds.get(entry.blockId) ?? [],\n compressedTokens: entry.shadowedTokenCount,\n createdAt: entry.createdAt,\n survivedCount: 0,\n generation: 'young',\n active: !consumed.has(entry.blockId),\n })\n }\n return blocks\n}\n\n/** The next kernel block id after the rehydrated blocks (or the initial 1). */\nfunction nextBlockIdAfter(events: readonly SessionEvent[]): number {\n const blocks = rebuildKernelBlocks(events)\n let max = 0\n for (const block of blocks) {\n const num = Number(block.blockId.slice(1))\n if (Number.isInteger(num)) max = Math.max(max, num)\n }\n return max + 1\n}\n\nexport class AcpStateStore {\n private readonly states = new Map<string, CompressionState>()\n\n /** Kernel state for one session, initialised on first access. */\n stateFor(session: Session): CompressionState {\n const id = session.id\n const existing = this.states.get(id)\n if (existing !== undefined) return existing\n const state = createInitialState()\n if (session.events.some((event) => event.type === 'compaction/summary')) {\n state.blocks = rebuildKernelBlocks(session.events)\n state.nextBlockId = nextBlockIdAfter(session.events)\n }\n this.states.set(id, state)\n return state\n }\n\n set(session: Session, state: CompressionState): void {\n this.states.set(session.id, state)\n }\n\n delete(session: Session): void {\n this.states.delete(session.id)\n }\n}\n","/**\n * M5 — durable region transaction and the log-rebuilt block ledger.\n *\n * Modeled on `dsh-compaction-basic/src/region.ts` (which is package-internal\n * and not exported by the seam): validate the surface range and tool-call/result\n * pairing, take the durable `compaction/start` lock, record `compaction/summary`\n * as the shadow price, land the `user/message` surface replacement carrying the\n * summary under `compactCheckpointSource`, and release the lock with\n * `compaction/end`. The original events stay in the append-only log, so\n * decompress/search/status can rebuild everything from the log.\n * @module billion-context-dsh/region\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { Session, SessionEvent, SessionEventMap } from '@deepseek-ai/dsh-session'\nimport {\n CompactionId,\n compactCheckpointSource,\n toolPairingBalancedAfter,\n toolPairingBalancedBefore,\n} from '@deepseek-ai/dsh-compaction'\nimport { createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'\nimport { defaultCountTokens } from 'acp-kernel'\nimport { extractEventText, extractText } from './messages.ts'\n\n/** One durable ACP block as rebuilt from the session log. */\nexport interface AcpBlockLedgerEntry {\n /** The compaction transaction id (stable block identity). */\n readonly blockId: string\n readonly summary: string\n readonly shadowedSeqs: readonly number[]\n readonly shadowedTokenCount: number\n readonly start: number\n readonly end: number\n /** Compression tier: 1 (message range), 2 (distills tier-1 blocks), 3 (distills tier-2 blocks). Legacy blocks default to 1. */\n readonly tier: 1 | 2 | 3\n /** Compaction ids of the blocks this block distilled (parents). Empty for tier-1 blocks. */\n readonly parentBlockIds: readonly string[]\n /** The acp-kernel block id (`bN`) created for this transaction — absent for legacy blocks (synthesised by order). */\n readonly kernelBlockId?: string\n /** The surface seq of this block's checkpoint summary node (derived from the log; null when the node is gone). */\n readonly summarySeq?: number\n /** The kernel block's raw direct/effective message ids at creation (recorded since the tier feature; absent for legacy). */\n readonly directMessageIds?: readonly string[]\n readonly effectiveMessageIds?: readonly string[]\n /** Unix epoch ms of the compaction/summary event. */\n readonly createdAt: number\n}\n\n/** The open turn number, or null when the log ends between turns. */\nexport function findOpenTurn(events: readonly SessionEvent[]): number | null {\n let open: number | null = null\n for (const event of events) {\n if (event.type === 'turn/start') open = event.data.turn\n else if (event.type === 'turn/end' && event.data.turn === open) open = null\n }\n return open\n}\n\n/** Reject a second concurrent compaction for the same session. */\nexport function assertNoActiveCompaction(events: readonly SessionEvent[]): void {\n let active = false\n for (const event of events) {\n if (event.type === 'compaction/start') active = true\n else if (event.type === 'compaction/end') active = false\n }\n if (active) {\n throw new Error('billion-context-dsh: another compaction is already active for this session')\n }\n}\n\n/**\n * Whether the surface node at `seq` projects to CoreMessage(s) whose ref key\n * is the bare seq — user messages, tool results, and text-only or SINGLE\n * tool-call assistant messages all do. Multi-tool-call assistant messages\n * project to `${seq}#${callId}` ids (projectEvent) and therefore carry NO\n * bare-`${seq}` ref, so compress's byRaw lookup can never resolve them as\n * range edges. resolveSurfaceRange treats such edges as unbalanced and shifts\n * them to the nearest clean cut.\n */\nfunction hasPlainRef(session: Session, seq: number): boolean {\n const event = session.events[seq]\n if (event === undefined) return false\n switch (event.type) {\n case 'user/message':\n case 'tool/result':\n return extractEventText(event).trim().length > 0\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = Array.isArray(content)\n ? content.filter(\n (block) => block !== null && typeof block === 'object' && (block as { type?: string }).type === 'tool-call',\n )\n : []\n if (calls.length > 1) return false\n // One tool-call: projectEvent emits a bare-seq CoreMessage unconditionally.\n // Zero: only when the text is non-empty.\n return calls.length === 1 || extractEventText(event).trim().length > 0\n }\n default:\n return false\n }\n}\n\n/**\n * Validate one inclusive surface span and adjust its edges to a\n * tool-pairing-balanced range whose boundaries carry a bare-seq ref. Missing\n * or reversed ranges still throw. An edge that sits inside a tool-call/result\n * pair — or on a multi-tool-call assistant message that has no bare-seq ref —\n * is first nudged inward to the nearest clean cut; if that collapses the range\n * (e.g. the model asked for a SINGLE tool result, which can never be balanced\n * alone), the range EXPANDS outward to the enclosing clean pair instead — a\n * lone tool message is almost always a \"consumed output\" the model genuinely\n * wants to compress. The returned range is what a caller should actually shadow.\n */\nexport function resolveSurfaceRange(\n session: Session,\n start: number,\n end: number,\n): { start: number; end: number } {\n const nodes = session.surface.nodes\n const requestedStartIdx = nodes.indexOf(start)\n const requestedEndIdx = nodes.indexOf(end)\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} not in the current surface — `\n + 'surface seqs are sparse message nodes (only user/message, assistant/message, '\n + 'tool/result events); consult acp_status for the current surface range',\n )\n }\n if (requestedStartIdx > requestedEndIdx) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n // Belt-and-braces: the surface can be locally out of order after surface\n // replacements, so index order alone does not guarantee value order.\n if (start > end) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n // A boundary must be BOTH tool-pairing-balanced AND carry a bare-seq ref.\n const cleanBefore = (index: number): boolean =>\n toolPairingBalancedBefore(session, nodes[index]!) && hasPlainRef(session, nodes[index]!)\n const cleanAfter = (index: number): boolean =>\n toolPairingBalancedAfter(session, nodes[index]!) && hasPlainRef(session, nodes[index]!)\n let startIdx = requestedStartIdx\n let endIdx = requestedEndIdx\n // First pass: nudge inward to the nearest clean cuts.\n while (startIdx <= endIdx && !cleanBefore(startIdx)) {\n startIdx += 1\n }\n while (endIdx >= startIdx && !cleanAfter(endIdx)) {\n endIdx -= 1\n }\n if (startIdx <= endIdx && nodes[startIdx]! <= nodes[endIdx]!) {\n return { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n // Second pass: the inward pass collapsed (a lone tool message) — expand\n // outward from the REQUESTED span to the smallest clean enclosing pair.\n startIdx = requestedStartIdx\n endIdx = requestedEndIdx\n while (startIdx > 0 && !cleanBefore(startIdx)) {\n startIdx -= 1\n }\n while (endIdx < nodes.length - 1 && !cleanAfter(endIdx)) {\n endIdx += 1\n }\n // Value order guard: the surface is locally non-monotonic after replacements\n // (a checkpoint seq inserted ahead of older residual nodes), so index order\n // alone is not enough — never return a span whose end seq is numerically\n // BEFORE its start seq. The caller (nudge / compress) skips such a span.\n if (cleanBefore(startIdx) && cleanAfter(endIdx) && nodes[startIdx]! <= nodes[endIdx]!) {\n return { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n throw new Error(\n `billion-context-dsh: no tool-pairing-balanced range around seq ${start}..${end} — `\n + 'narrow the range or consult acp_status for the current surface',\n )\n}\n\n/** The surface seqs shadowed by the inclusive positional span. */\nexport function shadowedSeqsOf(session: Session, start: number, end: number): number[] {\n const nodes = session.surface.nodes\n const startIdx = nodes.indexOf(start)\n const endIdx = nodes.indexOf(end)\n return nodes.slice(startIdx, endIdx + 1)\n}\n\nexport interface CompactionTransactionInput {\n readonly start: number\n readonly end: number\n readonly shadowedSeqs: readonly number[]\n readonly summary: ContentBlock[]\n readonly shadowedTokenCount: number\n readonly provider: string\n readonly model: string\n /** Compression tier of this block (default 1). */\n readonly tier?: 1 | 2 | 3\n /** The acp-kernel block id (`bN`) created by the kernel for this transaction. */\n readonly kernelBlockId?: string\n /** Compaction ids of the blocks distilled into this one. */\n readonly parentBlockIds?: readonly string[]\n /** The kernel block's direct/effective message ids (raw CoreMessage ids) — recorded for faithful rehydration. */\n readonly directMessageIds?: readonly string[]\n readonly effectiveMessageIds?: readonly string[]\n}\n\n/**\n * ACP tier extension fields carried on `compaction/summary` events. The\n * upstream dsh-compaction event type does not know them, so reads and writes\n * go through this precise intersection (never `any`).\n */\nexport interface AcpCompactionSummaryFields {\n /** Compression tier (1/2/3) — 1 = message range, 2 = distills tier-1, 3 = distills tier-2. */\n readonly tier?: 1 | 2 | 3\n /** The acp-kernel block id (`bN`) created for this transaction. */\n readonly kernelBlockId?: string\n /** Durable compaction ids of the blocks distilled into this one. */\n readonly parentBlockIds?: readonly string[]\n /**\n * The kernel block's direct message ids (raw CoreMessage ids) at creation —\n * recorded so a restarted engine rehydrates the SAME coverage (a tier-2\n * block's coverage is its parents' originals, not the checkpoint node).\n */\n readonly directMessageIds?: readonly string[]\n /** The kernel block's effective message ids (raw CoreMessage ids) at creation. */\n readonly effectiveMessageIds?: readonly string[]\n}\n\ntype CompactionSummaryData = SessionEventMap['compaction/summary']\n\n/** Read a `compaction/summary` event's data including the ACP tier extension fields. */\nexport function readCompactionSummary(event: SessionEvent): CompactionSummaryData & AcpCompactionSummaryFields {\n return event.data as CompactionSummaryData & AcpCompactionSummaryFields\n}\n\n/**\n * Run one durable compression transaction. Throws on invalid state; on success\n * the four events are in the log and the surface has one summary node.\n */\nexport function runCompactionTransaction(\n session: Session,\n input: CompactionTransactionInput,\n): { compactionId: string; seqs: number[] } {\n assertNoActiveCompaction(session.events)\n const turn = findOpenTurn(session.events)\n const compactionId = CompactionId(randomUUID())\n const seqs: number[] = []\n\n seqs.push(session.append('compaction/start', { compactionId, turn }).seq)\n seqs.push(session.append('compaction/summary', {\n compactionId,\n summary: input.summary,\n shadowedRange: { start: input.start, end: input.end },\n shadowedSeqs: [...input.shadowedSeqs],\n shadowedTokenCount: input.shadowedTokenCount,\n provider: input.provider,\n model: input.model,\n tier: input.tier ?? 1,\n ...(input.kernelBlockId === undefined ? {} : { kernelBlockId: input.kernelBlockId }),\n ...(input.parentBlockIds === undefined || input.parentBlockIds.length === 0\n ? {}\n : { parentBlockIds: [...input.parentBlockIds] }),\n ...(input.directMessageIds === undefined ? {} : { directMessageIds: [...input.directMessageIds] }),\n ...(input.effectiveMessageIds === undefined ? {} : { effectiveMessageIds: [...input.effectiveMessageIds] }),\n } as CompactionSummaryData & AcpCompactionSummaryFields).seq)\n\n const message = createUserMessage({\n content: input.summary,\n source: compactCheckpointSource(compactionId),\n })\n seqs.push(session.append('user/message', message, {\n surfaceOp: { op: 'replace', start: input.start, end: input.end },\n sourceEventSeqs: [...input.shadowedSeqs],\n }).seq)\n\n seqs.push(session.append('compaction/end', { compactionId, turn }).seq)\n return { compactionId, seqs }\n}\n\n/** The seq of a compaction's checkpoint summary node in the log (visible or shadowed). */\nfunction summarySeqOfCompaction(events: readonly SessionEvent[], compactionId: string): number | null {\n for (const event of events) {\n if (event.type !== 'user/message') continue\n const source = (event.data as { source?: { plugin?: string; compactionId?: string } }).source\n if (source?.plugin === 'compact' && source.compactionId === compactionId) return event.seq\n }\n return null\n}\n\n/** Rebuild the block ledger from the durable log (no kernel state needed). */\nexport function rebuildBlockLedger(events: readonly SessionEvent[]): AcpBlockLedgerEntry[] {\n const ledger: AcpBlockLedgerEntry[] = []\n for (const event of events) {\n if (event.type !== 'compaction/summary') continue\n const data = readCompactionSummary(event)\n // Blocks written before the token-accounting fix carry shadowedTokenCount\n // 0; backfill from the shadowed originals still in the log so acp_status\n // reports real reclaimed tokens.\n let shadowedTokenCount = data.shadowedTokenCount\n if (shadowedTokenCount === 0) {\n shadowedTokenCount = 0\n for (const seq of data.shadowedSeqs) {\n const original = events[seq]\n if (original !== undefined) shadowedTokenCount += defaultCountTokens(extractEventText(original))\n }\n }\n const tier = data.tier === 2 || data.tier === 3 ? data.tier : 1\n const parentBlockIds: string[] = Array.isArray(data.parentBlockIds) ? [...data.parentBlockIds] : []\n const directMessageIds: string[] | undefined = Array.isArray(data.directMessageIds) ? [...data.directMessageIds] : undefined\n const effectiveMessageIds: string[] | undefined = Array.isArray(data.effectiveMessageIds) ? [...data.effectiveMessageIds] : undefined\n const summarySeq = summarySeqOfCompaction(events, data.compactionId)\n ledger.push({\n blockId: data.compactionId,\n summary: extractText(data.summary),\n shadowedSeqs: [...data.shadowedSeqs],\n shadowedTokenCount,\n start: data.shadowedRange.start,\n end: data.shadowedRange.end,\n tier,\n parentBlockIds,\n ...(typeof data.kernelBlockId === 'string' ? { kernelBlockId: data.kernelBlockId } : {}),\n ...(summarySeq === null ? {} : { summarySeq }),\n ...(directMessageIds === undefined ? {} : { directMessageIds }),\n ...(effectiveMessageIds === undefined ? {} : { effectiveMessageIds }),\n createdAt: event.time,\n })\n }\n return ledger\n}\n\n/** One self-computed compressible span of the current surface. */\nexport interface SeqCompressibleRange {\n readonly start: number\n readonly end: number\n readonly count: number\n readonly tokens: number\n}\n\n/** Whether a surface user message is a compaction checkpoint node (already compressed). */\nfunction isCheckpointNode(event: SessionEvent): boolean {\n if (event.type !== 'user/message') return false\n const source = (event.data as { source?: { plugin?: string } }).source\n return source?.plugin === 'compact'\n}\n\n/**\n * Compute compressible spans directly from the surface — independent of the\n * kernel's ref map, which can drift after surface replacements in long\n * sessions and hide large tool results from the nudge range table. Skips the\n * recent protected tail, the last user message, and compaction checkpoints;\n * edges are then balanced through resolveSurfaceRange. Ranges are ordered by\n * size (largest reclaimed first).\n */\nexport function buildCompressibleSeqRanges(\n session: Session,\n opts: { preserveRecent?: number } = {},\n): SeqCompressibleRange[] {\n const nodes = session.surface.nodes\n const preserve = opts.preserveRecent ?? 5\n const protectedSeqs = new Set<number>()\n for (const seq of nodes.slice(-preserve)) protectedSeqs.add(seq)\n for (let index = nodes.length - 1; index >= 0; index -= 1) {\n const event = session.events[nodes[index]!]\n if (event?.type === 'user/message' && !isCheckpointNode(event)) {\n protectedSeqs.add(nodes[index]!)\n break\n }\n }\n const raw: SeqCompressibleRange[] = []\n let cur: SeqCompressibleRange | null = null\n const flush = (): void => {\n if (cur !== null) raw.push(cur)\n cur = null\n }\n for (const seq of nodes) {\n const event = session.events[seq]\n if (event === undefined || protectedSeqs.has(seq) || isCheckpointNode(event)) {\n flush()\n continue\n }\n // Surface nodes can be locally out of order after surface replacements in\n // long sessions; a node with a SMALLER seq than the running segment would\n // produce a reversed range (e.g. 110295..106762). Break the segment so\n // ranges always stay start <= end.\n if (cur !== null && seq < cur.start) {\n flush()\n cur = null\n }\n const tokens = defaultCountTokens(extractEventText(event))\n if (cur === null) {\n cur = { start: seq, end: seq, count: 1, tokens }\n } else {\n cur = { start: cur.start, end: seq, count: cur.count + 1, tokens: cur.tokens + tokens }\n }\n }\n flush()\n const out: SeqCompressibleRange[] = []\n for (const range of raw) {\n try {\n const { start, end } = resolveSurfaceRange(session, range.start, range.end)\n const count = range.count\n out.push({ start, end, count, tokens: range.tokens })\n } catch {\n // Cannot be balanced into a compressible span — skip.\n }\n }\n return out.sort((a, b) => b.tokens - a.tokens)\n}\n\n/**\n * A compact human-readable description of the current surface for the model:\n * node count plus the first/last message seqs. Surface seqs are sparse (the\n * event log interleaves non-message events and expanded delta batches), so a\n * model that never saw the nudge range table — e.g. low-pressure sessions\n * where no nudge fires — cannot guess its own seq space. acp_status and the\n * nudge's range table both surface this so compress edges can be located\n * without blind probing.\n */\nexport function surfaceSummary(session: Session): string {\n const nodes = session.surface.nodes\n if (nodes.length === 0) return 'empty'\n const first = nodes[0]!\n const last = nodes[nodes.length - 1]!\n return `${nodes.length} nodes, seqs ${first}..${last}`\n}\n\n/** One block as seen by the tier machinery: durable id ↔ kernel ref (`bN`). */\nexport interface AcpBlockRegistryEntry {\n /** The durable compaction id. */\n readonly blockId: string\n /** The acp-kernel block ref (`bN`); synthesised by log order for legacy blocks. */\n readonly kernelBlockId: string\n readonly tier: 1 | 2 | 3\n /** The surface seq of this block's checkpoint summary node (null when gone). */\n readonly summarySeq: number | null\n /** True until a LATER block distills this one. Only active blocks are distillable. */\n readonly active: boolean\n readonly parentBlockIds: readonly string[]\n}\n\n/**\n * Rebuild the compactionId ↔ kernel-block-ref registry from the durable log.\n * Legacy blocks (pre-tier, no recorded `kernelBlockId`) are synthesised as\n * `b1`, `b2`, … in log order; recorded ids are kept as-is. A block is active\n * until a later block lists it as a parent.\n */\nexport function blockRegistry(session: Session): AcpBlockRegistryEntry[] {\n const ledger = rebuildBlockLedger(session.events)\n const kernelIdOf = new Map<string, string>()\n const raw: AcpBlockRegistryEntry[] = []\n let next = 1\n for (const entry of ledger) {\n let kernelBlockId: string\n if (entry.kernelBlockId !== undefined && /^b\\d+$/.test(entry.kernelBlockId)) {\n kernelBlockId = entry.kernelBlockId\n const num = Number(kernelBlockId.slice(1))\n if (Number.isInteger(num)) next = Math.max(next, num + 1)\n } else {\n kernelBlockId = `b${next}`\n next += 1\n }\n kernelIdOf.set(entry.blockId, kernelBlockId)\n raw.push({\n blockId: entry.blockId,\n kernelBlockId,\n tier: entry.tier,\n summarySeq: entry.summarySeq ?? null,\n active: true,\n parentBlockIds: [...entry.parentBlockIds],\n })\n }\n const consumed = new Set<string>()\n for (const entry of raw) {\n for (const parent of entry.parentBlockIds) consumed.add(parent)\n }\n return raw.map((entry) => ({\n ...entry,\n active: !consumed.has(entry.blockId),\n }))\n}\n\n/**\n * The kernel block ref (`bN`) for a surface seq, when that seq is the\n * checkpoint summary node of a block — the edge the model must use to\n * distill (T2/T3). Active blocks distill; a stale (already-distilled) node\n * still maps to its `bN` so the kernel reports \"already compressed\" instead\n * of silently folding the summary as a plain message. Returns null for\n * anything else (plain messages, non-checkpoint nodes).\n */\nexport function blockRefForSummarySeq(session: Session, seq: number): string | null {\n const event = session.events[seq]\n if (event?.type !== 'user/message') return null\n const source = (event.data as { source?: { plugin?: string; compactionId?: string } }).source\n if (source?.plugin !== 'compact' || source.compactionId === undefined) return null\n const entry = blockRegistry(session).find((r) => r.blockId === source.compactionId)\n if (entry === undefined) return null\n return entry.kernelBlockId\n}\n\n/** The durable compaction ids distilled by the given kernel block refs (`bN`). */\nexport function compactionIdsOfKernelBlocks(session: Session, kernelBlockIds: readonly string[]): string[] {\n if (kernelBlockIds.length === 0) return []\n const byKernel = new Map(blockRegistry(session).map((r) => [r.kernelBlockId, r.blockId]))\n return kernelBlockIds\n .map((id) => byKernel.get(id))\n .filter((id): id is string => id !== undefined)\n}\n\n/** The checkpoint summary seq of an ACTIVE kernel block (`bN`), or null. */\nexport function summarySeqOfKernelBlock(session: Session, kernelBlockId: string): number | null {\n const entry = blockRegistry(session).find((r) => r.kernelBlockId === kernelBlockId)\n return entry?.active ? entry.summarySeq : null\n}\n\n/** The durable block whose checkpoint node sits at `seq` (or null). */\nfunction checkpointBlockIdOf(events: readonly SessionEvent[], seq: number): string | null {\n const event = events[seq]\n if (event?.type !== 'user/message') return null\n const source = (event.data as { source?: { plugin?: string; compactionId?: string } }).source\n if (source?.plugin !== 'compact' || source.compactionId === undefined) return null\n return source.compactionId\n}\n\n/**\n * The shadowed seqs of a block, recursing into distilled parent blocks: a\n * tier-2 block shadows its parent's checkpoint node, so recovering its\n * originals requires expanding that node into the parent block's own shadowed\n * seqs. Cycle-safe (a block can never be its own ancestor).\n */\nexport function expandShadowedSeqs(session: Session, blockId: string): number[] {\n const ledger = rebuildBlockLedger(session.events)\n const byId = new Map(ledger.map((entry) => [entry.blockId, entry]))\n const root = byId.get(blockId)\n if (root === undefined) return []\n const out: number[] = []\n const seen = new Set<string>()\n const visit = (entry: AcpBlockLedgerEntry): void => {\n if (seen.has(entry.blockId)) return\n seen.add(entry.blockId)\n for (const seq of entry.shadowedSeqs) {\n const childId = checkpointBlockIdOf(session.events, seq)\n const child = childId === null ? undefined : byId.get(childId)\n if (child !== undefined) visit(child)\n else out.push(seq)\n }\n }\n visit(root)\n return out\n}\n","/**\n * M1 — session-log projection: DSH surface events → acp-kernel CoreMessage.\n *\n * The ACP kernel is message-array based; DSH is event-log based. This module\n * is the bridge in the direction the engine needs (projectEvent /\n * eventsToCoreMessages). The reverse direction (CoreMessage[] → session\n * appends) is the M5 region transaction's job.\n * Mirrors billion-context-pi's `projectMessage`/`entriesToCoreMessages`\n * against DSH event shapes (see V-verification: SurfaceEventType =\n * 'user/message' | 'assistant/message' | 'tool/result').\n * @module billion-context-pi-dsh/messages\n */\n\nimport type { CoreMessage } from 'acp-kernel'\nimport type { SessionEvent } from '@deepseek-ai/dsh-session'\n\n/**\n * Extract plain text from a DSH content block array or string.\n *\n * Recursive: a real DSH `tool-result` block is `{ type: 'tool-result',\n * toolCallId, content: ContentBlock[] }` — the inner `content` array holds\n * the actual `text` blocks, so a top-level-only walk would drop every tool\n * result from the projection (and with it the seq's ref assignment, breaking\n * compress boundary resolution). Nested arrays are flattened depth-first.\n */\nexport function extractText(content: unknown): string {\n if (typeof content === 'string') return content\n if (!Array.isArray(content)) return ''\n const parts: string[] = []\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const b = block as { type?: unknown; text?: unknown; content?: unknown }\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text)\n } else if (Array.isArray(b.content)) {\n parts.push(extractText(b.content))\n }\n }\n return parts.join('\\n')\n}\n\ninterface ToolCallBlock {\n type: 'tool-call'\n id?: string\n name?: string\n arguments?: unknown\n}\n\nfunction toolCallsOf(content: unknown): ToolCallBlock[] {\n if (!Array.isArray(content)) return []\n return content.filter((b): b is ToolCallBlock => (b as { type?: string }).type === 'tool-call')\n}\n\nfunction stringifyArgs(args: unknown): string {\n if (!args) return ''\n if (typeof args === 'string') return args\n try {\n return JSON.stringify(args)\n } catch {\n return String(args)\n }\n}\n\n/**\n * Project one surface message event into CoreMessage(s).\n * - user/message → user text (verbatim content)\n * - assistant/message → assistant text, or one CoreMessage per tool-call\n * - tool/result → tool result (toolName/toolCallId, role 'tool')\n * Non-surface events project to nothing.\n */\nexport function projectEvent(event: SessionEvent): CoreMessage[] {\n switch (event.type) {\n case 'user/message': {\n const text = extractText((event.data as { content?: unknown }).content)\n return text.length > 0 ? [{ id: String(event.seq), role: 'user', contentType: 'text', text }] : []\n }\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = toolCallsOf(content)\n const text = extractText(content)\n if (calls.length === 0) {\n return text.trim().length > 0\n ? [{ id: String(event.seq), role: 'assistant', contentType: 'text', text }]\n : []\n }\n if (calls.length === 1) {\n const call = calls[0]!\n const argStr = stringifyArgs(call.arguments)\n const body = argStr && text ? `${text}\\n${argStr}` : argStr || text\n return [{\n id: String(event.seq),\n role: 'assistant',\n contentType: 'tool-call',\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: body,\n }]\n }\n return calls.map((call) => ({\n id: `${event.seq}#${call.id ?? ''}`,\n role: 'assistant' as const,\n contentType: 'tool-call' as const,\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: stringifyArgs(call.arguments) || text,\n }))\n }\n case 'tool/result': {\n const message = (event.data as {\n message?: { content?: unknown; toolName?: string; toolCallId?: string }\n }).message\n const text = extractText(message?.content)\n if (text.length === 0) return []\n return [{\n id: String(event.seq),\n role: 'tool',\n contentType: 'tool-result',\n toolName: message?.toolName ?? '',\n toolCallId: message?.toolCallId ?? '',\n text,\n }]\n }\n default:\n return []\n }\n}\n\n/** Project a session's message events into CoreMessage[] in log order. */\nexport function eventsToCoreMessages(events: readonly SessionEvent[]): CoreMessage[] {\n const out: CoreMessage[] = []\n for (const event of events) out.push(...projectEvent(event))\n return out\n}\n\n/** The surface-visible message events of a session, in model-visible order. */\nexport function surfaceEventsOf(session: import('@deepseek-ai/dsh-session').Session): SessionEvent[] {\n return session.surface.nodes\n .map((seq) => session.events[seq])\n .filter((event): event is SessionEvent => event !== undefined)\n}\n\n/**\n * ALL message-type events in log order — the visible surface PLUS everything\n * shadowed by compression. The ACP kernel deactivates any block whose consumed\n * message ids are absent from the array it is given (syncBlocks), and refuses\n * to anchor a block boundary that cannot find its messages, so T2/T3\n * distillation requires the full log, not just the visible surface.\n */\nexport function allLogMessages(session: import('@deepseek-ai/dsh-session').Session): CoreMessage[] {\n return eventsToCoreMessages(session.events)\n}\n\n/** Extract the model-facing text of any surface message event. */\nexport function extractEventText(event: SessionEvent): string {\n switch (event.type) {\n case 'user/message':\n return extractText((event.data as { content?: unknown }).content)\n case 'assistant/message':\n return extractText((event.data as { message?: { content?: unknown } }).message?.content)\n case 'tool/result':\n return extractText((event.data as { message?: { content?: unknown } }).message?.content)\n default:\n return ''\n }\n}\n","/**\n * M3 — the four model tools: compress / decompress / search_context /\n * acp_status, registered through `ctx.tools` (defineTool).\n *\n * compress is the heart of ACP: the model writes the summary and the tool\n * lands it as a durable surface replacement (no second LLM summarization\n * call). decompress recovers shadowed content read-only from the log (DSH\n * keeps the originals — V5). search_context scores blocks rebuilt from the\n * log. acp_status reports the block ledger and pressure.\n * @module billion-context-dsh/tools\n */\n\nimport { defineTool, type ToolDefinition, type ToolRunContext } from '@deepseek-ai/dsh-tools'\nimport { defaultCountTokens, type CompressionCore } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { AcpStateStore } from './state.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nimport { windowSourceLabel, type AcpWindow } from './window.ts'\nimport {\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n expandShadowedSeqs,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n surfaceSummary,\n} from './region.ts'\nimport { allLogMessages, eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\n\nexport interface ToolEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n /** Resolve the effective context window for an agent (optional: status falls back to modelContextLimit). */\n readonly windowFor?: (agent: Agent) => Promise<AcpWindow>\n}\n\ninterface TextOutput {\n text: string\n}\n\nfunction textOutput(): {\n schema: { type: 'object'; properties: { text: { type: 'string' } }; additionalProperties: boolean }\n render: (args: unknown, value: TextOutput) => import('@deepseek-ai/dsh-llm').ContentBlock[]\n} {\n return {\n schema: {\n type: 'object',\n properties: { text: { type: 'string' } },\n additionalProperties: false,\n },\n render: (_args, value) => [{ type: 'text', text: value.text }],\n }\n}\n\nfunction requireAgent(exec: ToolRunContext): Agent {\n if (exec.agent === undefined) {\n throw new Error('billion-context-dsh: tool requires an agent execution context')\n }\n return exec.agent\n}\n\nconst compressParameters = {\n topic: { type: 'string' as const, description: 'Fallback topic for entries without their own.' },\n content: {\n type: 'array' as const,\n required: true,\n description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary.',\n items: {\n type: 'object' as const,\n properties: {\n startSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'First surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n endSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'Inclusive last surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n summary: { type: 'string' as const, description: 'Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters.' },\n topic: { type: 'string' as const, description: 'Short label (3-5 words) for this range.' },\n },\n additionalProperties: false,\n },\n },\n} as const\n\n/** Normalize a seq arg: number, \"295\", or \"295#call_00_xxx\" → 295. */\nfunction parseSeq(value: number | string): number {\n const text = String(value).split('#')[0]!.trim()\n const seq = Number(text)\n if (!Number.isInteger(seq) || seq < 0) {\n throw new Error(`billion-context-dsh: invalid seq \"${String(value)}\" — use a surface seq like 295`)\n }\n return seq\n}\n\ninterface CompressArgs {\n topic?: string\n content: Array<{ startSeq: number | string; endSeq: number | string; summary: string; topic?: string }>\n}\n\n/** Resolve seq → kernel ref, then applyCompression and land the transaction. */\nasync function handleCompress(env: ToolEnvironment, args: CompressArgs, exec: ToolRunContext): Promise<TextOutput> {\n const agent = requireAgent(exec)\n const session = agent.session\n const state = env.store.stateFor(session)\n // The kernel gets the FULL log (visible + shadowed): syncBlocks deactivates\n // a block whose consumed messages are absent, and resolveBoundaries refuses\n // to anchor a block ref it cannot find, so tier-2/3 distillation needs the\n // originals present. The token count stays a surface measurement.\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = surfaceMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const config = kernelConfigFor(env)\n\n // Assign refs / advance state exactly like a turn would.\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n const byRaw = turn.state.messageRefs.byRaw\n\n const ranges = args.content.map((range) => {\n const startSeq = parseSeq(range.startSeq)\n const endSeq = parseSeq(range.endSeq)\n // Balance edges FIRST: the requested edges may sit on multi-tool-call\n // assistant messages, which project to `${seq}#${callId}` CoreMessage ids\n // and therefore have NO bare-`${seq}` ref. resolveSurfaceRange shifts them\n // to clean tool-pairing-balanced cuts that always carry a bare ref, so the\n // resolved refs exist and the shadowed span matches the returned range.\n const { start, end } = resolveSurfaceRange(session, startSeq, endSeq)\n // An edge on an ACTIVE block's checkpoint summary node resolves to the\n // kernel block ref (bN) — the boundary that makes applyCompression distill\n // (tier 2/3) instead of folding the summary as a plain message.\n const startBlockRef = blockRefForSummarySeq(session, start)\n const endBlockRef = blockRefForSummarySeq(session, end)\n const startRef = startBlockRef ?? byRaw[String(start)]\n const endRef = endBlockRef ?? byRaw[String(end)]\n if (startRef === undefined || endRef === undefined) {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} has no assigned ref — `\n + 'the range must be on the current surface (run acp_status for the live seq list)',\n )\n }\n return {\n startSeq,\n endSeq,\n start,\n end,\n startRef,\n endRef,\n summary: range.summary,\n ...(range.topic ?? args.topic) === undefined ? {} : { topic: range.topic ?? args.topic },\n }\n })\n\n const applied = env.kernel.applyCompression({\n ranges: ranges.map(({ startRef, endRef, summary, topic }) => ({ startRef, endRef, summary, topic })),\n messages: coreMessages,\n state: turn.state,\n config,\n // Deliberately NOT overriding protectedMessageIds: with the full log the\n // kernel's recent/last-user protection is computed over the same\n // non-block-covered messages as the visible feed, so default behavior is\n // preserved. Any 'Excluded N protected message(s)' warning is surfaced.\n })\n if (applied.result.errors.length > 0) {\n return { text: `compress failed: ${applied.result.errors.join('; ')}` }\n }\n env.store.set(session, applied.state)\n\n // Match freshly created kernel blocks to the requested ranges by their\n // range key (the kernel stamps startRef/endRef onto each new block).\n const previousIds = new Set(turn.state.blocks.map((block) => block.blockId))\n const newBlocks = applied.state.blocks.filter((block) => !previousIds.has(block.blockId))\n const blockByRangeKey = new Map(newBlocks.map((block) => [`${block.startRef}::${block.endRef}`, block]))\n // Warnings carry two shapes: range-prefixed (\"Skipped range (a..b) — …\")\n // attributable to a specific range, and free-form (\"Excluded N protected\n // message(s) …\") attributable to the call as a whole.\n const warningByRangeKey = new Map<string, string[]>()\n const freeWarnings: string[] = []\n for (const warning of applied.result.warnings) {\n const match = /^Skipped range \\((.+?)\\.\\.(.+?)\\)/.exec(warning)\n if (match !== null) {\n const key = `${match[1]}::${match[2]}`\n const list = warningByRangeKey.get(key) ?? []\n list.push(warning)\n warningByRangeKey.set(key, list)\n } else {\n freeWarnings.push(warning)\n }\n }\n\n const lines: string[] = []\n let skippedRanges = 0\n for (let index = 0; index < ranges.length; index += 1) {\n const range = ranges[index]!\n const original = args.content[index]!\n const key = `${range.startRef}::${range.endRef}`\n const block = blockByRangeKey.get(key)\n if (block === undefined) {\n // The kernel skipped this range (already compressed / overlapped): no\n // kernel block was created, so no durable transaction is landed — the\n // ledger must never record a block the kernel does not know.\n skippedRanges += 1\n const warnings = warningByRangeKey.get(key) ?? []\n for (const warning of warnings) lines.push(` ${warning}`)\n continue\n }\n // The edges were already balanced above; shadow exactly that span.\n const { start, end } = range\n const shadowed = shadowedSeqsOf(session, start, end)\n // Estimate the reclaimed tokens from the actual shadowed messages so the\n // durable ledger (compaction/summary.shadowedTokenCount) reports a real\n // number instead of 0.\n let shadowedTokens = 0\n for (const seq of shadowed) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokens += defaultCountTokens(extractEventText(event))\n }\n const tier = block.tier === 2 || block.tier === 3 ? block.tier : 1\n const parentBlockIds = compactionIdsOfKernelBlocks(session, block.directBlockIds)\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: original.summary }],\n shadowedTokenCount: shadowedTokens,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n tier,\n kernelBlockId: block.blockId,\n ...(parentBlockIds.length === 0 ? {} : { parentBlockIds }),\n // Record the kernel block's raw coverage so a restarted engine\n // rehydrates the SAME effective messages (a tier-2 block's coverage is\n // its parents' originals, not the checkpoint node).\n directMessageIds: block.directMessageIds,\n effectiveMessageIds: block.effectiveMessageIds,\n })\n const adjusted = start !== range.startSeq || end !== range.endSeq\n const tierLabel = tier === 1 ? '' : `, tier ${tier}`\n lines.push(\n ` block ${compactionId.slice(0, 8)}: seqs ${start}..${end}, ${shadowed.length} messages shadowed${tierLabel}`\n + (adjusted ? ` (adjusted from ${range.startSeq}..${range.endSeq} to balanced edges)` : ''),\n )\n }\n\n const summaryLine = `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.`\n const warningLines = [...freeWarnings.map((warning) => ` ${warning}`), ...lines]\n const footer = skippedRanges > 0\n ? ` (${skippedRanges} range(s) skipped — see warnings above)`\n : ''\n return { text: `${summaryLine}\\n${[...warningLines, footer].filter((line) => line !== '').join('\\n')}` }\n}\n\nconst decompressParameters = {\n blockId: { type: 'string' as const, required: true, description: 'Block id from acp_status or search_context (the compaction id).' },\n} as const\n\ninterface DecompressArgs {\n blockId: string\n}\n\nfunction handleDecompress(_env: ToolEnvironment, args: DecompressArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args.blockId))\n if (block === undefined) {\n return { text: `decompress: block \"${args.blockId}\" not found (see acp_status for the block list)` }\n }\n const parts: string[] = []\n // Tier-2/3 blocks shadow parent checkpoint nodes: expand to the originals.\n for (const seq of expandShadowedSeqs(session, block.blockId)) {\n const event = session.events[seq]\n const text = event === undefined ? '' : extractEventText(event)\n if (text.length > 0) parts.push(`[seq ${seq}] ${text}`)\n }\n const tierNote = block.tier > 1 ? ` (tier ${block.tier}, distills ${block.parentBlockIds.length} block(s))` : ''\n return {\n text: `Block ${block.blockId} — ${block.summary}${tierNote}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`,\n }\n}\n\nconst searchParameters = {\n query: { type: 'string' as const, required: true, description: 'Search terms to find inside compressed blocks.' },\n limit: { type: 'integer' as const, description: 'Maximum results (default 5).' },\n} as const\n\ninterface SearchArgs {\n query: string\n limit?: number\n}\n\nfunction handleSearch(_env: ToolEnvironment, args: SearchArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const terms = args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n const scored: Array<{ blockId: string; score: number; summary: string }> = []\n for (const block of ledger) {\n const original = block.shadowedSeqs\n .map((seq) => extractEventText(session.events[seq]!))\n .join('\\n')\n const haystack = `${block.summary}\\n${original}`.toLowerCase()\n let score = 0\n for (const term of terms) score += haystack.split(term).length - 1\n if (score > 0) scored.push({ blockId: block.blockId, score, summary: block.summary })\n }\n scored.sort((a, b) => b.score - a.score)\n const top = scored.slice(0, args.limit ?? 5)\n if (top.length === 0) return { text: `search_context: no matches for \"${args.query}\"` }\n return {\n text: `Matches for \"${args.query}\":\\n`\n + top.map((hit) => ` - ${hit.blockId} (score ${hit.score}): ${hit.summary.slice(0, 160)}`).join('\\n')\n + '\\n\\nDecompress with: decompress({ blockId })',\n }\n}\n\nconst statusParameters = {} as const\n\ninterface StatusArgs {\n [key: string]: never\n}\n\nasync function handleStatus(env: ToolEnvironment, _args: StatusArgs, exec: ToolRunContext): Promise<TextOutput> {\n const agent = requireAgent(exec)\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const window = env.windowFor === undefined\n ? { limit: env.modelContextLimit, source: 'explicit' as const }\n : await env.windowFor(agent)\n const limit = window.limit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ` context window: ${limit} (${windowSourceLabel(window)})`,\n ` surface: ${surfaceSummary(session)}`,\n ]\n for (const block of ledger.slice(0, 10)) {\n lines.push(` - ${block.blockId.slice(0, 8)}: seqs ${block.start}..${block.end} (${block.shadowedSeqs.length} msgs) — ${block.summary.slice(0, 80)}`)\n }\n return { text: lines.join('\\n') }\n}\n\n/** Build the four ACP model tools bound to one engine. */\nexport function makeTools(env: ToolEnvironment): ToolDefinition[] {\n return [\n defineTool({\n name: 'compress',\n description:\n 'Replace older conversation ranges with dense summaries you write. '\n + 'Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). '\n + 'Batch multiple unrelated ranges in one call (each content entry becomes its own block); keep ranges disjoint. '\n + 'Never compress content the current step is actively using.',\n parameters: compressParameters,\n output: textOutput(),\n async execute(args, exec) {\n return handleCompress(env, args as CompressArgs, exec)\n },\n }),\n defineTool({\n name: 'decompress',\n description: 'Recover the original content of a compressed block by its blockId (read-only; does not unshadow the range).',\n parameters: decompressParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleDecompress(env, args as DecompressArgs, exec))\n },\n }),\n defineTool({\n name: 'search_context',\n description: 'Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.',\n parameters: searchParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleSearch(env, args as SearchArgs, exec))\n },\n }),\n defineTool({\n name: 'acp_status',\n description: 'Report the ACP block ledger: compressed blocks, reclaimed tokens, and current context pressure.',\n parameters: statusParameters,\n output: textOutput(),\n execute(args, exec) {\n return handleStatus(env, args as StatusArgs, exec)\n },\n }),\n ]\n}\n","/**\n * Kernel configuration assembly — the DSH counterpart of billion-context-pi's\n * `resolveConfig`: build acp-kernel's `Config` from adapter-level knobs.\n *\n * Defaults are deliberately the acp-kernel `defaultConfig` values (the same\n * defaults billion-context-pi ships: nudge window 45%–75%, emergency 95%,\n * growth ratio 5%, protected last messages 5). Every knob is optional — an\n * omitted value keeps the kernel default, so the behavior matches the Pi\n * adapter exactly unless a deployment opts out.\n *\n * NOTE: `AcpCompactionEngine` (src/index.ts) ships its own engine-level\n * defaults 0.70/0.85 for the two nudge thresholds on top of this layer, so an\n * engine with no explicit config lands on 0.70/0.85, not 0.75/0.95.\n * @module billion-context-dsh/config\n */\n\nimport { defaultConfig, type Config } from 'acp-kernel'\n\n/** The kernel-facing knobs shared by the nudge path and the compress tool. */\nexport interface KernelConfigInput {\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction; validation only — the growth-driven trigger has no percentage floor). Kernel default: 0.45. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound — over-limit guarantee line. Kernel default: 0.75. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default: 0.95. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (the billion-context-pi escape hatch). */\n readonly coreOverrides?: Partial<Config>\n}\n\n/**\n * Assemble the kernel config: `defaultConfig(limit)` merged with the optional\n * nudge thresholds (merged into the defaults, never replacing them wholesale)\n * and any additional `coreOverrides`.\n */\nexport function kernelConfigFor(input: KernelConfigInput): Config {\n const nudgePatch: Partial<Config['nudge']> = {}\n if (input.nudgeMinContextLimitPct !== undefined) nudgePatch.minContextLimitPct = input.nudgeMinContextLimitPct\n if (input.nudgeMaxContextLimitPct !== undefined) nudgePatch.maxContextLimitPct = input.nudgeMaxContextLimitPct\n if (input.nudgeEmergencyThresholdPct !== undefined) nudgePatch.emergencyThresholdPct = input.nudgeEmergencyThresholdPct\n\n const overrides: Partial<Config> = { ...input.coreOverrides }\n if (Object.keys(nudgePatch).length > 0) {\n overrides.nudge = { ...defaultConfig(input.modelContextLimit).nudge, ...nudgePatch }\n }\n return defaultConfig(input.modelContextLimit, overrides)\n}\n","/**\n * Auto context-window detection — resolve the model's real context window\n * from the host LLM runtime instead of trusting a hardcoded config default.\n *\n * `agent.ctx.llm` (the cordis `LlmRuntime` service) exposes\n * `resolveModelInfo(provider, model)` → `{ context: { contextWindow } }`, the\n * exact-route capacity the adapter learned from the provider API (pi-ai reads\n * `context_window`/`context_length` during discovery). Probing is a standalone\n * capability query — no request is sent.\n * @module billion-context-dsh/window\n */\n\nimport type { Agent } from '@deepseek-ai/dsh-agent'\n\n/** Fallback window when auto-detection is unavailable. Same default as acp-kernel's `defaultConfig`. */\nexport const DEFAULT_CONTEXT_WINDOW = 128000\n\n/** The effective context window plus where it came from. */\nexport interface AcpWindow {\n /** Effective context window in tokens. */\n readonly limit: number\n /** Where the limit came from. */\n readonly source: 'explicit' | 'auto' | 'default'\n /** Route the auto window was resolved for (auto source only). */\n readonly provider?: string\n readonly model?: string\n}\n\n/** Human label for an AcpWindow's source (used by acp_status). */\nexport function windowSourceLabel(window: AcpWindow): string {\n if (window.source === 'explicit') return 'configured'\n if (window.source === 'auto') {\n return `auto-detected from ${window.provider ?? '?'}/${window.model ?? '?'}`\n }\n return 'default (auto-detection unavailable)'\n}\n\n/** The minimal LlmRuntime surface the probe needs (structural — no as any). */\ninterface LlmProbe {\n resolveModelInfo?: (\n provider: string,\n model: string,\n signal?: AbortSignal,\n ) => Promise<{ context?: { contextWindow?: number } }>\n}\n\n/**\n * Probe the model's real context window. Returns null when the host provides\n * no llm service, the adapter discloses no window, or the probe throws —\n * callers fall back to DEFAULT_CONTEXT_WINDOW. Never throws.\n */\nexport async function detectContextWindow(\n agent: Agent,\n provider: string,\n model: string,\n): Promise<number | null> {\n const llm = agent.ctx?.get?.('llm') as LlmProbe | undefined\n if (llm?.resolveModelInfo === undefined) return null\n try {\n const info = await llm.resolveModelInfo(provider, model)\n const window = info?.context?.contextWindow\n if (typeof window === 'number' && Number.isInteger(window) && window > 0) return window\n return null\n } catch {\n return null\n }\n}\n","/**\n * M4 — the `/acp` slash command: a human-friendly window into the same\n * machinery the model tools expose (status, one-shot compress, decompress).\n * @module billion-context-dsh/commands\n */\n\nimport type { CommandDefinition } from '@deepseek-ai/dsh-commands'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { ToolEnvironment } from './tools.ts'\nimport {\n blockRefForSummarySeq,\n expandShadowedSeqs,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n} from './region.ts'\nimport { eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\nimport { defaultConfig, defaultCountTokens } from 'acp-kernel'\nimport { windowSourceLabel } from './window.ts'\n\nasync function statusText(env: ToolEnvironment, agent: Agent): Promise<string> {\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const window = env.windowFor === undefined\n ? { limit: env.modelContextLimit, source: 'explicit' as const }\n : await env.windowFor(agent)\n const limit = window.limit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ` context window: ${limit} (${windowSourceLabel(window)})`,\n ]\n for (const block of ledger.slice(0, 10)) {\n const tier = block.tier > 1 ? ` [T${block.tier}]` : ''\n lines.push(` - ${block.blockId.slice(0, 8)}${tier}: seqs ${block.start}..${block.end} — ${block.summary.slice(0, 80)}`)\n }\n return lines.join('\\n')\n}\n\nfunction compressText(env: ToolEnvironment, agent: Agent, args: string[]): string {\n if (args.length < 3) {\n return '/acp compress <startSeq> <endSeq> <summary...>'\n }\n const startSeq = Number(args[0])\n const endSeq = Number(args[1])\n const summary = args.slice(2).join(' ')\n if (!Number.isInteger(startSeq) || !Number.isInteger(endSeq)) {\n return '/acp compress: startSeq and endSeq must be integers'\n }\n const session = agent.session\n const { start, end } = resolveSurfaceRange(session, startSeq, endSeq)\n // A checkpoint summary node can only be distilled through the kernel (the\n // compress tool); /acp compress is a plain T1 range transaction, so refuse\n // rather than silently folding the summary as a message.\n if (blockRefForSummarySeq(session, start) !== null || blockRefForSummarySeq(session, end) !== null) {\n return '/acp compress: the range touches a compressed block summary node — distill it with the compress tool (seq-based batch), not /acp compress'\n }\n const shadowed = shadowedSeqsOf(session, startSeq, endSeq)\n let shadowedTokens = 0\n for (const seq of shadowed) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokens += defaultCountTokens(extractEventText(event))\n }\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: summary }],\n shadowedTokenCount: shadowedTokens,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n })\n return `Compressed seqs ${start}..${end} (${shadowed.length} messages) as block ${compactionId.slice(0, 8)}`\n}\n\nfunction decompressText(_env: ToolEnvironment, agent: Agent, args: string[]): string {\n if (args.length < 1) return '/acp decompress <blockId>'\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args[0]!))\n if (block === undefined) return `block \"${args[0]}\" not found (see /acp status)`\n // Tier-2/3 blocks shadow parent checkpoint nodes: expand to the originals.\n const parts = expandShadowedSeqs(session, block.blockId)\n .map((seq) => extractEventText(session.events[seq]!))\n .filter((text) => text.length > 0)\n return `Block ${block.blockId} — ${block.summary}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`\n}\n\n/** Register the /acp command (idempotent per engine). */\nexport function acpCommand(env: ToolEnvironment): CommandDefinition {\n return {\n name: 'acp',\n description:\n 'Active Context Pruning — model-driven context compression. '\n + 'Usage: /acp status | /acp compress <startSeq> <endSeq> <summary> | /acp decompress <blockId>',\n handler: async (invocation) => {\n const raw = invocation.rawInput.trim()\n if (raw === '' || raw === 'status') {\n return { kind: 'success', text: await statusText(env, invocation.agent) }\n }\n if (raw.startsWith('compress')) {\n return { kind: 'success', text: compressText(env, invocation.agent, raw.slice('compress'.length).trim().split(/\\s+/) ) }\n }\n if (raw.startsWith('decompress')) {\n return { kind: 'success', text: decompressText(env, invocation.agent, raw.slice('decompress'.length).trim().split(/\\s+/)) }\n }\n return { kind: 'error', text: `unknown /acp subcommand \"${raw.split(/\\s+/)[0]}\" — use status | compress | decompress` }\n },\n }\n}\n","/**\n * M4 — ACP nudge: the kernel's compression recommendation, rendered as an\n * injected user message with a seq-based compressible-range table (D1:\n * \"seq is the ref\" — DSH has no in-memory message rewrite hook, so the model\n * targets ranges by surface seq rather than by <acp> tags).\n * @module billion-context-dsh/nudge\n */\n\nimport {\n defaultCountTokens,\n type CompressionCore,\n type CoreMessage,\n type NudgeDecision,\n} from 'acp-kernel'\nimport { createUserMessage, type UserMessage } from '@deepseek-ai/dsh-llm'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { allLogMessages, eventsToCoreMessages, surfaceEventsOf } from './messages.ts'\nimport { buildCompressibleSeqRanges, findOpenTurn, summarySeqOfKernelBlock, surfaceSummary } from './region.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\n\n/** Kernel inputs the nudge path shares with the compress tool. */\nexport interface NudgeEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\nexport interface NudgeOutcome {\n readonly message: UserMessage\n readonly emergency: boolean\n}\n\n/**\n * Render the compressible-range table as seq refs for the model.\n * Computed directly from the surface (not the kernel's ref map, which can\n * drift and hide large tool results) — see buildCompressibleSeqRanges.\n */\nexport function rangeTable(session: import('@deepseek-ai/dsh-session').Session): string {\n const ranges = buildCompressibleSeqRanges(session).slice(0, 6)\n if (ranges.length === 0) return ''\n const lines = ranges.map((range) => ` - seq ${range.start}..${range.end} — ${range.count} messages, ~${range.tokens} tokens`)\n return [\n '',\n `Surface: ${surfaceSummary(session)}`,\n 'Compressible ranges (suggestions only — compress any consumed span; refs are surface seqs):',\n ...lines,\n 'Compress with: compress({ content: [{ startSeq, endSeq, summary }] }) — content is an array: batch multiple unrelated segments in one call, each entry its own block. Keep ranges disjoint.',\n ].join('\\n')\n}\n\n/**\n * The token count driving pressure decisions. Prefer the host token meter's\n * SURFACE tokens (the input-side heuristic total — matches acp_status and the\n * UI's message-token view). `totalTokens` is request-AND-RESPONSE pressure and\n * can far exceed the window in long sessions (observed 230% against a real\n * ~20% occupancy), so it is not used. Falls back to the fast heuristic when\n * the meter is absent (tests, minimal hosts).\n */\nfunction measuredTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n const meter = agent.ctx?.get?.('tokenMeter') as\n | { measure?: (session: unknown) => { surfaceTokens?: number } }\n | undefined\n const surface = meter?.measure?.(agent.session)?.surfaceTokens\n if (typeof surface === 'number' && surface > 0) return surface\n return coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n}\n\n/**\n * Decide and build one nudge message for the agent's next pre-step. Returns\n * null when the kernel recommends no nudge or one was already injected for the\n * current turn (emergency nudges always bypass the dedup). Also advances the\n * in-memory kernel state (ref assignment) so the compress tool can resolve\n * seq → mNNNNN refs.\n */\nexport function buildNudge(\n agent: Agent,\n env: NudgeEnvironment,\n lastNudgeTurn: Map<string, number>,\n): NudgeOutcome | null {\n const session = agent.session\n const state = env.store.stateFor(session)\n // Full log for the kernel (so block anchors survive — see handleCompress);\n // the measured token count stays a SURFACE measurement.\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = measuredTokenCount(agent, surfaceMessages)\n const config = kernelConfigFor(env)\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n\n const nudge = turn.nudge\n if (nudge === undefined || !nudge.shouldInject) return null\n const emergency = nudge.breakdown?.emergencyOverride === 1\n\n const turnNumber = findOpenTurn(session.events) ?? 0\n const alreadyShown = !emergency && lastNudgeTurn.get(session.id) === turnNumber\n if (alreadyShown) return null\n lastNudgeTurn.set(session.id, turnNumber)\n\n const text = buildNudgeText(nudge, emergency, session)\n const message = createUserMessage({\n content: [{ type: 'text', text }],\n source: { kind: 'plugin', plugin: 'acp-nudge' },\n })\n return { message, emergency }\n}\n\n/**\n * A short ADVISORY nudge — ACP is model-driven, the model decides whether and\n * when to compress. Full guidance (tools, philosophy, summary rules) lives in\n * the system prompt once, not in every nudge.\n */\nexport function buildNudgeText(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n): string {\n // Cap the reported percentage at 100: a broken measurement (e.g. response\n // pressure folded in) must never surface as an absurd \"230%\" to the model.\n const pct = Math.round(Math.min(nudge.contextUsage, 1) * 100)\n const frame = emergency\n ? `⚠️ Context usage is at ${pct}% of the window — nearly full. Consider compressing consumed ranges soon so working context stays available; the choice and timing are yours.`\n : `Context usage is at ${pct}%. This is a suggestion, not a requirement — you decide whether and when to compress.`\n const guidance = 'Compress by need, not by percentage: replace only ranges you have genuinely consumed, with dense self-contained summaries.'\n const parts = [frame, '', guidance]\n if ((nudge.tier === 2 || nudge.tier === 3) && (nudge.tierTargetBlocks?.length ?? 0) > 0) {\n const targets = nudge.tierTargetBlocks!\n const summarySeqs = targets\n .map((block) => summarySeqOfKernelBlock(session, block.blockId))\n .filter((seq): seq is number => seq !== null)\n const pending = nudge.tier === 2 ? nudge.breakdown?.pendingT2 : nudge.breakdown?.pendingT3\n const tokens = typeof pending === 'number' ? pending : 0\n parts.push(\n `Tier ${nudge.tier}: ${targets.length} tier-${nudge.tier - 1} block(s) distillable (${tokens} tokens) — compress their summary node(s) [seqs ${summarySeqs.join(', ')}] to reclaim the original messages.`,\n )\n }\n parts.push(rangeTable(session))\n return parts.join('\\n')\n}\n","/**\n * M4 — the ACP system-prompt section (DSH counterpart of billion-context-pi's\n * ACP_SYSTEM_PROMPT): the load-bearing compression guidance lives here, ONCE,\n * instead of being re-sent with every nudge. The nudge itself stays a short,\n * advisory notice — ACP is model-driven, the model decides whether and when\n * to compress (never \"compress now\").\n * @module billion-context-dsh/system-prompt\n */\n\nimport { COMPRESS_PHILOSOPHY } from 'acp-kernel'\n\nexport const ACP_SYSTEM_PROMPT = `Active Context Pruning — model-driven context management\n\nYOU decide whether and when to compress context. Nothing forces you: the injected \"nudge\" is a suggestion, not an order, and you may ignore it when compression would not help. Compress only ranges you have genuinely consumed (read tool outputs, finished explorations, superseded steps) that the current work no longer needs verbatim.\n\n${COMPRESS_PHILOSOPHY}\n\nCompression tools (refs are SURFACE SEQS, not ids):\n- compress: replace one or more seq ranges, each with your own dense summary. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated segments in one call (each entry becomes its own block): compress({ content: [{ startSeq: 1, endSeq: 5, summary: '...' }, { startSeq: 12, endSeq: 18, summary: '...' }] }). Keep ranges disjoint — overlapping entries in one batch are skipped. Edges are auto-balanced to tool-call/result boundaries; a trailing #callId fragment in a seq is ignored. Ranges must be on the current surface — stale seqs fail with guidance.\n- decompress: recover a compressed block's original content, read-only. decompress({ blockId }).\n- search_context: find information inside compressed blocks BEFORE decompressing. search_context({ query }).\n- acp_status: current context usage and the live compressible-range list. Run it before compressing when in doubt.\n\nTiered compression: each compressed block appears on the surface as one summary node. Compressing that node again DISTILLS the block (tier 2): the parent summary folds into your new summary and the original messages are freed. Distilling a tier-2 block yields tier 3. Distill when a summary itself is consumed — decompress on the tier-2 block recovers the full originals.\n\nWhen you write a summary, it becomes the ONLY record of that range: keep file paths, signatures, exact values, decisions, and error strings verbatim so a later reader (or you, after decompress) can continue without the original. Never reuse historical seqs — the surface moves as messages land and compress; verify with acp_status.`\n\n/** System-prompt section order: tool guidance lives in 100–199. */\nexport const ACP_SYSTEM_PROMPT_ORDER = 150\n"],"mappings":"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","names":["defaultCountTokens","defaultCountTokens","defaultCountTokens","defaultCountTokens","defaultCountTokens","createUserMessage","defaultCountTokens","createUserMessage"]}
package/dist/tools.d.ts CHANGED
@@ -11,11 +11,15 @@
11
11
  */
12
12
  import { type ToolDefinition } from '@deepseek-ai/dsh-tools';
13
13
  import { type CompressionCore } from 'acp-kernel';
14
+ import type { Agent } from '@deepseek-ai/dsh-agent';
14
15
  import type { AcpStateStore } from './state.ts';
15
16
  import { type KernelConfigInput } from './config.ts';
17
+ import { type AcpWindow } from './window.ts';
16
18
  export interface ToolEnvironment extends KernelConfigInput {
17
19
  readonly kernel: CompressionCore;
18
20
  readonly store: AcpStateStore;
21
+ /** Resolve the effective context window for an agent (optional: status falls back to modelContextLimit). */
22
+ readonly windowFor?: (agent: Agent) => Promise<AcpWindow>;
19
23
  }
20
24
  /** Build the four ACP model tools bound to one engine. */
21
25
  export declare function makeTools(env: ToolEnvironment): ToolDefinition[];
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Auto context-window detection — resolve the model's real context window
3
+ * from the host LLM runtime instead of trusting a hardcoded config default.
4
+ *
5
+ * `agent.ctx.llm` (the cordis `LlmRuntime` service) exposes
6
+ * `resolveModelInfo(provider, model)` → `{ context: { contextWindow } }`, the
7
+ * exact-route capacity the adapter learned from the provider API (pi-ai reads
8
+ * `context_window`/`context_length` during discovery). Probing is a standalone
9
+ * capability query — no request is sent.
10
+ * @module billion-context-dsh/window
11
+ */
12
+ import type { Agent } from '@deepseek-ai/dsh-agent';
13
+ /** Fallback window when auto-detection is unavailable. Same default as acp-kernel's `defaultConfig`. */
14
+ export declare const DEFAULT_CONTEXT_WINDOW = 128000;
15
+ /** The effective context window plus where it came from. */
16
+ export interface AcpWindow {
17
+ /** Effective context window in tokens. */
18
+ readonly limit: number;
19
+ /** Where the limit came from. */
20
+ readonly source: 'explicit' | 'auto' | 'default';
21
+ /** Route the auto window was resolved for (auto source only). */
22
+ readonly provider?: string;
23
+ readonly model?: string;
24
+ }
25
+ /** Human label for an AcpWindow's source (used by acp_status). */
26
+ export declare function windowSourceLabel(window: AcpWindow): string;
27
+ /**
28
+ * Probe the model's real context window. Returns null when the host provides
29
+ * no llm service, the adapter discloses no window, or the probe throws —
30
+ * callers fall back to DEFAULT_CONTEXT_WINDOW. Never throws.
31
+ */
32
+ export declare function detectContextWindow(agent: Agent, provider: string, model: string): Promise<number | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "billion-context-dsh",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Active Context Pruning (ACP) for the DeepSeek Harness — model-driven context management as a CompactionEngine backend.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",